@better-t-stack/template-generator 3.28.2 → 3.29.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -694,8 +694,8 @@ function renameDevScriptsForAlchemy(vfs, config) {
694
694
  //#region src/utils/add-deps.ts
695
695
  const dependencyVersionMap = {
696
696
  typescript: "^6",
697
- "better-auth": "1.6.9",
698
- "@better-auth/expo": "1.6.9",
697
+ "better-auth": "1.6.11",
698
+ "@better-auth/expo": "1.6.11",
699
699
  "@clerk/backend": "^3.2.1",
700
700
  "@clerk/express": "^2.0.5",
701
701
  "@clerk/fastify": "^3.1.3",
@@ -715,16 +715,17 @@ const dependencyVersionMap = {
715
715
  "@types/ws": "^8.18.1",
716
716
  ws: "^8.18.3",
717
717
  mysql2: "^3.14.0",
718
- "@prisma/client": "^7.7.0",
719
- prisma: "^7.7.0",
720
- "@prisma/adapter-d1": "^7.7.0",
721
- "@prisma/adapter-neon": "^7.7.0",
722
- "@prisma/adapter-mariadb": "^7.7.0",
723
- "@prisma/adapter-libsql": "^7.7.0",
724
- "@prisma/adapter-better-sqlite3": "^7.7.0",
725
- "@prisma/adapter-pg": "^7.7.0",
726
- "@prisma/adapter-planetscale": "^7.7.0",
727
- mongoose: "^8.14.0",
718
+ "@prisma/client": "^7.8.0",
719
+ prisma: "^7.8.0",
720
+ "@prisma/adapter-d1": "^7.8.0",
721
+ "@prisma/adapter-neon": "^7.8.0",
722
+ "@prisma/adapter-mariadb": "^7.8.0",
723
+ "@prisma/adapter-libsql": "^7.8.0",
724
+ "@prisma/adapter-better-sqlite3": "^7.8.0",
725
+ "@prisma/adapter-pg": "^7.8.0",
726
+ "@prisma/adapter-planetscale": "^7.8.0",
727
+ mongoose: "^9.6.2",
728
+ mongodb: "^7.2.0",
728
729
  "vite-plugin-pwa": "^1.2.0",
729
730
  "@vite-pwa/assets-generator": "^1.0.2",
730
731
  "@tauri-apps/cli": "^2.4.0",
@@ -777,7 +778,7 @@ const dependencyVersionMap = {
777
778
  "convex-svelte": "^0.0.12",
778
779
  "convex-nuxt": "0.1.5",
779
780
  "convex-vue": "^0.1.5",
780
- "@convex-dev/better-auth": "^0.12.1",
781
+ "@convex-dev/better-auth": "^0.12.2",
781
782
  "@tanstack/svelte-query": "^5.85.3",
782
783
  "@tanstack/svelte-query-devtools": "^5.85.3",
783
784
  "@tanstack/vue-query-devtools": "^6.1.5",
@@ -806,7 +807,7 @@ const dependencyVersionMap = {
806
807
  "@t3-oss/env-core": "^0.13.1",
807
808
  "@t3-oss/env-nextjs": "^0.13.1",
808
809
  "@t3-oss/env-nuxt": "^0.13.1",
809
- "@polar-sh/better-auth": "^1.8.3",
810
+ "@polar-sh/better-auth": "^1.8.4",
810
811
  "@polar-sh/sdk": "^0.42.2",
811
812
  evlog: "^2.14.1"
812
813
  };
@@ -1488,7 +1489,7 @@ function addConvexDeps(vfs, frontend, frontendType) {
1488
1489
  }
1489
1490
  //#endregion
1490
1491
  //#region src/processors/auth-deps.ts
1491
- const CONVEX_BETTER_AUTH_VERSION = "1.6.9";
1492
+ const CONVEX_BETTER_AUTH_VERSION = "~1.6.9";
1492
1493
  function processAuthDeps(vfs, config) {
1493
1494
  const { auth, backend } = config;
1494
1495
  if (!auth || auth === "none") return;
@@ -1599,7 +1600,7 @@ function processConvexAuthDeps(vfs, config) {
1599
1600
  }
1600
1601
  }
1601
1602
  function processStandardAuthDeps(vfs, config) {
1602
- const { auth, backend, frontend } = config;
1603
+ const { auth, backend, frontend, orm } = config;
1603
1604
  const authPath = "packages/auth/package.json";
1604
1605
  const apiPath = "packages/api/package.json";
1605
1606
  const webPath = "apps/web/package.json";
@@ -1696,10 +1697,12 @@ function processStandardAuthDeps(vfs, config) {
1696
1697
  }
1697
1698
  } else if (auth === "better-auth") {
1698
1699
  if (authExists) {
1700
+ const authDependencies = ["better-auth"];
1701
+ if (orm === "mongoose") authDependencies.push("mongodb");
1699
1702
  addPackageDependency({
1700
1703
  vfs,
1701
1704
  packagePath: authPath,
1702
- dependencies: ["better-auth"]
1705
+ dependencies: authDependencies
1703
1706
  });
1704
1707
  if (hasNative) addPackageDependency({
1705
1708
  vfs,
@@ -1871,13 +1874,13 @@ function processPrismaDeps(vfs, config, dbPkgPath, webPkgPath, webExists) {
1871
1874
  addPackageDependency({
1872
1875
  vfs,
1873
1876
  packagePath: dbPkgPath,
1874
- customDependencies: { "@prisma/client": "6.19.0" },
1875
- customDevDependencies: { prisma: "6.19.0" }
1877
+ customDependencies: { "@prisma/client": "6.19.3" },
1878
+ customDevDependencies: { prisma: "6.19.3" }
1876
1879
  });
1877
1880
  if (webExists) addPackageDependency({
1878
1881
  vfs,
1879
1882
  packagePath: webPkgPath,
1880
- customDependencies: { "@prisma/client": "6.19.0" }
1883
+ customDependencies: { "@prisma/client": "6.19.3" }
1881
1884
  });
1882
1885
  return;
1883
1886
  }
@@ -2208,6 +2211,8 @@ function buildNativeVars(frontend, backend, auth) {
2208
2211
  return vars;
2209
2212
  }
2210
2213
  function buildConvexBackendVars(frontend, auth, examples) {
2214
+ const hasReactRouter = frontend.includes("react-router");
2215
+ const hasTanStackRouter = frontend.includes("tanstack-router");
2211
2216
  const hasNextJs = frontend.includes("next");
2212
2217
  const hasNative = frontend.includes("native-bare") || frontend.includes("native-uniwind") || frontend.includes("native-unistyles");
2213
2218
  const hasWeb = frontend.includes("react-router") || frontend.includes("tanstack-router") || frontend.includes("tanstack-start") || hasNextJs || frontend.includes("nuxt") || frontend.includes("solid") || frontend.includes("svelte") || frontend.includes("astro");
@@ -2220,6 +2225,12 @@ function buildConvexBackendVars(frontend, auth, examples) {
2220
2225
  comment: "Google AI API key for AI agent"
2221
2226
  });
2222
2227
  if (auth === "better-auth") {
2228
+ if (hasReactRouter || hasTanStackRouter) vars.push({
2229
+ key: "CONVEX_SITE_URL",
2230
+ value: "",
2231
+ condition: true,
2232
+ comment: "Same as CONVEX_URL but ends in .site"
2233
+ });
2223
2234
  if (hasNative) vars.push({
2224
2235
  key: "EXPO_PUBLIC_CONVEX_SITE_URL",
2225
2236
  value: "",
@@ -2247,6 +2258,7 @@ function buildConvexBackendVars(frontend, auth, examples) {
2247
2258
  return vars;
2248
2259
  }
2249
2260
  function buildConvexCommentBlocks(frontend, auth, examples) {
2261
+ const needsConvexSiteUrl = frontend.includes("react-router") || frontend.includes("tanstack-router");
2250
2262
  const hasNative = frontend.includes("native-bare") || frontend.includes("native-uniwind") || frontend.includes("native-unistyles");
2251
2263
  const hasWeb = frontend.includes("react-router") || frontend.includes("tanstack-router") || frontend.includes("tanstack-start") || frontend.includes("next") || frontend.includes("nuxt") || frontend.includes("solid") || frontend.includes("svelte") || frontend.includes("astro");
2252
2264
  const defaultSiteUrl = hasNative && !hasWeb ? "http://localhost:8081" : frontend.includes("react-router") || frontend.includes("svelte") ? "http://localhost:5173" : frontend.includes("astro") ? "http://localhost:4321" : "http://localhost:3001";
@@ -2257,7 +2269,7 @@ function buildConvexCommentBlocks(frontend, auth, examples) {
2257
2269
  `;
2258
2270
  if (auth === "better-auth") commentBlocks += `# Set Convex environment variables
2259
2271
  # npx convex env set BETTER_AUTH_SECRET=$(openssl rand -base64 32)
2260
- ${hasWeb || hasNative ? `# npx convex env set SITE_URL ${defaultSiteUrl}\n` : ""}`;
2272
+ ${needsConvexSiteUrl ? "# npx convex env set CONVEX_SITE_URL https://<YOUR_CONVEX_SITE_URL>\n" : ""}${hasWeb || hasNative ? `# npx convex env set SITE_URL ${defaultSiteUrl}\n` : ""}`;
2261
2273
  return commentBlocks;
2262
2274
  }
2263
2275
  function buildServerVars(backend, frontend, auth, api, database, dbSetup, runtime, webDeploy, serverDeploy, payments, examples) {
@@ -3275,7 +3287,6 @@ function getDeployTasks() {
3275
3287
  }
3276
3288
  //#endregion
3277
3289
  //#region src/processors/workspace-deps.ts
3278
- const NATIVE_TYPESCRIPT_VERSION = "~5.9.2";
3279
3290
  function processWorkspaceDeps(vfs, config) {
3280
3291
  const { projectName, packageManager, runtime, backend, database, auth, api, serverDeploy, webDeploy } = config;
3281
3292
  const workspaceVersion = packageManager === "npm" ? "*" : "workspace:*";
@@ -3411,10 +3422,7 @@ function processWorkspaceDeps(vfs, config) {
3411
3422
  packagePath: "apps/native/package.json",
3412
3423
  dependencies: commonDeps,
3413
3424
  customDependencies: nativeDeps,
3414
- customDevDependencies: {
3415
- ...configDep,
3416
- typescript: NATIVE_TYPESCRIPT_VERSION
3417
- }
3425
+ customDevDependencies: configDep
3418
3426
  });
3419
3427
  }
3420
3428
  }
@@ -3445,11 +3453,6 @@ function processDependencies(vfs, config) {
3445
3453
  }
3446
3454
  //#endregion
3447
3455
  //#region src/template-handlers/utils.ts
3448
- function hasTemplatesWithPrefix(templates, prefix) {
3449
- const normalizedPrefix = prefix.endsWith("/") ? prefix : `${prefix}/`;
3450
- for (const path of templates.keys()) if (path.startsWith(normalizedPrefix)) return true;
3451
- return false;
3452
- }
3453
3456
  function processSingleTemplate(vfs, templates, templatePath, destPath, config) {
3454
3457
  const templateKey = templatePath.endsWith(".hbs") ? templatePath : `${templatePath}.hbs`;
3455
3458
  const content = templates.get(templateKey);
@@ -3829,11 +3832,9 @@ async function processExtrasTemplates(vfs, templates, config) {
3829
3832
  "native-unistyles"
3830
3833
  ].includes(f));
3831
3834
  const hasNuxt = config.frontend.includes("nuxt");
3832
- if (config.packageManager === "pnpm") {
3833
- if (hasTemplatesWithPrefix(templates, "extras")) processTemplatesFromPrefix(vfs, templates, "extras/pnpm-workspace.yaml", "", config);
3834
- }
3835
- if (config.packageManager === "bun") processTemplatesFromPrefix(vfs, templates, "extras/bunfig.toml", "", config);
3836
- if (config.packageManager === "pnpm" && (hasNative || hasNuxt)) processTemplatesFromPrefix(vfs, templates, "extras/_npmrc", "", config);
3835
+ if (config.packageManager === "pnpm") processSingleTemplate(vfs, templates, "extras/pnpm-workspace.yaml", "pnpm-workspace.yaml", config);
3836
+ if (config.packageManager === "bun") processSingleTemplate(vfs, templates, "extras/bunfig.toml", "bunfig.toml", config);
3837
+ if (config.packageManager === "pnpm" && (hasNative || hasNuxt)) processSingleTemplate(vfs, templates, "extras/_npmrc", ".npmrc", config);
3837
3838
  if (config.serverDeploy === "cloudflare" || config.backend === "self" && config.webDeploy === "cloudflare") processSingleTemplate(vfs, templates, "extras/env.d.ts", "packages/env/env.d.ts", config);
3838
3839
  }
3839
3840
  //#endregion
@@ -4792,7 +4793,7 @@ import { auth } from "@{{projectName}}/auth";
4792
4793
  {{/if}}
4793
4794
  {{/if}}
4794
4795
 
4795
- export async function createContext(req: NextRequest){{#if (eq auth "clerk")}}: Promise<ClerkRequestContext>{{/if}} {
4796
+ export async function createContext({{#if (eq auth "none")}}_req{{else}}req{{/if}}: NextRequest){{#if (eq auth "clerk")}}: Promise<ClerkRequestContext>{{/if}} {
4796
4797
  {{#if (eq auth "better-auth")}}
4797
4798
  const session = await {{#if (or (eq runtime "workers") (eq serverDeploy "cloudflare") (and (eq backend "self") (eq webDeploy "cloudflare")))}}createAuth(){{else}}auth{{/if}}.api.getSession({
4798
4799
  headers: req.headers,
@@ -4824,7 +4825,7 @@ import { auth } from "@{{projectName}}/auth";
4824
4825
  {{/if}}
4825
4826
  {{/if}}
4826
4827
 
4827
- export async function createContext({ req }: { req: Request }){{#if (eq auth "clerk")}}: Promise<ClerkRequestContext>{{/if}} {
4828
+ export async function createContext({{#if (eq auth "none")}}_options{{else}}{ req }{{/if}}: { req: Request }){{#if (eq auth "clerk")}}: Promise<ClerkRequestContext>{{/if}} {
4828
4829
  {{#if (eq auth "better-auth")}}
4829
4830
  const session = await {{#if (or (eq runtime "workers") (eq serverDeploy "cloudflare") (and (eq backend "self") (eq webDeploy "cloudflare")))}}createAuth(){{else}}auth{{/if}}.api.getSession({
4830
4831
  headers: req.headers,
@@ -4860,7 +4861,7 @@ export type CreateContextOptions = {
4860
4861
  headers: Headers;
4861
4862
  };
4862
4863
 
4863
- export async function createContext({ headers }: CreateContextOptions) {
4864
+ export async function createContext({{#if (eq auth "none")}}_options{{else}}{ headers }{{/if}}: CreateContextOptions) {
4864
4865
  {{#if (eq auth "better-auth")}}
4865
4866
  const session = await {{#if (or (eq runtime "workers") (eq serverDeploy "cloudflare") (and (eq backend "self") (eq webDeploy "cloudflare")))}}createAuth(){{else}}auth{{/if}}.api.getSession({ headers });
4866
4867
  return {
@@ -4894,7 +4895,7 @@ export type CreateContextOptions = {
4894
4895
  {{/if}}
4895
4896
  };
4896
4897
 
4897
- export async function createContext({ headers{{#if (eq webDeploy "cloudflare")}}, env{{/if}} }: CreateContextOptions) {
4898
+ export async function createContext({{#if (eq auth "none")}}{{#if (eq webDeploy "cloudflare")}}{ env: _env }{{else}}_options{{/if}}{{else}}{ headers{{#if (eq webDeploy "cloudflare")}}, env{{/if}} }{{/if}}: CreateContextOptions) {
4898
4899
  {{#if (eq auth "better-auth")}}
4899
4900
  const session = await {{#if (or (eq runtime "workers") (eq serverDeploy "cloudflare") (and (eq backend "self") (eq webDeploy "cloudflare")))}}createAuth({{#if (eq webDeploy "cloudflare")}}env{{/if}}){{else}}auth{{/if}}.api.getSession({ headers });
4900
4901
  return {
@@ -4928,7 +4929,7 @@ export type CreateContextOptions = {
4928
4929
  headers: Headers;
4929
4930
  };
4930
4931
 
4931
- export async function createContext({ headers }: CreateContextOptions) {
4932
+ export async function createContext({{#if (eq auth "none")}}_options{{else}}{ headers }{{/if}}: CreateContextOptions) {
4932
4933
  {{#if (eq auth "better-auth")}}
4933
4934
  const session = await {{#if (or (eq runtime "workers") (eq serverDeploy "cloudflare") (and (eq backend "self") (eq webDeploy "cloudflare")))}}createAuth(){{else}}auth{{/if}}.api.getSession({ headers });
4934
4935
  return {
@@ -4957,7 +4958,7 @@ export type CreateContextOptions = {
4957
4958
  context: HonoContext;
4958
4959
  };
4959
4960
 
4960
- export async function createContext({ context }: CreateContextOptions){{#if (eq auth "clerk")}}: Promise<ClerkRequestContext>{{/if}} {
4961
+ export async function createContext({{#if (eq auth "none")}}_options{{else}}{ context }{{/if}}: CreateContextOptions){{#if (eq auth "clerk")}}: Promise<ClerkRequestContext>{{/if}} {
4961
4962
  {{#if (eq auth "better-auth")}}
4962
4963
  const session = await {{#if (or (eq runtime "workers") (eq serverDeploy "cloudflare") (and (eq backend "self") (eq webDeploy "cloudflare")))}}createAuth(){{else}}auth{{/if}}.api.getSession({
4963
4964
  headers: context.req.raw.headers,
@@ -4990,7 +4991,7 @@ export type CreateContextOptions = {
4990
4991
  context: ElysiaContext;
4991
4992
  };
4992
4993
 
4993
- export async function createContext({ context }: CreateContextOptions){{#if (eq auth "clerk")}}: Promise<ClerkRequestContext>{{/if}} {
4994
+ export async function createContext({{#if (eq auth "none")}}_options{{else}}{ context }{{/if}}: CreateContextOptions){{#if (eq auth "clerk")}}: Promise<ClerkRequestContext>{{/if}} {
4994
4995
  {{#if (eq auth "better-auth")}}
4995
4996
  const session = await auth.api.getSession({
4996
4997
  headers: context.request.headers,
@@ -5026,7 +5027,7 @@ interface CreateContextOptions {
5026
5027
  req: Request;
5027
5028
  }
5028
5029
 
5029
- export async function createContext(opts: CreateContextOptions){{#if (eq auth "clerk")}}: Promise<ClerkRequestContext>{{/if}} {
5030
+ export async function createContext({{#if (eq auth "none")}}_opts{{else}}opts{{/if}}: CreateContextOptions){{#if (eq auth "clerk")}}: Promise<ClerkRequestContext>{{/if}} {
5030
5031
  {{#if (eq auth "better-auth")}}
5031
5032
  const session = await auth.api.getSession({
5032
5033
  headers: fromNodeHeaders(opts.req.headers),
@@ -5076,6 +5077,7 @@ export async function createContext(req: {{#if (eq auth "clerk")}}Parameters<typ
5076
5077
  session: null,
5077
5078
  };
5078
5079
  {{else}}
5080
+ void req;
5079
5081
  return {
5080
5082
  auth: null,
5081
5083
  session: null,
@@ -5094,7 +5096,7 @@ export async function createContext() {
5094
5096
 
5095
5097
  export type Context = Awaited<ReturnType<typeof createContext>>;
5096
5098
  `],
5097
- ["api/orpc/server/src/index.ts.hbs", `import { ORPCError, os } from "@orpc/server";
5099
+ ["api/orpc/server/src/index.ts.hbs", `import { {{#if (or (eq auth "better-auth") (eq auth "clerk"))}}ORPCError, {{/if}}os } from "@orpc/server";
5098
5100
  import type { Context } from "./context";
5099
5101
 
5100
5102
  export const o = os.$context<Context>();
@@ -5716,7 +5718,7 @@ import { auth } from "@{{projectName}}/auth";
5716
5718
  {{/if}}
5717
5719
  {{/if}}
5718
5720
 
5719
- export async function createContext(req: NextRequest){{#if (eq auth "clerk")}}: Promise<ClerkRequestContext>{{/if}} {
5721
+ export async function createContext({{#if (eq auth "none")}}_req{{else}}req{{/if}}: NextRequest){{#if (eq auth "clerk")}}: Promise<ClerkRequestContext>{{/if}} {
5720
5722
  {{#if (eq auth "better-auth")}}
5721
5723
  const session = await {{#if (or (eq runtime "workers") (eq serverDeploy "cloudflare") (and (eq backend "self") (eq webDeploy "cloudflare")))}}createAuth(){{else}}auth{{/if}}.api.getSession({
5722
5724
  headers: req.headers,
@@ -5748,7 +5750,7 @@ import { auth } from "@{{projectName}}/auth";
5748
5750
  {{/if}}
5749
5751
  {{/if}}
5750
5752
 
5751
- export async function createContext({ req }: { req: Request }){{#if (eq auth "clerk")}}: Promise<ClerkRequestContext>{{/if}} {
5753
+ export async function createContext({{#if (eq auth "none")}}_options{{else}}{ req }{{/if}}: { req: Request }){{#if (eq auth "clerk")}}: Promise<ClerkRequestContext>{{/if}} {
5752
5754
  {{#if (eq auth "better-auth")}}
5753
5755
  const session = await {{#if (or (eq runtime "workers") (eq serverDeploy "cloudflare") (and (eq backend "self") (eq webDeploy "cloudflare")))}}createAuth(){{else}}auth{{/if}}.api.getSession({
5754
5756
  headers: req.headers,
@@ -5785,7 +5787,7 @@ export type CreateContextOptions = {
5785
5787
  context: HonoContext;
5786
5788
  };
5787
5789
 
5788
- export async function createContext({ context }: CreateContextOptions){{#if (eq auth "clerk")}}: Promise<ClerkRequestContext>{{/if}} {
5790
+ export async function createContext({{#if (eq auth "none")}}_options{{else}}{ context }{{/if}}: CreateContextOptions){{#if (eq auth "clerk")}}: Promise<ClerkRequestContext>{{/if}} {
5789
5791
  {{#if (eq auth "better-auth")}}
5790
5792
  const session = await {{#if (or (eq runtime "workers") (eq serverDeploy "cloudflare") (and (eq backend "self") (eq webDeploy "cloudflare")))}}createAuth(){{else}}auth{{/if}}.api.getSession({
5791
5793
  headers: context.req.raw.headers,
@@ -5818,7 +5820,7 @@ export type CreateContextOptions = {
5818
5820
  context: ElysiaContext;
5819
5821
  };
5820
5822
 
5821
- export async function createContext({ context }: CreateContextOptions){{#if (eq auth "clerk")}}: Promise<ClerkRequestContext>{{/if}} {
5823
+ export async function createContext({{#if (eq auth "none")}}_options{{else}}{ context }{{/if}}: CreateContextOptions){{#if (eq auth "clerk")}}: Promise<ClerkRequestContext>{{/if}} {
5822
5824
  {{#if (eq auth "better-auth")}}
5823
5825
  const session = await auth.api.getSession({
5824
5826
  headers: context.request.headers,
@@ -5850,7 +5852,7 @@ import { auth } from "@{{projectName}}/auth";
5850
5852
  import { getAuth } from "@clerk/express";
5851
5853
  {{/if}}
5852
5854
 
5853
- export async function createContext(opts: CreateExpressContextOptions){{#if (eq auth "clerk")}}: Promise<ClerkRequestContext>{{/if}} {
5855
+ export async function createContext({{#if (eq auth "none")}}_opts{{else}}opts{{/if}}: CreateExpressContextOptions){{#if (eq auth "clerk")}}: Promise<ClerkRequestContext>{{/if}} {
5854
5856
  {{#if (eq auth "better-auth")}}
5855
5857
  const session = await auth.api.getSession({
5856
5858
  headers: fromNodeHeaders(opts.req.headers),
@@ -5898,6 +5900,7 @@ export async function createContext({ req }: CreateFastifyContextOptions){{#if (
5898
5900
  session: null,
5899
5901
  };
5900
5902
  {{else}}
5903
+ void req;
5901
5904
  return {
5902
5905
  auth: null,
5903
5906
  session: null,
@@ -5916,7 +5919,7 @@ export async function createContext() {
5916
5919
 
5917
5920
  export type Context = Awaited<ReturnType<typeof createContext>>;
5918
5921
  `],
5919
- ["api/trpc/server/src/index.ts.hbs", `import { initTRPC, TRPCError } from "@trpc/server";
5922
+ ["api/trpc/server/src/index.ts.hbs", `import { initTRPC{{#if (or (eq auth "better-auth") (eq auth "clerk"))}}, TRPCError{{/if}} } from "@trpc/server";
5920
5923
  import type { Context } from "./context";
5921
5924
 
5922
5925
  export const t = initTRPC.context<Context>().create();
@@ -6184,11 +6187,14 @@ export const authComponent = createClient<DataModel>(components.betterAuth);
6184
6187
 
6185
6188
  function createAuth(ctx: GenericCtx<DataModel>) {
6186
6189
  return betterAuth({
6190
+ {{#if (or (includes frontend "tanstack-router") (includes frontend "react-router"))}}
6191
+ baseURL: process.env.CONVEX_SITE_URL,
6192
+ {{/if}}
6187
6193
  {{#if (or (includes frontend "tanstack-start") (includes frontend "next"))}}
6188
6194
  baseURL: siteUrl,
6189
6195
  {{/if}}
6190
6196
  {{#if (or (includes frontend "native-bare") (includes frontend "native-uniwind") (includes frontend "native-unistyles"))}}
6191
- trustedOrigins: [siteUrl, nativeAppUrl, ...(process.env.NODE_ENV === "development" ? ["exp://", "exp://**", "exp://192.168.*.*:*/**"] : [])],
6197
+ trustedOrigins: [siteUrl, nativeAppUrl, "exp://"],
6192
6198
  {{else if (or (includes frontend "tanstack-router") (includes frontend "react-router") (includes frontend "nuxt") (includes frontend "svelte") (includes frontend "solid"))}}
6193
6199
  trustedOrigins: [siteUrl],
6194
6200
  {{else if (or (includes frontend "tanstack-start") (includes frontend "next"))}}
@@ -6229,14 +6235,7 @@ import { authComponent, createAuth } from "./auth";
6229
6235
  const http = httpRouter();
6230
6236
 
6231
6237
  {{#if (or (includes frontend "native-bare") (includes frontend "native-uniwind") (includes frontend "native-unistyles") (includes frontend "tanstack-router") (includes frontend "react-router") (includes frontend "nuxt") (includes frontend "svelte") (includes frontend "solid"))}}
6232
- {{#if (or (includes frontend "tanstack-router") (includes frontend "react-router") (includes frontend "nuxt") (includes frontend "svelte") (includes frontend "solid"))}}
6233
- authComponent.registerRoutesLazy(http, createAuth, {
6234
- cors: true,
6235
- trustedOrigins: [process.env.SITE_URL!],
6236
- });
6237
- {{else}}
6238
6238
  authComponent.registerRoutes(http, createAuth, { cors: true });
6239
- {{/if}}
6240
6239
  {{else}}
6241
6240
  authComponent.registerRoutes(http, createAuth);
6242
6241
  {{/if}}
@@ -8300,7 +8299,7 @@ import { env } from "@{{projectName}}/env/web";
8300
8299
 
8301
8300
  export const authClient = createAuthClient({
8302
8301
  baseURL: env.VITE_CONVEX_SITE_URL,
8303
- plugins: [crossDomainClient(), convexClient()],
8302
+ plugins: [convexClient(), crossDomainClient()],
8304
8303
  });
8305
8304
  `],
8306
8305
  ["auth/better-auth/convex/web/react/react-router/src/routes/dashboard.tsx.hbs", `import SignInForm from "@/components/sign-in-form";
@@ -8704,7 +8703,7 @@ import { env } from "@{{projectName}}/env/web";
8704
8703
 
8705
8704
  export const authClient = createAuthClient({
8706
8705
  baseURL: env.VITE_CONVEX_SITE_URL,
8707
- plugins: [crossDomainClient(), convexClient()],
8706
+ plugins: [convexClient(), crossDomainClient()],
8708
8707
  });
8709
8708
  `],
8710
8709
  ["auth/better-auth/convex/web/react/tanstack-router/src/routes/dashboard.tsx.hbs", `import SignInForm from "@/components/sign-in-form";
@@ -9321,7 +9320,8 @@ export const Route = createFileRoute('/api/auth/$')({
9321
9320
  },
9322
9321
  })
9323
9322
  `],
9324
- ["auth/better-auth/native/bare/app/(drawer)/index.tsx.hbs", `import { View, Text, ScrollView, TouchableOpacity, StyleSheet } from "react-native";
9323
+ ["auth/better-auth/native/bare/app/(drawer)/index.tsx.hbs", `import { Button, Column, Host, Text as ExpoUIText } from "@expo/ui";
9324
+ import { View, ScrollView, StyleSheet } from "react-native";
9325
9325
  import { Container } from "@/components/container";
9326
9326
  import { useColorScheme } from "@/lib/use-color-scheme";
9327
9327
  import { NAV_THEME } from "@/lib/constants";
@@ -9358,65 +9358,101 @@ return (
9358
9358
  <Container>
9359
9359
  <ScrollView style={styles.scrollView}>
9360
9360
  <View style={styles.content}>
9361
- <Text style={[styles.title, { color: theme.text }]}>
9362
- BETTER T STACK
9363
- </Text>
9361
+ <Host style={styles.titleHost} matchContents=\\{{ vertical: true }}>
9362
+ <Column>
9363
+ <ExpoUIText
9364
+ textStyle=\\{{ color: theme.text, fontSize: 24, fontWeight: "bold" }}
9365
+ >
9366
+ BETTER T STACK
9367
+ </ExpoUIText>
9368
+ </Column>
9369
+ </Host>
9364
9370
 
9365
9371
  {session?.user ? (
9366
9372
  <View style={[styles.userCard, { backgroundColor: theme.card, borderColor: theme.border }]}>
9367
- <View style={styles.userHeader}>
9368
- <Text style={[styles.userText, { color: theme.text }]}>
9369
- Welcome, <Text style={styles.userName}>{session.user.name}</Text>
9370
- </Text>
9371
- </View>
9372
- <Text style={[styles.userEmail, { color: theme.text, opacity: 0.7 }]}>
9373
- {session.user.email}
9374
- </Text>
9375
- <TouchableOpacity style={[styles.signOutButton, { backgroundColor: theme.notification }]} onPress={()=> {
9376
- authClient.signOut();
9377
- {{#if (eq api "orpc")}}
9378
- queryClient.invalidateQueries();
9379
- {{/if}}
9380
- {{#if (eq api "trpc")}}
9381
- queryClient.invalidateQueries();
9382
- {{/if}}
9383
- }}
9384
- >
9385
- <Text style={styles.signOutText}>Sign Out</Text>
9386
- </TouchableOpacity>
9373
+ <Host style={styles.userHeader} matchContents=\\{{ vertical: true }}>
9374
+ <Column spacing={8}>
9375
+ <ExpoUIText textStyle=\\{{ color: theme.text, fontSize: 16 }}>
9376
+ {\`Welcome, \${session.user.name}\`}
9377
+ </ExpoUIText>
9378
+ <ExpoUIText
9379
+ textStyle=\\{{ color: theme.text, fontSize: 14 }}
9380
+ style=\\{{ opacity: 0.7 }}
9381
+ >
9382
+ {session.user.email}
9383
+ </ExpoUIText>
9384
+ </Column>
9385
+ </Host>
9386
+ <Host matchContents=\\{{ vertical: true }}>
9387
+ <Button
9388
+ label="Sign Out"
9389
+ variant="outlined"
9390
+ onPress={() => {
9391
+ authClient.signOut();
9392
+ {{#if (eq api "orpc")}}
9393
+ queryClient.invalidateQueries();
9394
+ {{/if}}
9395
+ {{#if (eq api "trpc")}}
9396
+ queryClient.invalidateQueries();
9397
+ {{/if}}
9398
+ }}
9399
+ />
9400
+ </Host>
9387
9401
  </View>
9388
9402
  ) : null}
9389
9403
 
9390
9404
  {{#unless (eq api "none")}}
9391
9405
  <View style={[styles.statusCard, { backgroundColor: theme.card, borderColor: theme.border }]}>
9392
- <Text style={[styles.cardTitle, { color: theme.text }]}>
9393
- System Status
9394
- </Text>
9406
+ <Host style={styles.cardTitleHost} matchContents=\\{{ vertical: true }}>
9407
+ <ExpoUIText
9408
+ textStyle=\\{{ color: theme.text, fontSize: 16, fontWeight: "bold" }}
9409
+ >
9410
+ System Status
9411
+ </ExpoUIText>
9412
+ </Host>
9395
9413
  <View style={styles.statusRow}>
9396
9414
  <View style={[styles.statusIndicator, { backgroundColor: isConnected ? "#10b981" : "#ef4444" }]} />
9397
9415
  <View style={styles.statusContent}>
9398
- <Text style={[styles.statusTitle, { color: theme.text }]}>
9399
- {{#if (eq api "orpc")}}ORPC{{else}}TRPC{{/if}} Backend
9400
- </Text>
9401
- <Text style={[styles.statusText, { color: theme.text, opacity: 0.7 }]}>
9402
- {isLoading
9403
- ? "Checking connection..."
9404
- : isConnected
9405
- ? "Connected to API"
9406
- : "API Disconnected"}
9407
- </Text>
9416
+ <Host matchContents=\\{{ vertical: true }}>
9417
+ <Column spacing={4}>
9418
+ <ExpoUIText
9419
+ textStyle=\\{{ color: theme.text, fontSize: 14, fontWeight: "bold" }}
9420
+ >
9421
+ {{#if (eq api "orpc")}}ORPC{{else}}TRPC{{/if}} Backend
9422
+ </ExpoUIText>
9423
+ <ExpoUIText
9424
+ textStyle=\\{{ color: theme.text, fontSize: 12 }}
9425
+ style=\\{{ opacity: 0.7 }}
9426
+ >
9427
+ {isLoading
9428
+ ? "Checking connection..."
9429
+ : isConnected
9430
+ ? "Connected to API"
9431
+ : "API Disconnected"}
9432
+ </ExpoUIText>
9433
+ </Column>
9434
+ </Host>
9408
9435
  </View>
9409
9436
  </View>
9410
9437
  </View>
9411
9438
 
9412
9439
  <View style={[styles.privateDataCard, { backgroundColor: theme.card, borderColor: theme.border }]}>
9413
- <Text style={[styles.cardTitle, { color: theme.text }]}>
9414
- Private Data
9415
- </Text>
9440
+ <Host style={styles.cardTitleHost} matchContents=\\{{ vertical: true }}>
9441
+ <ExpoUIText
9442
+ textStyle=\\{{ color: theme.text, fontSize: 16, fontWeight: "bold" }}
9443
+ >
9444
+ Private Data
9445
+ </ExpoUIText>
9446
+ </Host>
9416
9447
  {privateData && (
9417
- <Text style={[styles.privateDataText, { color: theme.text, opacity: 0.7 }]}>
9418
- {privateData.data?.message}
9419
- </Text>
9448
+ <Host matchContents=\\{{ vertical: true }}>
9449
+ <ExpoUIText
9450
+ textStyle=\\{{ color: theme.text, fontSize: 14 }}
9451
+ style=\\{{ opacity: 0.7 }}
9452
+ >
9453
+ {privateData.data?.message ?? ""}
9454
+ </ExpoUIText>
9455
+ </Host>
9420
9456
  )}
9421
9457
  </View>
9422
9458
  {{/unless}}
@@ -9438,45 +9474,30 @@ scrollView: {
9438
9474
  flex: 1,
9439
9475
  },
9440
9476
  content: {
9441
- padding: 16,
9477
+ paddingHorizontal: 20,
9478
+ paddingTop: 28,
9479
+ paddingBottom: 32,
9442
9480
  },
9443
- title: {
9444
- fontSize: 24,
9445
- fontWeight: "bold",
9446
- marginBottom: 16,
9481
+ titleHost: {
9482
+ alignSelf: "center",
9483
+ marginBottom: 24,
9447
9484
  },
9448
9485
  userCard: {
9449
9486
  marginBottom: 16,
9450
9487
  padding: 16,
9451
9488
  borderWidth: 1,
9489
+ borderRadius: 16,
9452
9490
  },
9453
9491
  userHeader: {
9454
9492
  marginBottom: 8,
9455
9493
  },
9456
- userText: {
9457
- fontSize: 16,
9458
- },
9459
- userName: {
9460
- fontWeight: "bold",
9461
- },
9462
- userEmail: {
9463
- fontSize: 14,
9464
- marginBottom: 12,
9465
- },
9466
- signOutButton: {
9467
- padding: 12,
9468
- },
9469
- signOutText: {
9470
- color: "#ffffff",
9471
- },
9472
9494
  statusCard: {
9473
9495
  marginBottom: 16,
9474
9496
  padding: 16,
9475
9497
  borderWidth: 1,
9498
+ borderRadius: 16,
9476
9499
  },
9477
- cardTitle: {
9478
- fontSize: 16,
9479
- fontWeight: "bold",
9500
+ cardTitleHost: {
9480
9501
  marginBottom: 12,
9481
9502
  },
9482
9503
  statusRow: {
@@ -9491,22 +9512,14 @@ width: 8,
9491
9512
  statusContent: {
9492
9513
  flex: 1,
9493
9514
  },
9494
- statusTitle: {
9495
- fontSize: 14,
9496
- fontWeight: "bold",
9497
- },
9498
- statusText: {
9499
- fontSize: 12,
9500
- },
9501
9515
  privateDataCard: {
9502
9516
  marginBottom: 16,
9503
9517
  padding: 16,
9504
9518
  borderWidth: 1,
9519
+ borderRadius: 16,
9505
9520
  },
9506
- privateDataText: {
9507
- fontSize: 14,
9508
- },
9509
- });`],
9521
+ });
9522
+ `],
9510
9523
  ["auth/better-auth/native/bare/components/sign-in.tsx.hbs", `import { authClient } from "@/lib/auth-client";
9511
9524
  {{#if (eq api "trpc")}}
9512
9525
  import { queryClient } from "@/utils/trpc";
@@ -11259,14 +11272,8 @@ export function createAuth({{#if (and (eq backend "self") (eq webDeploy "cloudfl
11259
11272
  env.CORS_ORIGIN,
11260
11273
  {{#if (or (includes frontend "native-bare") (includes frontend "native-uniwind") (includes frontend "native-unistyles"))}}
11261
11274
  "{{projectName}}://",
11262
- ...(env.NODE_ENV === "development"
11263
- ? [
11264
- "exp://",
11265
- "exp://**",
11266
- "exp://192.168.*.*:*/**",
11267
- "http://localhost:8081",
11268
- ]
11269
- : []),
11275
+ "exp://",
11276
+ "http://localhost:8081",
11270
11277
  {{/if}}
11271
11278
  ],
11272
11279
  emailAndPassword: {
@@ -11348,14 +11355,8 @@ export function createAuth({{#if (and (eq backend "self") (eq webDeploy "cloudfl
11348
11355
  env.CORS_ORIGIN,
11349
11356
  {{#if (or (includes frontend "native-bare") (includes frontend "native-uniwind") (includes frontend "native-unistyles"))}}
11350
11357
  "{{projectName}}://",
11351
- ...(env.NODE_ENV === "development"
11352
- ? [
11353
- "exp://",
11354
- "exp://**",
11355
- "exp://192.168.*.*:*/**",
11356
- "http://localhost:8081",
11357
- ]
11358
- : []),
11358
+ "exp://",
11359
+ "http://localhost:8081",
11359
11360
  {{/if}}
11360
11361
  ],
11361
11362
  emailAndPassword: {
@@ -11428,14 +11429,8 @@ export function createAuth() {
11428
11429
  env.CORS_ORIGIN,
11429
11430
  {{#if (or (includes frontend "native-bare") (includes frontend "native-uniwind") (includes frontend "native-unistyles"))}}
11430
11431
  "{{projectName}}://",
11431
- ...(env.NODE_ENV === "development"
11432
- ? [
11433
- "exp://",
11434
- "exp://**",
11435
- "exp://192.168.*.*:*/**",
11436
- "http://localhost:8081",
11437
- ]
11438
- : []),
11432
+ "exp://",
11433
+ "http://localhost:8081",
11439
11434
  {{/if}}
11440
11435
  ],
11441
11436
  emailAndPassword: {
@@ -11515,14 +11510,8 @@ export function createAuth({{#if (and (eq backend "self") (eq webDeploy "cloudfl
11515
11510
  env.CORS_ORIGIN,
11516
11511
  {{#if (or (includes frontend "native-bare") (includes frontend "native-uniwind") (includes frontend "native-unistyles"))}}
11517
11512
  "{{projectName}}://",
11518
- ...(env.NODE_ENV === "development"
11519
- ? [
11520
- "exp://",
11521
- "exp://**",
11522
- "exp://192.168.*.*:*/**",
11523
- "http://localhost:8081",
11524
- ]
11525
- : []),
11513
+ "exp://",
11514
+ "http://localhost:8081",
11526
11515
  {{/if}}
11527
11516
  ],
11528
11517
  emailAndPassword: {
@@ -11593,14 +11582,8 @@ export function createAuth({{#if (and (eq backend "self") (eq webDeploy "cloudfl
11593
11582
  env.CORS_ORIGIN,
11594
11583
  {{#if (or (includes frontend "native-bare") (includes frontend "native-uniwind") (includes frontend "native-unistyles"))}}
11595
11584
  "{{projectName}}://",
11596
- ...(env.NODE_ENV === "development"
11597
- ? [
11598
- "exp://",
11599
- "exp://**",
11600
- "exp://192.168.*.*:*/**",
11601
- "http://localhost:8081",
11602
- ]
11603
- : []),
11585
+ "exp://",
11586
+ "http://localhost:8081",
11604
11587
  {{/if}}
11605
11588
  ],
11606
11589
  emailAndPassword: {
@@ -11963,40 +11946,42 @@ export const accountRelations = relations(account, ({ one }) => ({
11963
11946
  ["auth/better-auth/server/db/mongoose/mongodb/src/models/auth.model.ts.hbs", `import mongoose from 'mongoose';
11964
11947
 
11965
11948
  const { Schema, model } = mongoose;
11949
+ const { ObjectId } = Schema.Types;
11966
11950
 
11967
11951
  const userSchema = new Schema(
11968
11952
  {
11969
- _id: { type: String },
11953
+ _id: { type: ObjectId, auto: true },
11970
11954
  name: { type: String, required: true },
11971
11955
  email: { type: String, required: true, unique: true },
11972
- emailVerified: { type: Boolean, required: true },
11956
+ emailVerified: { type: Boolean, required: true, default: false },
11973
11957
  image: { type: String },
11974
- createdAt: { type: Date, required: true },
11975
- updatedAt: { type: Date, required: true },
11958
+ createdAt: { type: Date, required: true, default: Date.now },
11959
+ updatedAt: { type: Date, required: true, default: Date.now },
11976
11960
  },
11977
11961
  { collection: 'user' }
11978
11962
  );
11979
11963
 
11980
11964
  const sessionSchema = new Schema(
11981
11965
  {
11982
- _id: { type: String },
11966
+ _id: { type: ObjectId, auto: true },
11983
11967
  expiresAt: { type: Date, required: true },
11984
11968
  token: { type: String, required: true, unique: true },
11985
- createdAt: { type: Date, required: true },
11986
- updatedAt: { type: Date, required: true },
11969
+ createdAt: { type: Date, required: true, default: Date.now },
11970
+ updatedAt: { type: Date, required: true, default: Date.now },
11987
11971
  ipAddress: { type: String },
11988
11972
  userAgent: { type: String },
11989
- userId: { type: String, ref: 'User', required: true },
11973
+ userId: { type: ObjectId, ref: 'User', required: true },
11990
11974
  },
11991
11975
  { collection: 'session' }
11992
11976
  );
11977
+ sessionSchema.index({ userId: 1 });
11993
11978
 
11994
11979
  const accountSchema = new Schema(
11995
11980
  {
11996
- _id: { type: String },
11981
+ _id: { type: ObjectId, auto: true },
11997
11982
  accountId: { type: String, required: true },
11998
11983
  providerId: { type: String, required: true },
11999
- userId: { type: String, ref: 'User', required: true },
11984
+ userId: { type: ObjectId, ref: 'User', required: true },
12000
11985
  accessToken: { type: String },
12001
11986
  refreshToken: { type: String },
12002
11987
  idToken: { type: String },
@@ -12004,23 +11989,25 @@ const accountSchema = new Schema(
12004
11989
  refreshTokenExpiresAt: { type: Date },
12005
11990
  scope: { type: String },
12006
11991
  password: { type: String },
12007
- createdAt: { type: Date, required: true },
12008
- updatedAt: { type: Date, required: true },
11992
+ createdAt: { type: Date, required: true, default: Date.now },
11993
+ updatedAt: { type: Date, required: true, default: Date.now },
12009
11994
  },
12010
11995
  { collection: 'account' }
12011
11996
  );
11997
+ accountSchema.index({ userId: 1 });
12012
11998
 
12013
11999
  const verificationSchema = new Schema(
12014
12000
  {
12015
- _id: { type: String },
12001
+ _id: { type: ObjectId, auto: true },
12016
12002
  identifier: { type: String, required: true },
12017
12003
  value: { type: String, required: true },
12018
12004
  expiresAt: { type: Date, required: true },
12019
- createdAt: { type: Date },
12020
- updatedAt: { type: Date },
12005
+ createdAt: { type: Date, required: true, default: Date.now },
12006
+ updatedAt: { type: Date, required: true, default: Date.now },
12021
12007
  },
12022
12008
  { collection: 'verification' }
12023
12009
  );
12010
+ verificationSchema.index({ identifier: 1 });
12024
12011
 
12025
12012
  const User = model('User', userSchema);
12026
12013
  const Session = model('Session', sessionSchema);
@@ -17295,6 +17282,82 @@ export const startInstance = createStart(() => {
17295
17282
  `],
17296
17283
  ["backend/convex/packages/backend/_gitignore", `
17297
17284
  .env.local
17285
+ `],
17286
+ ["backend/convex/packages/backend/convex/_generated/api.d.ts", `/* eslint-disable */
17287
+ export declare const api: any;
17288
+ export declare const internal: any;
17289
+ export declare const components: any;
17290
+ `],
17291
+ ["backend/convex/packages/backend/convex/_generated/api.js", `/* eslint-disable */
17292
+ import { anyApi, componentsGeneric } from "convex/server";
17293
+
17294
+ export const api = anyApi;
17295
+ export const internal = anyApi;
17296
+ export const components = componentsGeneric();
17297
+ `],
17298
+ ["backend/convex/packages/backend/convex/_generated/dataModel.d.ts", `/* eslint-disable */
17299
+ import type {
17300
+ DataModelFromSchemaDefinition,
17301
+ DocumentByName,
17302
+ SystemTableNames,
17303
+ TableNamesInDataModel,
17304
+ } from "convex/server";
17305
+ import type { GenericId } from "convex/values";
17306
+
17307
+ import schema from "../schema.js";
17308
+
17309
+ export type DataModel = DataModelFromSchemaDefinition<typeof schema>;
17310
+ export type TableNames = TableNamesInDataModel<DataModel>;
17311
+ export type Doc<TableName extends TableNames> = DocumentByName<DataModel, TableName>;
17312
+ export type Id<TableName extends TableNames | SystemTableNames> = GenericId<TableName>;
17313
+ `],
17314
+ ["backend/convex/packages/backend/convex/_generated/server.d.ts", `/* eslint-disable */
17315
+ import type {
17316
+ ActionBuilder,
17317
+ GenericActionCtx,
17318
+ GenericDatabaseReader,
17319
+ GenericDatabaseWriter,
17320
+ GenericMutationCtx,
17321
+ GenericQueryCtx,
17322
+ HttpActionBuilder,
17323
+ MutationBuilder,
17324
+ QueryBuilder,
17325
+ } from "convex/server";
17326
+
17327
+ import type { DataModel } from "./dataModel.js";
17328
+
17329
+ export declare const query: QueryBuilder<DataModel, "public">;
17330
+ export declare const internalQuery: QueryBuilder<DataModel, "internal">;
17331
+ export declare const mutation: MutationBuilder<DataModel, "public">;
17332
+ export declare const internalMutation: MutationBuilder<DataModel, "internal">;
17333
+ export declare const action: ActionBuilder<DataModel, "public">;
17334
+ export declare const internalAction: ActionBuilder<DataModel, "internal">;
17335
+ export declare const httpAction: HttpActionBuilder;
17336
+
17337
+ export type QueryCtx = GenericQueryCtx<DataModel>;
17338
+ export type MutationCtx = GenericMutationCtx<DataModel>;
17339
+ export type ActionCtx = GenericActionCtx<DataModel>;
17340
+ export type DatabaseReader = GenericDatabaseReader<DataModel>;
17341
+ export type DatabaseWriter = GenericDatabaseWriter<DataModel>;
17342
+ `],
17343
+ ["backend/convex/packages/backend/convex/_generated/server.js", `/* eslint-disable */
17344
+ import {
17345
+ actionGeneric,
17346
+ httpActionGeneric,
17347
+ internalActionGeneric,
17348
+ internalMutationGeneric,
17349
+ internalQueryGeneric,
17350
+ mutationGeneric,
17351
+ queryGeneric,
17352
+ } from "convex/server";
17353
+
17354
+ export const query = queryGeneric;
17355
+ export const internalQuery = internalQueryGeneric;
17356
+ export const mutation = mutationGeneric;
17357
+ export const internalMutation = internalMutationGeneric;
17358
+ export const action = actionGeneric;
17359
+ export const internalAction = internalActionGeneric;
17360
+ export const httpAction = httpActionGeneric;
17298
17361
  `],
17299
17362
  ["backend/convex/packages/backend/convex/convex.config.ts.hbs", `import { defineApp } from "convex/server";
17300
17363
  {{#if (eq auth "better-auth")}}
@@ -17436,6 +17499,7 @@ export default defineSchema({
17436
17499
  "jsx": "react-jsx",
17437
17500
  "skipLibCheck": true,
17438
17501
  "allowSyntheticDefaultImports": true,
17502
+ "types": ["node"],
17439
17503
 
17440
17504
  /* These compiler options are required by Convex */
17441
17505
  "target": "ESNext",
@@ -17712,10 +17776,8 @@ import { ZodToJsonSchemaConverter } from "@orpc/zod/zod4";
17712
17776
  import { RPCHandler } from "@orpc/server/node";
17713
17777
  import { onError } from "@orpc/server";
17714
17778
  import { appRouter } from "@{{projectName}}/api/routers/index";
17715
- {{#if (or (eq auth "better-auth") (eq auth "clerk"))}}
17716
17779
  import { createContext } from "@{{projectName}}/api/context";
17717
17780
  {{/if}}
17718
- {{/if}}
17719
17781
  import cors from "cors";
17720
17782
  import express from "express";
17721
17783
  {{#if (includes examples "ai")}}
@@ -17788,21 +17850,13 @@ const apiHandler = new OpenAPIHandler(appRouter, {
17788
17850
  app.use(async (req, res, next) => {
17789
17851
  const rpcResult = await rpcHandler.handle(req, res, {
17790
17852
  prefix: "/rpc",
17791
- {{#if (or (eq auth "better-auth") (eq auth "clerk"))}}
17792
17853
  context: await createContext({ req }),
17793
- {{else}}
17794
- context: {},
17795
- {{/if}}
17796
17854
  });
17797
17855
  if (rpcResult.matched) return;
17798
17856
 
17799
17857
  const apiResult = await apiHandler.handle(req, res, {
17800
17858
  prefix: "/api-reference",
17801
- {{#if (or (eq auth "better-auth") (eq auth "clerk"))}}
17802
17859
  context: await createContext({ req }),
17803
- {{else}}
17804
- context: {},
17805
- {{/if}}
17806
17860
  });
17807
17861
  if (apiResult.matched) return;
17808
17862
 
@@ -17913,7 +17967,10 @@ const fastify = Fastify({
17913
17967
 
17914
17968
  fastify.register(fastifyCors, baseCorsConfig);
17915
17969
  {{#if (eq auth "clerk")}}
17916
- fastify.register(clerkPlugin);
17970
+ fastify.register(clerkPlugin, {
17971
+ publishableKey: env.CLERK_PUBLISHABLE_KEY,
17972
+ secretKey: env.CLERK_SECRET_KEY,
17973
+ });
17917
17974
  {{/if}}
17918
17975
 
17919
17976
  {{#if (eq api "orpc")}}
@@ -18678,14 +18735,13 @@ export function createDb() {
18678
18735
  }
18679
18736
  {{/if}}
18680
18737
  `],
18738
+ ["db/drizzle/sqlite/src/migrations/.gitkeep", ``],
18681
18739
  ["db/mongoose/mongodb/src/index.ts.hbs", `import mongoose from "mongoose";
18682
18740
  import { env } from "@{{projectName}}/env/server";
18683
18741
 
18684
- await mongoose.connect(env.DATABASE_URL).catch((error) => {
18685
- console.log("Error connecting to database:", error);
18686
- });
18742
+ await mongoose.connect(env.DATABASE_URL);
18687
18743
 
18688
- const client = mongoose.connection.getClient().db("myDB");
18744
+ const client = mongoose.connection.getClient().db();
18689
18745
 
18690
18746
  export { client };
18691
18747
  `],
@@ -19007,6 +19063,7 @@ export default defineConfig({
19007
19063
  {{/if}}
19008
19064
  },
19009
19065
  });`],
19066
+ ["db/prisma/sqlite/prisma/migrations/.gitkeep", ``],
19010
19067
  ["db/prisma/sqlite/prisma/schema/schema.prisma.hbs", `generator client {
19011
19068
  provider = "prisma-client"
19012
19069
  output = "../generated"
@@ -19252,7 +19309,6 @@ import { Ionicons } from "@expo/vector-icons";
19252
19309
  import {
19253
19310
  useUIMessages,
19254
19311
  useSmoothText,
19255
- type UIMessage,
19256
19312
  } from "@convex-dev/agent/react";
19257
19313
  import { api } from "@{{projectName}}/backend/convex/_generated/api";
19258
19314
  import { useMutation } from "convex/react";
@@ -19306,7 +19362,7 @@ export default function AIScreen() {
19306
19362
  );
19307
19363
 
19308
19364
  const hasStreamingMessage = messages?.some(
19309
- (m: UIMessage) => m.status === "streaming",
19365
+ (m) => m.status === "streaming",
19310
19366
  );
19311
19367
 
19312
19368
  useEffect(() => {
@@ -19363,7 +19419,7 @@ export default function AIScreen() {
19363
19419
  </View>
19364
19420
  ) : (
19365
19421
  <View style={styles.messagesList}>
19366
- {messages.map((message: UIMessage) => (
19422
+ {messages.map((message) => (
19367
19423
  <View
19368
19424
  key={message.key}
19369
19425
  style={[
@@ -19383,7 +19439,9 @@ export default function AIScreen() {
19383
19439
  {message.role === "user" ? "You" : "AI Assistant"}
19384
19440
  </Text>
19385
19441
  <MessageContent
19386
- text={message.text ?? ""}
19442
+ text={(message.parts ?? [])
19443
+ .map((part) => (part.type === "text" ? part.text : ""))
19444
+ .join("")}
19387
19445
  isStreaming={message.status === "streaming"}
19388
19446
  textColor={theme.text}
19389
19447
  />
@@ -19546,6 +19604,7 @@ const styles = StyleSheet.create({
19546
19604
  });
19547
19605
  {{else}}
19548
19606
  import { useRef, useEffect, useState } from "react";
19607
+ import { Ionicons } from "@expo/vector-icons";
19549
19608
  import {
19550
19609
  View,
19551
19610
  Text,
@@ -19558,8 +19617,6 @@ import {
19558
19617
  } from "react-native";
19559
19618
  import { useChat } from "@ai-sdk/react";
19560
19619
  import { DefaultChatTransport } from "ai";
19561
- import { fetch as expoFetch } from "expo/fetch";
19562
- import { Ionicons } from "@expo/vector-icons";
19563
19620
  import { Container } from "@/components/container";
19564
19621
  import { useColorScheme } from "@/lib/use-color-scheme";
19565
19622
  import { NAV_THEME } from "@/lib/constants";
@@ -19582,7 +19639,6 @@ export default function AIScreen() {
19582
19639
  const [input, setInput] = useState("");
19583
19640
  const { messages, error, sendMessage } = useChat({
19584
19641
  transport: new DefaultChatTransport({
19585
- fetch: expoFetch as unknown as typeof globalThis.fetch,
19586
19642
  api: generateAPIUrl("/ai"),
19587
19643
  }),
19588
19644
  onError: (error) => console.error(error, "AI Chat Error"),
@@ -19862,7 +19918,6 @@ import { Ionicons } from "@expo/vector-icons";
19862
19918
  import {
19863
19919
  useUIMessages,
19864
19920
  useSmoothText,
19865
- type UIMessage,
19866
19921
  } from "@convex-dev/agent/react";
19867
19922
  import { api } from "@{{projectName}}/backend/convex/_generated/api";
19868
19923
  import { useMutation } from "convex/react";
@@ -19913,7 +19968,7 @@ export default function AIScreen() {
19913
19968
  );
19914
19969
 
19915
19970
  const hasStreamingMessage = messages?.some(
19916
- (m: UIMessage) => m.status === "streaming",
19971
+ (m) => m.status === "streaming",
19917
19972
  );
19918
19973
 
19919
19974
  useEffect(() => {
@@ -19969,7 +20024,7 @@ export default function AIScreen() {
19969
20024
  </View>
19970
20025
  ) : (
19971
20026
  <View style={styles.messagesWrapper}>
19972
- {messages.map((message: UIMessage) => (
20027
+ {messages.map((message) => (
19973
20028
  <View
19974
20029
  key={message.key}
19975
20030
  style={[
@@ -19983,7 +20038,9 @@ export default function AIScreen() {
19983
20038
  {message.role === "user" ? "You" : "AI Assistant"}
19984
20039
  </Text>
19985
20040
  <MessageContent
19986
- text={message.text ?? ""}
20041
+ text={(message.parts ?? [])
20042
+ .map((part) => (part.type === "text" ? part.text : ""))
20043
+ .join("")}
19987
20044
  isStreaming={message.status === "streaming"}
19988
20045
  style={styles.messageContent}
19989
20046
  />
@@ -20163,7 +20220,6 @@ import {
20163
20220
  } from "react-native";
20164
20221
  import { useChat } from "@ai-sdk/react";
20165
20222
  import { DefaultChatTransport } from "ai";
20166
- import { fetch as expoFetch } from "expo/fetch";
20167
20223
  import { Ionicons } from "@expo/vector-icons";
20168
20224
  import { StyleSheet, useUnistyles } from "react-native-unistyles";
20169
20225
  import { Container } from "@/components/container";
@@ -20185,7 +20241,6 @@ export default function AIScreen() {
20185
20241
  const [input, setInput] = useState("");
20186
20242
  const { messages, error, sendMessage } = useChat({
20187
20243
  transport: new DefaultChatTransport({
20188
- fetch: expoFetch as unknown as typeof globalThis.fetch,
20189
20244
  api: generateAPIUrl("/ai"),
20190
20245
  }),
20191
20246
  onError: (error) => console.error(error, "AI Chat Error"),
@@ -20474,7 +20529,6 @@ import { Ionicons } from "@expo/vector-icons";
20474
20529
  import {
20475
20530
  useUIMessages,
20476
20531
  useSmoothText,
20477
- type UIMessage,
20478
20532
  } from "@convex-dev/agent/react";
20479
20533
  import { api } from "@{{projectName}}/backend/convex/_generated/api";
20480
20534
  import { useMutation } from "convex/react";
@@ -20521,7 +20575,7 @@ export default function AIScreen() {
20521
20575
  );
20522
20576
 
20523
20577
  const hasStreamingMessage = messages?.some(
20524
- (m: UIMessage) => m.status === "streaming",
20578
+ (m) => m.status === "streaming",
20525
20579
  );
20526
20580
 
20527
20581
  useEffect(() => {
@@ -20576,7 +20630,7 @@ export default function AIScreen() {
20576
20630
  </Surface>
20577
20631
  ) : (
20578
20632
  <View className="gap-3">
20579
- {messages.map((message: UIMessage) => (
20633
+ {messages.map((message) => (
20580
20634
  <Surface
20581
20635
  key={message.key}
20582
20636
  variant={message.role === "user" ? "tertiary" : "secondary"}
@@ -20586,7 +20640,9 @@ export default function AIScreen() {
20586
20640
  {message.role === "user" ? "You" : "AI"}
20587
20641
  </Text>
20588
20642
  <MessageContent
20589
- text={message.text ?? ""}
20643
+ text={(message.parts ?? [])
20644
+ .map((part) => (part.type === "text" ? part.text : ""))
20645
+ .join("")}
20590
20646
  isStreaming={message.status === "streaming"}
20591
20647
  />
20592
20648
  </Surface>
@@ -20649,7 +20705,6 @@ import {
20649
20705
  } from "react-native";
20650
20706
  import { useChat } from "@ai-sdk/react";
20651
20707
  import { DefaultChatTransport } from "ai";
20652
- import { fetch as expoFetch } from "expo/fetch";
20653
20708
  import { Ionicons } from "@expo/vector-icons";
20654
20709
  import { Container } from "@/components/container";
20655
20710
  import { Button, Separator, FieldError, Spinner, Surface, Input, TextField, useThemeColor } from "heroui-native";
@@ -20670,7 +20725,6 @@ export default function AIScreen() {
20670
20725
  const [input, setInput] = useState("");
20671
20726
  const { messages, error, sendMessage, status } = useChat({
20672
20727
  transport: new DefaultChatTransport({
20673
- fetch: expoFetch as unknown as typeof globalThis.fetch,
20674
20728
  api: generateAPIUrl("/ai"),
20675
20729
  }),
20676
20730
  onError: (error) => console.error(error, "AI Chat Error"),
@@ -20994,7 +21048,6 @@ import { api } from "@{{projectName}}/backend/convex/_generated/api";
20994
21048
  import {
20995
21049
  useUIMessages,
20996
21050
  useSmoothText,
20997
- type UIMessage,
20998
21051
  } from "@convex-dev/agent/react";
20999
21052
  import { useMutation } from "convex/react";
21000
21053
  import { Send, Loader2 } from "lucide-react";
@@ -21053,7 +21106,7 @@ export default function AIPage() {
21053
21106
  }, [messages]);
21054
21107
 
21055
21108
  const hasStreamingMessage = messages?.some(
21056
- (m: UIMessage) => m.status === "streaming",
21109
+ (m) => m.status === "streaming",
21057
21110
  );
21058
21111
 
21059
21112
  const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
@@ -21087,7 +21140,7 @@ export default function AIPage() {
21087
21140
  Ask me anything to get started!
21088
21141
  </div>
21089
21142
  ) : (
21090
- messages.map((message: UIMessage) => (
21143
+ messages.map((message) => (
21091
21144
  <div
21092
21145
  key={message.key}
21093
21146
  className={\`p-3 rounded-lg \${
@@ -21100,7 +21153,9 @@ export default function AIPage() {
21100
21153
  {message.role === "user" ? "You" : "AI Assistant"}
21101
21154
  </p>
21102
21155
  <MessageContent
21103
- text={message.text ?? ""}
21156
+ text={(message.parts ?? [])
21157
+ .map((part) => (part.type === "text" ? part.text : ""))
21158
+ .join("")}
21104
21159
  isStreaming={message.status === "streaming"}
21105
21160
  />
21106
21161
  </div>
@@ -21260,7 +21315,6 @@ import { api } from "@{{projectName}}/backend/convex/_generated/api";
21260
21315
  import {
21261
21316
  useUIMessages,
21262
21317
  useSmoothText,
21263
- type UIMessage,
21264
21318
  } from "@convex-dev/agent/react";
21265
21319
  import { useMutation } from "convex/react";
21266
21320
  import { Send, Loader2 } from "lucide-react";
@@ -21303,7 +21357,7 @@ const AI: React.FC = () => {
21303
21357
  }, [messages]);
21304
21358
 
21305
21359
  const hasStreamingMessage = messages?.some(
21306
- (m: UIMessage) => m.status === "streaming",
21360
+ (m) => m.status === "streaming",
21307
21361
  );
21308
21362
 
21309
21363
  const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
@@ -21337,7 +21391,7 @@ const AI: React.FC = () => {
21337
21391
  Ask me anything to get started!
21338
21392
  </div>
21339
21393
  ) : (
21340
- messages.map((message: UIMessage) => (
21394
+ messages.map((message) => (
21341
21395
  <div
21342
21396
  key={message.key}
21343
21397
  className={\`p-3 rounded-lg \${
@@ -21350,7 +21404,9 @@ const AI: React.FC = () => {
21350
21404
  {message.role === "user" ? "You" : "AI Assistant"}
21351
21405
  </p>
21352
21406
  <MessageContent
21353
- text={message.text ?? ""}
21407
+ text={(message.parts ?? [])
21408
+ .map((part) => (part.type === "text" ? part.text : ""))
21409
+ .join("")}
21354
21410
  isStreaming={message.status === "streaming"}
21355
21411
  />
21356
21412
  </div>
@@ -21496,7 +21552,6 @@ import { api } from "@{{projectName}}/backend/convex/_generated/api";
21496
21552
  import {
21497
21553
  useUIMessages,
21498
21554
  useSmoothText,
21499
- type UIMessage,
21500
21555
  } from "@convex-dev/agent/react";
21501
21556
  import { createFileRoute } from "@tanstack/react-router";
21502
21557
  import { useMutation } from "convex/react";
@@ -21544,7 +21599,7 @@ function RouteComponent() {
21544
21599
  }, [messages]);
21545
21600
 
21546
21601
  const hasStreamingMessage = messages?.some(
21547
- (m: UIMessage) => m.status === "streaming",
21602
+ (m) => m.status === "streaming",
21548
21603
  );
21549
21604
 
21550
21605
  const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
@@ -21578,7 +21633,7 @@ function RouteComponent() {
21578
21633
  Ask me anything to get started!
21579
21634
  </div>
21580
21635
  ) : (
21581
- messages.map((message: UIMessage) => (
21636
+ messages.map((message) => (
21582
21637
  <div
21583
21638
  key={message.key}
21584
21639
  className={\`p-3 rounded-lg \${
@@ -21591,7 +21646,9 @@ function RouteComponent() {
21591
21646
  {message.role === "user" ? "You" : "AI Assistant"}
21592
21647
  </p>
21593
21648
  <MessageContent
21594
- text={message.text ?? ""}
21649
+ text={(message.parts ?? [])
21650
+ .map((part) => (part.type === "text" ? part.text : ""))
21651
+ .join("")}
21595
21652
  isStreaming={message.status === "streaming"}
21596
21653
  />
21597
21654
  </div>
@@ -21739,7 +21796,6 @@ import { api } from "@{{projectName}}/backend/convex/_generated/api";
21739
21796
  import {
21740
21797
  useUIMessages,
21741
21798
  useSmoothText,
21742
- type UIMessage,
21743
21799
  } from "@convex-dev/agent/react";
21744
21800
  import { createFileRoute } from "@tanstack/react-router";
21745
21801
  import { useMutation } from "convex/react";
@@ -21787,7 +21843,7 @@ function RouteComponent() {
21787
21843
  }, [messages]);
21788
21844
 
21789
21845
  const hasStreamingMessage = messages?.some(
21790
- (m: UIMessage) => m.status === "streaming",
21846
+ (m) => m.status === "streaming",
21791
21847
  );
21792
21848
 
21793
21849
  const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
@@ -21821,7 +21877,7 @@ function RouteComponent() {
21821
21877
  Ask me anything to get started!
21822
21878
  </div>
21823
21879
  ) : (
21824
- messages.map((message: UIMessage) => (
21880
+ messages.map((message) => (
21825
21881
  <div
21826
21882
  key={message.key}
21827
21883
  className={\`p-3 rounded-lg \${
@@ -21834,7 +21890,9 @@ function RouteComponent() {
21834
21890
  {message.role === "user" ? "You" : "AI Assistant"}
21835
21891
  </p>
21836
21892
  <MessageContent
21837
- text={message.text ?? ""}
21893
+ text={(message.parts ?? [])
21894
+ .map((part) => (part.type === "text" ? part.text : ""))
21895
+ .join("")}
21838
21896
  isStreaming={message.status === "streaming"}
21839
21897
  />
21840
21898
  </div>
@@ -22149,7 +22207,7 @@ import { Ionicons } from "@expo/vector-icons";
22149
22207
  {{#if (eq backend "convex")}}
22150
22208
  import { useMutation, useQuery } from "convex/react";
22151
22209
  import { api } from "@{{projectName}}/backend/convex/_generated/api";
22152
- import type { Id } from "@{{projectName}}/backend/convex/_generated/dataModel";
22210
+ import type { Doc, Id } from "@{{projectName}}/backend/convex/_generated/dataModel";
22153
22211
  {{else}}
22154
22212
  import { useMutation, useQuery } from "@tanstack/react-query";
22155
22213
  {{/if}}
@@ -22199,7 +22257,7 @@ export default function TodosScreen() {
22199
22257
  }
22200
22258
 
22201
22259
  const isLoading = !todos;
22202
- const completedCount = todos?.filter((t) => t.completed).length || 0;
22260
+ const completedCount = todos?.filter((t: Doc<"todos">) => t.completed).length || 0;
22203
22261
  const totalCount = todos?.length || 0;
22204
22262
  {{else}}
22205
22263
  {{#if (eq api "orpc")}}
@@ -22408,7 +22466,7 @@ export default function TodosScreen() {
22408
22466
 
22409
22467
  {todos && todos.length > 0 && (
22410
22468
  <View style={styles.todosList}>
22411
- {todos.map((todo) => (
22469
+ {todos.map((todo: Doc<"todos">) => (
22412
22470
  <View
22413
22471
  key={todo._id}
22414
22472
  style={[
@@ -22598,9 +22656,10 @@ const styles = StyleSheet.create({
22598
22656
  fontSize: 16,
22599
22657
  },
22600
22658
  addButton: {
22601
- padding: 12,
22602
- justifyContent: "center",
22659
+ width: 48,
22660
+ height: 48,
22603
22661
  alignItems: "center",
22662
+ justifyContent: "center",
22604
22663
  },
22605
22664
  centerContainer: {
22606
22665
  alignItems: "center",
@@ -22638,23 +22697,24 @@ const styles = StyleSheet.create({
22638
22697
  alignItems: "center",
22639
22698
  gap: 12,
22640
22699
  },
22641
- checkbox: {
22642
- width: 20,
22643
- height: 20,
22644
- borderWidth: 2,
22645
- justifyContent: "center",
22646
- alignItems: "center",
22647
- },
22648
22700
  todoTextContainer: {
22649
22701
  flex: 1,
22650
22702
  },
22651
22703
  todoText: {
22652
22704
  fontSize: 16,
22653
22705
  },
22706
+ checkbox: {
22707
+ width: 24,
22708
+ height: 24,
22709
+ borderWidth: 2,
22710
+ alignItems: "center",
22711
+ justifyContent: "center",
22712
+ },
22654
22713
  deleteButton: {
22655
- padding: 8,
22714
+ padding: 4,
22656
22715
  },
22657
- });`],
22716
+ });
22717
+ `],
22658
22718
  ["examples/todo/native/unistyles/app/(drawer)/todos.tsx.hbs", `import { useState } from "react";
22659
22719
  import {
22660
22720
  View,
@@ -22671,7 +22731,7 @@ import { StyleSheet, useUnistyles } from "react-native-unistyles";
22671
22731
  {{#if (eq backend "convex")}}
22672
22732
  import { useMutation, useQuery } from "convex/react";
22673
22733
  import { api } from "@{{projectName}}/backend/convex/_generated/api";
22674
- import type { Id } from "@{{projectName}}/backend/convex/_generated/dataModel";
22734
+ import type { Doc, Id } from "@{{projectName}}/backend/convex/_generated/dataModel";
22675
22735
  {{else}}
22676
22736
  import { useMutation, useQuery } from "@tanstack/react-query";
22677
22737
  {{/if}}
@@ -22839,7 +22899,7 @@ export default function TodosScreen() {
22839
22899
  {todos && todos.length === 0 && !isLoading && (
22840
22900
  <Text style={styles.emptyText}>No todos yet. Add one!</Text>
22841
22901
  )}
22842
- {todos?.map((todo) => (
22902
+ {todos?.map((todo: Doc<"todos">) => (
22843
22903
  <View key={todo._id} style={styles.todoItem}>
22844
22904
  <TouchableOpacity
22845
22905
  onPress={() => handleToggleTodo(todo._id, todo.completed)}
@@ -23002,7 +23062,7 @@ import { Ionicons } from "@expo/vector-icons";
23002
23062
  {{#if (eq backend "convex")}}
23003
23063
  import { useMutation, useQuery } from "convex/react";
23004
23064
  import { api } from "@{{projectName}}/backend/convex/_generated/api";
23005
- import type { Id } from "@{{projectName}}/backend/convex/_generated/dataModel";
23065
+ import type { Doc, Id } from "@{{projectName}}/backend/convex/_generated/dataModel";
23006
23066
  {{else}}
23007
23067
  import { useMutation, useQuery } from "@tanstack/react-query";
23008
23068
  {{/if}}
@@ -23093,7 +23153,7 @@ export default function TodosScreen() {
23093
23153
  };
23094
23154
 
23095
23155
  const isLoading = !todos;
23096
- const completedCount = todos?.filter((t) => t.completed).length || 0;
23156
+ const completedCount = todos?.filter((t: Doc<"todos">) => t.completed).length || 0;
23097
23157
  const totalCount = todos?.length || 0;
23098
23158
  {{else}}
23099
23159
  const handleAddTodo = () => {
@@ -23205,7 +23265,7 @@ export default function TodosScreen() {
23205
23265
 
23206
23266
  {todos && todos.length > 0 && (
23207
23267
  <View className="gap-2">
23208
- {todos.map((todo) => (
23268
+ {todos.map((todo: Doc<"todos">) => (
23209
23269
  <Surface key={todo._id} variant="secondary" className="p-3 rounded-lg">
23210
23270
  <View className="flex-row items-center gap-3">
23211
23271
  <Checkbox
@@ -23277,7 +23337,8 @@ export default function TodosScreen() {
23277
23337
  </ScrollView>
23278
23338
  </Container>
23279
23339
  );
23280
- }`],
23340
+ }
23341
+ `],
23281
23342
  ["examples/todo/server/drizzle/base/src/routers/todo.ts.hbs", `{{#if (eq api "orpc")}}
23282
23343
  import { eq } from "drizzle-orm";
23283
23344
  import z from "zod";
@@ -23412,19 +23473,22 @@ export const todo = sqliteTable("todo", {
23412
23473
  `],
23413
23474
  ["examples/todo/server/mongoose/base/src/routers/todo.ts.hbs", `{{#if (eq api "orpc")}}
23414
23475
  import z from "zod";
23476
+ import "@{{projectName}}/db";
23415
23477
  import { publicProcedure } from "../index";
23416
23478
  import { Todo } from "@{{projectName}}/db/models/todo.model";
23417
23479
 
23418
23480
  export const todoRouter = {
23419
23481
  getAll: publicProcedure.handler(async () => {
23420
- return await Todo.find().lean();
23482
+ const todos = await Todo.find().lean();
23483
+ return todos.map((todo) => ({ ...todo, id: todo.id }));
23421
23484
  }),
23422
23485
 
23423
23486
  create: publicProcedure
23424
23487
  .input(z.object({ text: z.string().min(1) }))
23425
23488
  .handler(async ({ input }) => {
23426
23489
  const newTodo = await Todo.create({ text: input.text });
23427
- return newTodo.toObject();
23490
+ const todo = newTodo.toObject();
23491
+ return { ...todo, id: todo.id };
23428
23492
  }),
23429
23493
 
23430
23494
  toggle: publicProcedure
@@ -23446,19 +23510,22 @@ export const todoRouter = {
23446
23510
 
23447
23511
  {{#if (eq api "trpc")}}
23448
23512
  import z from "zod";
23513
+ import "@{{projectName}}/db";
23449
23514
  import { router, publicProcedure } from "../index";
23450
23515
  import { Todo } from "@{{projectName}}/db/models/todo.model";
23451
23516
 
23452
23517
  export const todoRouter = router({
23453
23518
  getAll: publicProcedure.query(async () => {
23454
- return await Todo.find().lean();
23519
+ const todos = await Todo.find().lean();
23520
+ return todos.map((todo) => ({ ...todo, id: todo.id }));
23455
23521
  }),
23456
23522
 
23457
23523
  create: publicProcedure
23458
23524
  .input(z.object({ text: z.string().min(1) }))
23459
23525
  .mutation(async ({ input }) => {
23460
23526
  const newTodo = await Todo.create({ text: input.text });
23461
- return newTodo.toObject();
23527
+ const todo = newTodo.toObject();
23528
+ return { ...todo, id: todo.id };
23462
23529
  }),
23463
23530
 
23464
23531
  toggle: publicProcedure
@@ -23483,8 +23550,9 @@ const { Schema, model } = mongoose;
23483
23550
 
23484
23551
  const todoSchema = new Schema({
23485
23552
  id: {
23486
- type: mongoose.Schema.Types.ObjectId,
23487
- auto: true,
23553
+ type: String,
23554
+ required: true,
23555
+ default: () => new mongoose.Types.ObjectId().toString(),
23488
23556
  },
23489
23557
  text: {
23490
23558
  type: String,
@@ -23495,7 +23563,8 @@ const todoSchema = new Schema({
23495
23563
  default: false,
23496
23564
  },
23497
23565
  }, {
23498
- collection: 'todo'
23566
+ collection: 'todo',
23567
+ id: false,
23499
23568
  });
23500
23569
 
23501
23570
  const Todo = model('Todo', todoSchema);
@@ -24103,6 +24172,9 @@ import { trpc } from "@/utils/trpc";
24103
24172
  {{/if}}
24104
24173
  {{/if}}
24105
24174
 
24175
+ {{#unless (eq backend "convex")}}
24176
+ type TodoId = {{#if (or (eq orm "mongoose") (eq database "mongodb"))}}string{{else}}number{{/if}};
24177
+ {{/unless}}
24106
24178
 
24107
24179
  export default function TodosPage() {
24108
24180
  const [newTodoText, setNewTodoText] = useState("");
@@ -24179,11 +24251,11 @@ export default function TodosPage() {
24179
24251
  }
24180
24252
  };
24181
24253
 
24182
- const handleToggleTodo = (id: number, completed: boolean) => {
24254
+ const handleToggleTodo = (id: TodoId, completed: boolean) => {
24183
24255
  toggleMutation.mutate({ id, completed: !completed });
24184
24256
  };
24185
24257
 
24186
- const handleDeleteTodo = (id: number) => {
24258
+ const handleDeleteTodo = (id: TodoId) => {
24187
24259
  deleteMutation.mutate({ id });
24188
24260
  };
24189
24261
  {{/if}}
@@ -24347,6 +24419,10 @@ import type { Id } from "@{{projectName}}/backend/convex/_generated/dataModel";
24347
24419
  import { useMutation, useQuery } from "@tanstack/react-query";
24348
24420
  {{/if}}
24349
24421
 
24422
+ {{#unless (eq backend "convex")}}
24423
+ type TodoId = {{#if (or (eq orm "mongoose") (eq database "mongodb"))}}string{{else}}number{{/if}};
24424
+ {{/unless}}
24425
+
24350
24426
  export default function Todos() {
24351
24427
  const [newTodoText, setNewTodoText] = useState("");
24352
24428
 
@@ -24422,11 +24498,11 @@ export default function Todos() {
24422
24498
  }
24423
24499
  };
24424
24500
 
24425
- const handleToggleTodo = (id: number, completed: boolean) => {
24501
+ const handleToggleTodo = (id: TodoId, completed: boolean) => {
24426
24502
  toggleMutation.mutate({ id, completed: !completed });
24427
24503
  };
24428
24504
 
24429
- const handleDeleteTodo = (id: number) => {
24505
+ const handleDeleteTodo = (id: TodoId) => {
24430
24506
  deleteMutation.mutate({ id });
24431
24507
  };
24432
24508
  {{/if}}
@@ -24591,6 +24667,10 @@ import type { Id } from "@{{projectName}}/backend/convex/_generated/dataModel";
24591
24667
  import { useMutation, useQuery } from "@tanstack/react-query";
24592
24668
  {{/if}}
24593
24669
 
24670
+ {{#unless (eq backend "convex")}}
24671
+ type TodoId = {{#if (or (eq orm "mongoose") (eq database "mongodb"))}}string{{else}}number{{/if}};
24672
+ {{/unless}}
24673
+
24594
24674
  export const Route = createFileRoute("/todos")({
24595
24675
  component: TodosRoute,
24596
24676
  });
@@ -24670,11 +24750,11 @@ function TodosRoute() {
24670
24750
  }
24671
24751
  };
24672
24752
 
24673
- const handleToggleTodo = (id: number, completed: boolean) => {
24753
+ const handleToggleTodo = (id: TodoId, completed: boolean) => {
24674
24754
  toggleMutation.mutate({ id, completed: !completed });
24675
24755
  };
24676
24756
 
24677
- const handleDeleteTodo = (id: number) => {
24757
+ const handleDeleteTodo = (id: TodoId) => {
24678
24758
  deleteMutation.mutate({ id });
24679
24759
  };
24680
24760
  {{/if}}
@@ -24845,6 +24925,10 @@ import { orpc } from "@/utils/orpc";
24845
24925
  import { useMutation, useQuery } from "@tanstack/react-query";
24846
24926
  {{/if}}
24847
24927
 
24928
+ {{#unless (eq backend "convex")}}
24929
+ type TodoId = {{#if (or (eq orm "mongoose") (eq database "mongodb"))}}string{{else}}number{{/if}};
24930
+ {{/unless}}
24931
+
24848
24932
  export const Route = createFileRoute("/todos")({
24849
24933
  component: TodosRoute,
24850
24934
  });
@@ -24946,11 +25030,11 @@ function TodosRoute() {
24946
25030
  }
24947
25031
  };
24948
25032
 
24949
- const handleToggleTodo = (id: number, completed: boolean) => {
25033
+ const handleToggleTodo = (id: TodoId, completed: boolean) => {
24950
25034
  toggleMutation.mutate({ id, completed: !completed });
24951
25035
  };
24952
25036
 
24953
- const handleDeleteTodo = (id: number) => {
25037
+ const handleDeleteTodo = (id: TodoId) => {
24954
25038
  deleteMutation.mutate({ id });
24955
25039
  };
24956
25040
  {{/if}}
@@ -25541,11 +25625,15 @@ shamefully-hoist=true
25541
25625
  strict-peer-dependencies=false
25542
25626
  {{/if}}`],
25543
25627
  ["extras/bunfig.toml.hbs", `[install]
25544
- {{#if (or (includes frontend "nuxt"))}}
25545
- linker = "hoisted" # having issues with Nuxt when linker is isolated
25628
+ {{#if (includes frontend "nuxt")}}
25629
+ linker = "hoisted" # Nuxt needs hoisting for its dependency resolver
25546
25630
  {{else}}
25547
25631
  linker = "isolated"
25548
- {{/if}}`],
25632
+ {{#if (or (includes frontend "native-bare") (includes frontend "native-uniwind") (includes frontend "native-unistyles"))}}
25633
+ peer = false # Expo native projects declare SDK peers explicitly; this keeps Bun isolated installs deduped for native modules
25634
+ {{/if}}
25635
+ {{/if}}
25636
+ `],
25549
25637
  ["extras/env.d.ts.hbs", `{{#if (eq serverDeploy "cloudflare")}}
25550
25638
  import { type server } from "@{{projectName}}/infra/alchemy.run";
25551
25639
  {{else}}
@@ -25567,9 +25655,40 @@ declare module "cloudflare:workers" {
25567
25655
  }
25568
25656
  }
25569
25657
  `],
25570
- ["extras/pnpm-workspace.yaml", `packages:
25658
+ ["extras/pnpm-workspace.yaml.hbs", `packages:
25571
25659
  - "apps/*"
25572
25660
  - "packages/*"
25661
+ {{#if (or (eq runtime "node") (eq webDeploy "cloudflare") (eq serverDeploy "cloudflare") (eq orm "prisma") (includes addons "lefthook") (includes addons "nx") (includes addons "pwa") (includes frontend "tanstack-router") (includes frontend "react-router") (includes frontend "tanstack-start") (includes frontend "next") (includes frontend "native-bare") (includes frontend "native-uniwind") (includes frontend "native-unistyles"))}}
25662
+
25663
+ # pnpm 11 blocks dependency lifecycle scripts unless they are approved here.
25664
+ # Entries are scoped to packages this generated stack can pull in.
25665
+ allowBuilds:
25666
+ {{#if (or (eq runtime "node") (eq webDeploy "cloudflare") (eq serverDeploy "cloudflare") (includes frontend "tanstack-start"))}}
25667
+ esbuild: true
25668
+ {{/if}}
25669
+ {{#if (or (includes frontend "tanstack-router") (includes frontend "react-router") (includes frontend "tanstack-start") (includes frontend "next"))}}
25670
+ msw: true
25671
+ {{/if}}
25672
+ {{#if (or (includes frontend "native-bare") (includes frontend "native-uniwind") (includes frontend "native-unistyles"))}}
25673
+ msgpackr-extract: true
25674
+ {{/if}}
25675
+ {{#if (or (eq webDeploy "cloudflare") (eq serverDeploy "cloudflare") (includes addons "pwa"))}}
25676
+ sharp: true
25677
+ {{/if}}
25678
+ {{#if (or (eq webDeploy "cloudflare") (eq serverDeploy "cloudflare"))}}
25679
+ workerd: true
25680
+ {{/if}}
25681
+ {{#if (eq orm "prisma")}}
25682
+ "@prisma/engines": true
25683
+ prisma: true
25684
+ {{/if}}
25685
+ {{#if (includes addons "lefthook")}}
25686
+ lefthook: true
25687
+ {{/if}}
25688
+ {{#if (includes addons "nx")}}
25689
+ nx: true
25690
+ {{/if}}
25691
+ {{/if}}
25573
25692
  `],
25574
25693
  ["frontend/astro/_gitignore", `# build output
25575
25694
  dist/
@@ -25938,12 +26057,7 @@ import { env } from "@{{projectName}}/env/native";
25938
26057
  {{/if}}
25939
26058
 
25940
26059
  import { Stack } from "expo-router";
25941
- import {
25942
- DarkTheme,
25943
- DefaultTheme,
25944
- type Theme,
25945
- ThemeProvider,
25946
- } from "@react-navigation/native";
26060
+ import { DarkTheme, DefaultTheme, ThemeProvider } from "expo-router/react-navigation";
25947
26061
  import { StatusBar } from "expo-status-bar";
25948
26062
  import { GestureHandlerRootView } from "react-native-gesture-handler";
25949
26063
  {{#if (eq api "trpc")}}
@@ -25956,11 +26070,11 @@ import { NAV_THEME } from "@/lib/constants";
25956
26070
  import { useColorScheme } from "@/lib/use-color-scheme";
25957
26071
  import { StyleSheet } from "react-native";
25958
26072
 
25959
- const LIGHT_THEME: Theme = {
26073
+ const LIGHT_THEME = {
25960
26074
  ...DefaultTheme,
25961
26075
  colors: NAV_THEME.light,
25962
26076
  };
25963
- const DARK_THEME: Theme = {
26077
+ const DARK_THEME = {
25964
26078
  ...DarkTheme,
25965
26079
  colors: NAV_THEME.dark,
25966
26080
  };
@@ -25971,9 +26085,6 @@ export const unstable_settings = {
25971
26085
 
25972
26086
  {{#if (eq backend "convex")}}
25973
26087
  const convex = new ConvexReactClient(env.EXPO_PUBLIC_CONVEX_URL, {
25974
- {{#if (eq auth "better-auth")}}
25975
- expectAuth: true,
25976
- {{/if}}
25977
26088
  unsavedChangesWarning: false,
25978
26089
  });
25979
26090
  {{/if}}
@@ -26240,7 +26351,8 @@ export default function TabLayout() {
26240
26351
 
26241
26352
  `],
26242
26353
  ["frontend/native/bare/app/(drawer)/(tabs)/index.tsx.hbs", `import { Container } from "@/components/container";
26243
- import { ScrollView, Text, View, StyleSheet } from "react-native";
26354
+ import { Column, Host, Text as ExpoUIText } from "@expo/ui";
26355
+ import { ScrollView, View, StyleSheet } from "react-native";
26244
26356
  import { useColorScheme } from "@/lib/use-color-scheme";
26245
26357
  import { NAV_THEME } from "@/lib/constants";
26246
26358
 
@@ -26252,12 +26364,21 @@ export default function TabOne() {
26252
26364
  <Container>
26253
26365
  <ScrollView style={styles.scrollView}>
26254
26366
  <View style={styles.content}>
26255
- <Text style={[styles.title, { color: theme.text }]}>
26256
- Tab One
26257
- </Text>
26258
- <Text style={[styles.subtitle, { color: theme.text, opacity: 0.7 }]}>
26259
- Explore the first section of your app
26260
- </Text>
26367
+ <Host matchContents=\\{{ vertical: true }}>
26368
+ <Column spacing={8}>
26369
+ <ExpoUIText
26370
+ textStyle=\\{{ color: theme.text, fontSize: 24, fontWeight: "bold" }}
26371
+ >
26372
+ Tab One
26373
+ </ExpoUIText>
26374
+ <ExpoUIText
26375
+ textStyle=\\{{ color: theme.text, fontSize: 16 }}
26376
+ style=\\{{ opacity: 0.7 }}
26377
+ >
26378
+ Explore the first section of your app
26379
+ </ExpoUIText>
26380
+ </Column>
26381
+ </Host>
26261
26382
  </View>
26262
26383
  </ScrollView>
26263
26384
  </Container>
@@ -26272,19 +26393,11 @@ const styles = StyleSheet.create({
26272
26393
  content: {
26273
26394
  paddingVertical: 16,
26274
26395
  },
26275
- title: {
26276
- fontSize: 24,
26277
- fontWeight: "bold",
26278
- marginBottom: 8,
26279
- },
26280
- subtitle: {
26281
- fontSize: 16,
26282
- },
26283
26396
  });
26284
-
26285
26397
  `],
26286
26398
  ["frontend/native/bare/app/(drawer)/(tabs)/two.tsx.hbs", `import { Container } from "@/components/container";
26287
- import { ScrollView, Text, View, StyleSheet } from "react-native";
26399
+ import { Column, Host, Text as ExpoUIText } from "@expo/ui";
26400
+ import { ScrollView, View, StyleSheet } from "react-native";
26288
26401
  import { useColorScheme } from "@/lib/use-color-scheme";
26289
26402
  import { NAV_THEME } from "@/lib/constants";
26290
26403
 
@@ -26296,12 +26409,21 @@ export default function TabTwo() {
26296
26409
  <Container>
26297
26410
  <ScrollView style={styles.scrollView}>
26298
26411
  <View style={styles.content}>
26299
- <Text style={[styles.title, { color: theme.text }]}>
26300
- Tab Two
26301
- </Text>
26302
- <Text style={[styles.subtitle, { color: theme.text, opacity: 0.7 }]}>
26303
- Discover more features and content
26304
- </Text>
26412
+ <Host matchContents=\\{{ vertical: true }}>
26413
+ <Column spacing={8}>
26414
+ <ExpoUIText
26415
+ textStyle=\\{{ color: theme.text, fontSize: 24, fontWeight: "bold" }}
26416
+ >
26417
+ Tab Two
26418
+ </ExpoUIText>
26419
+ <ExpoUIText
26420
+ textStyle=\\{{ color: theme.text, fontSize: 16 }}
26421
+ style=\\{{ opacity: 0.7 }}
26422
+ >
26423
+ Discover more features and content
26424
+ </ExpoUIText>
26425
+ </Column>
26426
+ </Host>
26305
26427
  </View>
26306
26428
  </ScrollView>
26307
26429
  </Container>
@@ -26316,18 +26438,10 @@ const styles = StyleSheet.create({
26316
26438
  content: {
26317
26439
  paddingVertical: 16,
26318
26440
  },
26319
- title: {
26320
- fontSize: 24,
26321
- fontWeight: "bold",
26322
- marginBottom: 8,
26323
- },
26324
- subtitle: {
26325
- fontSize: 16,
26326
- },
26327
26441
  });
26328
-
26329
26442
  `],
26330
- ["frontend/native/bare/app/(drawer)/index.tsx.hbs", `import { View, Text, ScrollView, TouchableOpacity, StyleSheet } from "react-native";
26443
+ ["frontend/native/bare/app/(drawer)/index.tsx.hbs", `import { {{#if (or (eq auth "clerk") (eq auth "better-auth"))}}Button, {{/if}}Column, Host, Text as ExpoUIText } from "@expo/ui";
26444
+ import { View, ScrollView, StyleSheet } from "react-native";
26331
26445
  import { Container } from "@/components/container";
26332
26446
  import { useColorScheme } from "@/lib/use-color-scheme";
26333
26447
  import { NAV_THEME } from "@/lib/constants";
@@ -26340,13 +26454,13 @@ import { useQuery } from "@tanstack/react-query";
26340
26454
  import { trpc } from "@/utils/trpc";
26341
26455
  {{/if}}
26342
26456
  {{#if (and (eq backend "convex") (eq auth "clerk"))}}
26343
- import { Link } from "expo-router";
26457
+ import { router } from "expo-router";
26344
26458
  import { Authenticated, AuthLoading, Unauthenticated, useQuery } from "convex/react";
26345
26459
  import { api } from "@{{ projectName }}/backend/convex/_generated/api";
26346
26460
  import { useUser } from "@clerk/expo";
26347
26461
  import { SignOutButton } from "@/components/sign-out-button";
26348
26462
  {{else if (and (ne backend "convex") (eq auth "clerk"))}}
26349
- import { Link } from "expo-router";
26463
+ import { router } from "expo-router";
26350
26464
  import { useAuth, useUser } from "@clerk/expo";
26351
26465
  import { SignOutButton } from "@/components/sign-out-button";
26352
26466
  {{else if (and (eq backend "convex") (eq auth "better-auth"))}}
@@ -26388,9 +26502,15 @@ return (
26388
26502
  <Container>
26389
26503
  <ScrollView style={styles.scrollView}>
26390
26504
  <View style={styles.content}>
26391
- <Text style={[styles.title, { color: theme.text }]}>
26392
- BETTER T STACK
26393
- </Text>
26505
+ <Host style={styles.titleHost} matchContents=\\{{ vertical: true }}>
26506
+ <Column>
26507
+ <ExpoUIText
26508
+ textStyle=\\{{ color: theme.text, fontSize: 24, fontWeight: "bold" }}
26509
+ >
26510
+ BETTER T STACK
26511
+ </ExpoUIText>
26512
+ </Column>
26513
+ </Host>
26394
26514
 
26395
26515
  {{#unless (and (eq backend "convex") (eq auth "better-auth"))}}
26396
26516
  <View style={[styles.card, { backgroundColor: theme.card, borderColor: theme.border }]}>
@@ -26398,16 +26518,25 @@ return (
26398
26518
  <View style={styles.statusRow}>
26399
26519
  <View style={[styles.statusIndicator, { backgroundColor: healthCheck ? "#10b981" : "#f59e0b" }]} />
26400
26520
  <View style={styles.statusContent}>
26401
- <Text style={[styles.statusTitle, { color: theme.text }]}>
26402
- Convex
26403
- </Text>
26404
- <Text style={[styles.statusText, { color: theme.text, opacity: 0.7 }]}>
26405
- {healthCheck === undefined
26406
- ? "Checking..."
26407
- : healthCheck === "OK"
26408
- ? "Connected to API"
26409
- : "API Disconnected"}
26410
- </Text>
26521
+ <Host matchContents=\\{{ vertical: true }}>
26522
+ <Column spacing={4}>
26523
+ <ExpoUIText
26524
+ textStyle=\\{{ color: theme.text, fontSize: 14, fontWeight: "bold" }}
26525
+ >
26526
+ Convex
26527
+ </ExpoUIText>
26528
+ <ExpoUIText
26529
+ textStyle=\\{{ color: theme.text, fontSize: 12 }}
26530
+ style=\\{{ opacity: 0.7 }}
26531
+ >
26532
+ {healthCheck === undefined
26533
+ ? "Checking..."
26534
+ : healthCheck === "OK"
26535
+ ? "Connected to API"
26536
+ : "API Disconnected"}
26537
+ </ExpoUIText>
26538
+ </Column>
26539
+ </Host>
26411
26540
  </View>
26412
26541
  </View>
26413
26542
  {{else}}
@@ -26415,16 +26544,25 @@ return (
26415
26544
  <View style={styles.statusRow}>
26416
26545
  <View style={[styles.statusIndicator, { backgroundColor: healthCheck.data ? "#10b981" : "#f59e0b" }]} />
26417
26546
  <View style={styles.statusContent}>
26418
- <Text style={[styles.statusTitle, { color: theme.text }]}>
26419
- {{#if (eq api "orpc")}}ORPC{{else}}TRPC{{/if}}
26420
- </Text>
26421
- <Text style={[styles.statusText, { color: theme.text, opacity: 0.7 }]}>
26422
- {healthCheck.isLoading
26423
- ? "Checking connection..."
26424
- : healthCheck.data
26425
- ? "All systems operational"
26426
- : "Service unavailable"}
26427
- </Text>
26547
+ <Host matchContents=\\{{ vertical: true }}>
26548
+ <Column spacing={4}>
26549
+ <ExpoUIText
26550
+ textStyle=\\{{ color: theme.text, fontSize: 14, fontWeight: "bold" }}
26551
+ >
26552
+ {{#if (eq api "orpc")}}ORPC{{else}}TRPC{{/if}}
26553
+ </ExpoUIText>
26554
+ <ExpoUIText
26555
+ textStyle=\\{{ color: theme.text, fontSize: 12 }}
26556
+ style=\\{{ opacity: 0.7 }}
26557
+ >
26558
+ {healthCheck.isLoading
26559
+ ? "Checking connection..."
26560
+ : healthCheck.data
26561
+ ? "All systems operational"
26562
+ : "Service unavailable"}
26563
+ </ExpoUIText>
26564
+ </Column>
26565
+ </Host>
26428
26566
  </View>
26429
26567
  </View>
26430
26568
  {{/unless}}
@@ -26434,85 +26572,139 @@ return (
26434
26572
 
26435
26573
  {{#if (and (eq backend "convex") (eq auth "clerk"))}}
26436
26574
  <Authenticated>
26437
- <Text style=\\{{ color: theme.text }}>Hello {user?.emailAddresses[0].emailAddress}</Text>
26438
- <Text style=\\{{ color: theme.text }}>Private Data: {privateData?.message}</Text>
26575
+ <Host style={styles.authHost} matchContents=\\{{ vertical: true }}>
26576
+ <Column spacing={6}>
26577
+ <ExpoUIText textStyle=\\{{ color: theme.text, fontSize: 14 }}>
26578
+ {\`Hello \${user?.emailAddresses[0].emailAddress ?? ""}\`}
26579
+ </ExpoUIText>
26580
+ <ExpoUIText textStyle=\\{{ color: theme.text, fontSize: 14 }}>
26581
+ {\`Private Data: \${privateData?.message ?? ""}\`}
26582
+ </ExpoUIText>
26583
+ </Column>
26584
+ </Host>
26439
26585
  <SignOutButton />
26440
26586
  </Authenticated>
26441
26587
  <Unauthenticated>
26442
- <Link href="/(auth)/sign-in">
26443
- <Text style=\\{{ color: theme.primary }}>Sign in</Text>
26444
- </Link>
26445
- <Link href="/(auth)/sign-up">
26446
- <Text style=\\{{ color: theme.primary }}>Sign up</Text>
26447
- </Link>
26588
+ <Host style={styles.authActionsHost} matchContents=\\{{ vertical: true }}>
26589
+ <Column spacing={8}>
26590
+ <Button
26591
+ label="Sign in"
26592
+ variant="outlined"
26593
+ onPress={() => router.push("/(auth)/sign-in")}
26594
+ />
26595
+ <Button
26596
+ label="Sign up"
26597
+ onPress={() => router.push("/(auth)/sign-up")}
26598
+ />
26599
+ </Column>
26600
+ </Host>
26448
26601
  </Unauthenticated>
26449
26602
  <AuthLoading>
26450
- <Text style=\\{{ color: theme.text }}>Loading...</Text>
26603
+ <Host matchContents=\\{{ vertical: true }}>
26604
+ <ExpoUIText textStyle=\\{{ color: theme.text, fontSize: 14 }}>
26605
+ Loading...
26606
+ </ExpoUIText>
26607
+ </Host>
26451
26608
  </AuthLoading>
26452
26609
  {{/if}}
26453
26610
 
26454
26611
  {{#if (and (ne backend "convex") (eq auth "clerk"))}}
26455
26612
  {!isLoaded ? (
26456
- <Text style=\\{{ color: theme.text }}>Loading...</Text>
26613
+ <Host matchContents=\\{{ vertical: true }}>
26614
+ <ExpoUIText textStyle=\\{{ color: theme.text, fontSize: 14 }}>
26615
+ Loading...
26616
+ </ExpoUIText>
26617
+ </Host>
26457
26618
  ) : isSignedIn ? (
26458
26619
  <View style={[styles.userCard, { backgroundColor: theme.card, borderColor: theme.border }]}>
26459
- <View style={styles.userHeader}>
26460
- <Text style={[styles.userText, { color: theme.text }]}>
26461
- Welcome, <Text style={styles.userName}>{user?.fullName ?? user?.firstName ?? "there"}</Text>
26462
- </Text>
26463
- </View>
26464
- <Text style={[styles.userEmail, { color: theme.text, opacity: 0.7 }]}>
26465
- {user?.emailAddresses[0]?.emailAddress}
26466
- </Text>
26620
+ <Host style={styles.userHeader} matchContents=\\{{ vertical: true }}>
26621
+ <Column spacing={8}>
26622
+ <ExpoUIText textStyle=\\{{ color: theme.text, fontSize: 16 }}>
26623
+ {\`Welcome, \${user?.fullName ?? user?.firstName ?? "there"}\`}
26624
+ </ExpoUIText>
26625
+ <ExpoUIText
26626
+ textStyle=\\{{ color: theme.text, fontSize: 14 }}
26627
+ style=\\{{ opacity: 0.7 }}
26628
+ >
26629
+ {user?.emailAddresses[0]?.emailAddress ?? ""}
26630
+ </ExpoUIText>
26631
+ </Column>
26632
+ </Host>
26467
26633
  <SignOutButton />
26468
26634
  </View>
26469
26635
  ) : (
26470
26636
  <View style={[styles.card, { backgroundColor: theme.card, borderColor: theme.border }]}>
26471
- <Link href="/(auth)/sign-in">
26472
- <Text style=\\{{ color: theme.primary }}>Sign in</Text>
26473
- </Link>
26474
- <Link href="/(auth)/sign-up">
26475
- <Text style=\\{{ color: theme.primary }}>Sign up</Text>
26476
- </Link>
26637
+ <Host style={styles.authActionsHost} matchContents=\\{{ vertical: true }}>
26638
+ <Column spacing={8}>
26639
+ <Button
26640
+ label="Sign in"
26641
+ variant="outlined"
26642
+ onPress={() => router.push("/(auth)/sign-in")}
26643
+ />
26644
+ <Button
26645
+ label="Sign up"
26646
+ onPress={() => router.push("/(auth)/sign-up")}
26647
+ />
26648
+ </Column>
26649
+ </Host>
26477
26650
  </View>
26478
26651
  )}
26479
26652
  {{/if}}
26480
26653
 
26481
26654
  {{#if (and (eq backend "convex") (eq auth "better-auth"))}}
26482
- {user ? (
26483
- <View style={[styles.userCard, { backgroundColor: theme.card, borderColor: theme.border }]}>
26484
- <View style={styles.userHeader}>
26485
- <Text style={[styles.userText, { color: theme.text }]}>
26486
- Welcome, <Text style={styles.userName}>{user.name}</Text>
26487
- </Text>
26488
- </View>
26489
- <Text style={[styles.userEmail, { color: theme.text, opacity: 0.7 }]}>
26490
- {user.email}
26491
- </Text>
26492
- <TouchableOpacity style={[styles.signOutButton, { backgroundColor: theme.notification }]} onPress={()=> {
26493
- authClient.signOut();
26494
- }}
26495
- >
26496
- <Text style={styles.signOutText}>Sign Out</Text>
26497
- </TouchableOpacity>
26498
- </View>
26499
- ) : null}
26500
26655
  <View style={[styles.statusCard, { backgroundColor: theme.card, borderColor: theme.border }]}>
26501
- <Text style={[styles.statusCardTitle, { color: theme.text }]}>
26502
- API Status
26503
- </Text>
26656
+ <Host style={styles.statusCardTitleHost} matchContents=\\{{ vertical: true }}>
26657
+ <ExpoUIText
26658
+ textStyle=\\{{ color: theme.text, fontSize: 16, fontWeight: "bold" }}
26659
+ >
26660
+ API Status
26661
+ </ExpoUIText>
26662
+ </Host>
26504
26663
  <View style={styles.statusRow}>
26505
- <View style={[styles.statusIndicator, { backgroundColor: healthCheck ? "#10b981" : "#ef4444" }]} />
26506
- <Text style={[styles.statusText, { color: theme.text, opacity: 0.7 }]}>
26507
- {healthCheck === undefined
26508
- ? "Checking..."
26509
- : healthCheck === "OK"
26510
- ? "Connected to API"
26511
- : "API Disconnected"}
26512
- </Text>
26664
+ <View style={[styles.statusIndicator, { backgroundColor: healthCheck ? "#10b981" : "#f59e0b" }]} />
26665
+ <View style={styles.statusContent}>
26666
+ <Host matchContents=\\{{ vertical: true }}>
26667
+ <ExpoUIText
26668
+ textStyle=\\{{ color: theme.text, fontSize: 12 }}
26669
+ style=\\{{ opacity: 0.7 }}
26670
+ >
26671
+ {healthCheck === undefined
26672
+ ? "Checking..."
26673
+ : healthCheck === "OK"
26674
+ ? "Connected to API"
26675
+ : "API Disconnected"}
26676
+ </ExpoUIText>
26677
+ </Host>
26678
+ </View>
26513
26679
  </View>
26514
26680
  </View>
26515
- {!user && (
26681
+
26682
+ {user ? (
26683
+ <View style={[styles.userCard, { backgroundColor: theme.card, borderColor: theme.border }]}>
26684
+ <Host style={styles.userHeader} matchContents>
26685
+ <Column spacing={6}>
26686
+ <ExpoUIText textStyle=\\{{ color: theme.text, fontSize: 16, fontWeight: "bold" }}>
26687
+ {\`Welcome, \${user.name}\`}
26688
+ </ExpoUIText>
26689
+ <ExpoUIText
26690
+ textStyle=\\{{ color: theme.text, fontSize: 14 }}
26691
+ style=\\{{ opacity: 0.7 }}
26692
+ >
26693
+ {user.email}
26694
+ </ExpoUIText>
26695
+ </Column>
26696
+ </Host>
26697
+ <Host matchContents=\\{{ vertical: true }}>
26698
+ <Button
26699
+ label="Sign Out"
26700
+ variant="outlined"
26701
+ onPress={() => {
26702
+ authClient.signOut();
26703
+ }}
26704
+ />
26705
+ </Host>
26706
+ </View>
26707
+ ) : (
26516
26708
  <>
26517
26709
  <SignIn />
26518
26710
  <SignUp />
@@ -26530,12 +26722,13 @@ scrollView: {
26530
26722
  flex: 1,
26531
26723
  },
26532
26724
  content: {
26533
- padding: 16,
26725
+ paddingHorizontal: 20,
26726
+ paddingTop: 28,
26727
+ paddingBottom: 32,
26534
26728
  },
26535
- title: {
26536
- fontSize: 24,
26537
- fontWeight: "bold",
26538
- marginBottom: 16,
26729
+ titleHost: {
26730
+ alignSelf: "center",
26731
+ marginBottom: 24,
26539
26732
  },
26540
26733
  card: {
26541
26734
  padding: 16,
@@ -26548,56 +26741,42 @@ alignItems: "center",
26548
26741
  gap: 8,
26549
26742
  },
26550
26743
  statusIndicator: {
26551
- height: 8,
26552
- width: 8,
26744
+ height: 10,
26745
+ width: 10,
26746
+ borderRadius: 999,
26553
26747
  },
26554
26748
  statusContent: {
26555
26749
  flex: 1,
26556
26750
  },
26557
- statusTitle: {
26558
- fontSize: 14,
26559
- fontWeight: "bold",
26560
- },
26561
- statusText: {
26562
- fontSize: 12,
26563
- },
26564
26751
  userCard: {
26565
26752
  marginBottom: 16,
26566
26753
  padding: 16,
26567
26754
  borderWidth: 1,
26755
+ borderRadius: 16,
26568
26756
  },
26569
26757
  userHeader: {
26570
26758
  marginBottom: 8,
26571
26759
  },
26572
- userText: {
26573
- fontSize: 16,
26574
- },
26575
- userName: {
26576
- fontWeight: "bold",
26577
- },
26578
- userEmail: {
26579
- fontSize: 14,
26760
+ authHost: {
26580
26761
  marginBottom: 12,
26581
26762
  },
26582
- signOutButton: {
26583
- padding: 12,
26584
- },
26585
- signOutText: {
26586
- color: "#ffffff",
26763
+ authActionsHost: {
26764
+ marginTop: 4,
26587
26765
  },
26588
26766
  statusCard: {
26589
26767
  marginBottom: 16,
26590
26768
  padding: 16,
26591
26769
  borderWidth: 1,
26770
+ borderRadius: 16,
26592
26771
  },
26593
- statusCardTitle: {
26772
+ statusCardTitleHost: {
26594
26773
  marginBottom: 8,
26595
- fontWeight: "bold",
26596
26774
  },
26597
26775
  });
26598
26776
  `],
26599
26777
  ["frontend/native/bare/app/+not-found.tsx.hbs", `import { Container } from "@/components/container";
26600
- import { Link, Stack } from "expo-router";
26778
+ import { Button, Column, Host, Text as ExpoUIText } from "@expo/ui";
26779
+ import { Stack, router } from "expo-router";
26601
26780
  import { Text, View, StyleSheet } from "react-native";
26602
26781
  import { useColorScheme } from "@/lib/use-color-scheme";
26603
26782
  import { NAV_THEME } from "@/lib/constants";
@@ -26613,17 +26792,26 @@ export default function NotFoundScreen() {
26613
26792
  <View style={styles.container}>
26614
26793
  <View style={styles.content}>
26615
26794
  <Text style={styles.emoji}>🤔</Text>
26616
- <Text style={[styles.title, { color: theme.text }]}>
26617
- Page Not Found
26618
- </Text>
26619
- <Text style={[styles.subtitle, { color: theme.text, opacity: 0.7 }]}>
26620
- Sorry, the page you're looking for doesn't exist.
26621
- </Text>
26622
- <Link href="/" asChild>
26623
- <Text style={[styles.link, { color: theme.primary, backgroundColor: \`\${theme.primary}1a\` }]}>
26624
- Go to Home
26625
- </Text>
26626
- </Link>
26795
+ <Host matchContents=\\{{ vertical: true }}>
26796
+ <Column spacing={12} alignment="center">
26797
+ <ExpoUIText
26798
+ textStyle=\\{{ color: theme.text, fontSize: 20, fontWeight: "bold", textAlign: "center" }}
26799
+ >
26800
+ Page Not Found
26801
+ </ExpoUIText>
26802
+ <ExpoUIText
26803
+ textStyle=\\{{ color: theme.text, fontSize: 14, textAlign: "center" }}
26804
+ style=\\{{ opacity: 0.7 }}
26805
+ >
26806
+ Sorry, the page you're looking for doesn't exist.
26807
+ </ExpoUIText>
26808
+ <Button
26809
+ label="Go to Home"
26810
+ variant="outlined"
26811
+ onPress={() => router.replace("/")}
26812
+ />
26813
+ </Column>
26814
+ </Host>
26627
26815
  </View>
26628
26816
  </View>
26629
26817
  </Container>
@@ -26645,25 +26833,11 @@ const styles = StyleSheet.create({
26645
26833
  fontSize: 48,
26646
26834
  marginBottom: 16,
26647
26835
  },
26648
- title: {
26649
- fontSize: 20,
26650
- fontWeight: "bold",
26651
- marginBottom: 8,
26652
- textAlign: "center",
26653
- },
26654
- subtitle: {
26655
- fontSize: 14,
26656
- textAlign: "center",
26657
- marginBottom: 24,
26658
- },
26659
- link: {
26660
- padding: 12,
26661
- },
26662
26836
  });
26663
-
26664
26837
  `],
26665
26838
  ["frontend/native/bare/app/modal.tsx.hbs", `import { Container } from "@/components/container";
26666
- import { Text, View, StyleSheet } from "react-native";
26839
+ import { Button, Column, Host, Text as ExpoUIText } from "@expo/ui";
26840
+ import { View, StyleSheet } from "react-native";
26667
26841
  import { useColorScheme } from "@/lib/use-color-scheme";
26668
26842
  import { NAV_THEME } from "@/lib/constants";
26669
26843
 
@@ -26674,9 +26848,22 @@ export default function Modal() {
26674
26848
  return (
26675
26849
  <Container>
26676
26850
  <View style={styles.container}>
26677
- <View style={styles.header}>
26678
- <Text style={[styles.title, { color: theme.text }]}>Modal</Text>
26679
- </View>
26851
+ <Host style={styles.expoUiHost}>
26852
+ <Column spacing={12} alignment="center">
26853
+ <ExpoUIText
26854
+ textStyle=\\{{ color: theme.text, fontSize: 20, fontWeight: "bold" }}
26855
+ >
26856
+ Modal
26857
+ </ExpoUIText>
26858
+ <ExpoUIText
26859
+ textStyle=\\{{ color: theme.text, fontSize: 14, textAlign: "center" }}
26860
+ style=\\{{ opacity: 0.7 }}
26861
+ >
26862
+ Built with Expo UI universal components
26863
+ </ExpoUIText>
26864
+ <Button label="Native control" onPress={() => null} />
26865
+ </Column>
26866
+ </Host>
26680
26867
  </View>
26681
26868
  </Container>
26682
26869
  );
@@ -26687,15 +26874,11 @@ const styles = StyleSheet.create({
26687
26874
  flex: 1,
26688
26875
  padding: 16,
26689
26876
  },
26690
- header: {
26691
- marginBottom: 16,
26692
- },
26693
- title: {
26694
- fontSize: 20,
26695
- fontWeight: "bold",
26877
+ expoUiHost: {
26878
+ alignSelf: "stretch",
26879
+ padding: 16,
26696
26880
  },
26697
26881
  });
26698
-
26699
26882
  `],
26700
26883
  ["frontend/native/bare/components/container.tsx.hbs", `import React from "react";
26701
26884
  import { SafeAreaView } from "react-native-safe-area-context";
@@ -26773,13 +26956,14 @@ const styles = StyleSheet.create({
26773
26956
  `],
26774
26957
  ["frontend/native/bare/components/tabbar-icon.tsx.hbs", `import FontAwesome from "@expo/vector-icons/FontAwesome";
26775
26958
 
26959
+ type FontAwesomeProps = React.ComponentProps<typeof FontAwesome>;
26960
+
26776
26961
  export const TabBarIcon = (props: {
26777
- name: React.ComponentProps<typeof FontAwesome>["name"];
26778
- color: string;
26962
+ name: FontAwesomeProps["name"];
26963
+ color: FontAwesomeProps["color"];
26779
26964
  }) => {
26780
26965
  return <FontAwesome size={24} style=\\{{ marginBottom: -3 }} {...props} />;
26781
26966
  };
26782
-
26783
26967
  `],
26784
26968
  ["frontend/native/bare/lib/constants.ts.hbs", `export const NAV_THEME = {
26785
26969
  light: {
@@ -26841,41 +27025,39 @@ module.exports = config;
26841
27025
  "web": "expo start --web"
26842
27026
  },
26843
27027
  "dependencies": {
27028
+ "@expo/ui": "~56.0.12",
26844
27029
  "@expo/vector-icons": "^15.1.1",
26845
- "@react-navigation/bottom-tabs": "^7.15.9",
26846
- "@react-navigation/drawer": "^7.9.4",
26847
- "@react-navigation/native": "^7.2.2",
26848
27030
  "@tanstack/react-query": "^5.99.2",
26849
27031
  {{#if (includes examples "ai")}}
26850
27032
  "@stardazed/streams-text-encoding": "^1.0.2",
26851
27033
  "@ungap/structured-clone": "^1.3.0",
26852
27034
  {{/if}}
26853
- "expo": "^55.0.17",
26854
- "expo-constants": "~55.0.15",
26855
- "expo-crypto": "~55.0.14",
26856
- "expo-font": "~55.0.6",
26857
- "expo-linking": "~55.0.14",
26858
- "expo-network": "~55.0.13",
26859
- "expo-router": "~55.0.13",
26860
- "expo-secure-store": "~55.0.13",
26861
- "expo-splash-screen": "~55.0.19",
26862
- "expo-status-bar": "~55.0.5",
26863
- "expo-system-ui": "~55.0.16",
26864
- "expo-web-browser": "~55.0.14",
26865
- "react": "19.2.0",
26866
- "react-dom": "19.2.0",
26867
- "react-native": "0.83.6",
26868
- "react-native-gesture-handler": "~2.30.0",
26869
- "react-native-reanimated": "4.2.1",
26870
- "react-native-safe-area-context": "~5.6.2",
26871
- "react-native-screens": "~4.23.0",
27035
+ "expo": "~56.0.3",
27036
+ "expo-constants": "~56.0.14",
27037
+ "expo-crypto": "~56.0.3",
27038
+ "expo-font": "~56.0.5",
27039
+ "expo-linking": "~56.0.11",
27040
+ "expo-network": "~56.0.4",
27041
+ "expo-router": "~56.2.5",
27042
+ "expo-secure-store": "~56.0.4",
27043
+ "expo-splash-screen": "~56.0.9",
27044
+ "expo-status-bar": "~56.0.4",
27045
+ "expo-system-ui": "~56.0.5",
27046
+ "expo-web-browser": "~56.0.5",
27047
+ "react": "19.2.3",
27048
+ "react-dom": "19.2.3",
27049
+ "react-native": "0.85.3",
27050
+ "react-native-gesture-handler": "~2.31.1",
27051
+ "react-native-reanimated": "4.3.1",
27052
+ "react-native-safe-area-context": "~5.7.0",
27053
+ "react-native-screens": "4.25.1",
26872
27054
  "react-native-web": "~0.21.0",
26873
- "react-native-worklets": "0.7.4"
27055
+ "react-native-worklets": "0.8.3"
26874
27056
  },
26875
27057
  "devDependencies": {
26876
- "@babel/core": "^7.28.0",
26877
- "@types/react": "~19.2.10",
26878
- "typescript": "~5.9.2"
27058
+ "@babel/core": "^7.29.0",
27059
+ "@types/react": "~19.2.14",
27060
+ "typescript": "^6"
26879
27061
  },
26880
27062
  "private": true
26881
27063
  }
@@ -27023,9 +27205,6 @@ export const unstable_settings = {
27023
27205
 
27024
27206
  {{#if (eq backend "convex")}}
27025
27207
  const convex = new ConvexReactClient(env.EXPO_PUBLIC_CONVEX_URL, {
27026
- {{#if (eq auth "better-auth")}}
27027
- expectAuth: true,
27028
- {{/if}}
27029
27208
  unsavedChangesWarning: false,
27030
27209
  });
27031
27210
  {{/if}}
@@ -28017,9 +28196,11 @@ const styles = StyleSheet.create((theme) => ({
28017
28196
  `],
28018
28197
  ["frontend/native/unistyles/components/tabbar-icon.tsx.hbs", `import FontAwesome from "@expo/vector-icons/FontAwesome";
28019
28198
 
28199
+ type FontAwesomeProps = React.ComponentProps<typeof FontAwesome>;
28200
+
28020
28201
  export const TabBarIcon = (props: {
28021
- name: React.ComponentProps<typeof FontAwesome>["name"];
28022
- color: string;
28202
+ name: FontAwesomeProps["name"];
28203
+ color: FontAwesomeProps["color"];
28023
28204
  }) => {
28024
28205
  return <FontAwesome size={24} style=\\{{ marginBottom: -3 }} {...props} />;
28025
28206
  };
@@ -28046,45 +28227,42 @@ module.exports = config;
28046
28227
  },
28047
28228
  "dependencies": {
28048
28229
  "@expo/vector-icons": "^15.1.1",
28049
- "@react-navigation/bottom-tabs": "^7.15.9",
28050
- "@react-navigation/drawer": "^7.9.4",
28051
- "@react-navigation/native": "^7.2.2",
28052
28230
  {{#if (includes examples "ai")}}
28053
28231
  "@stardazed/streams-text-encoding": "^1.0.2",
28054
28232
  "@ungap/structured-clone": "^1.3.0",
28055
28233
  {{/if}}
28056
- "babel-preset-expo": "~55.0.18",
28057
- "expo": "^55.0.17",
28058
- "expo-constants": "~55.0.15",
28059
- "expo-crypto": "~55.0.14",
28060
- "expo-dev-client": "~55.0.28",
28061
- "expo-font": "~55.0.6",
28062
- "expo-linking": "~55.0.14",
28063
- "expo-network": "~55.0.13",
28064
- "expo-router": "~55.0.13",
28065
- "expo-secure-store": "~55.0.13",
28066
- "expo-splash-screen": "~55.0.19",
28067
- "expo-status-bar": "~55.0.5",
28068
- "expo-system-ui": "~55.0.16",
28069
- "expo-web-browser": "~55.0.14",
28070
- "react": "19.2.0",
28071
- "react-dom": "19.2.0",
28072
- "react-native": "0.83.6",
28234
+ "babel-preset-expo": "~56.0.0",
28235
+ "expo": "~56.0.3",
28236
+ "expo-constants": "~56.0.14",
28237
+ "expo-crypto": "~56.0.3",
28238
+ "expo-dev-client": "~56.0.14",
28239
+ "expo-font": "~56.0.5",
28240
+ "expo-linking": "~56.0.11",
28241
+ "expo-network": "~56.0.4",
28242
+ "expo-router": "~56.2.5",
28243
+ "expo-secure-store": "~56.0.4",
28244
+ "expo-splash-screen": "~56.0.9",
28245
+ "expo-status-bar": "~56.0.4",
28246
+ "expo-system-ui": "~56.0.5",
28247
+ "expo-web-browser": "~56.0.5",
28248
+ "react": "19.2.3",
28249
+ "react-dom": "19.2.3",
28250
+ "react-native": "0.85.3",
28073
28251
  "react-native-edge-to-edge": "^1.8.1",
28074
- "react-native-gesture-handler": "~2.30.0",
28075
- "react-native-nitro-modules": "^0.35.4",
28076
- "react-native-reanimated": "4.2.1",
28077
- "react-native-safe-area-context": "~5.6.2",
28078
- "react-native-screens": "~4.23.0",
28079
- "react-native-unistyles": "^3.2.3",
28252
+ "react-native-gesture-handler": "~2.31.1",
28253
+ "react-native-nitro-modules": "^0.35.7",
28254
+ "react-native-reanimated": "4.3.1",
28255
+ "react-native-safe-area-context": "~5.7.0",
28256
+ "react-native-screens": "4.25.1",
28257
+ "react-native-unistyles": "^3.2.4",
28080
28258
  "react-native-web": "~0.21.0",
28081
- "react-native-worklets": "0.7.4"
28259
+ "react-native-worklets": "0.8.3"
28082
28260
  },
28083
28261
  "devDependencies": {
28084
- "ajv": "^8.17.1",
28085
- "@babel/core": "^7.28.0",
28086
- "@types/react": "~19.2.10",
28087
- "typescript": "~5.9.2"
28262
+ "ajv": "^8.20.0",
28263
+ "@babel/core": "^7.29.0",
28264
+ "@types/react": "~19.2.14",
28265
+ "typescript": "^6"
28088
28266
  }
28089
28267
  }
28090
28268
  `],
@@ -28194,7 +28372,7 @@ export const darkTheme = {
28194
28372
  "strict": true,
28195
28373
  "jsx": "react-jsx",
28196
28374
  "paths": {
28197
- "@/*": ["*"]
28375
+ "@/*": ["./*"]
28198
28376
  }
28199
28377
  },
28200
28378
  "include": ["**/*.ts", "**/*.tsx", ".expo/types/**/*.ts", "expo-env.d.ts"]
@@ -28326,9 +28504,6 @@ export const unstable_settings = {
28326
28504
 
28327
28505
  {{#if (eq backend "convex")}}
28328
28506
  const convex = new ConvexReactClient(env.EXPO_PUBLIC_CONVEX_URL, {
28329
- {{#if (eq auth "better-auth")}}
28330
- expectAuth: true,
28331
- {{/if}}
28332
28507
  unsavedChangesWarning: false,
28333
28508
  });
28334
28509
  {{/if}}
@@ -28580,7 +28755,7 @@ export default function TabLayout() {
28580
28755
  name="index"
28581
28756
  options=\\{{
28582
28757
  title: "Home",
28583
- tabBarIcon: ({ color, size }: { color: string; size: number }) => (
28758
+ tabBarIcon: ({ color, size }) => (
28584
28759
  <Ionicons name="home" size={size} color={color} />
28585
28760
  ),
28586
28761
  }}
@@ -28589,7 +28764,7 @@ export default function TabLayout() {
28589
28764
  name="two"
28590
28765
  options=\\{{
28591
28766
  title: "Explore",
28592
- tabBarIcon: ({ color, size }: { color: string; size: number }) => (
28767
+ tabBarIcon: ({ color, size }) => (
28593
28768
  <Ionicons name="compass" size={size} color={color} />
28594
28769
  ),
28595
28770
  }}
@@ -29106,46 +29281,44 @@ module.exports = uniwindConfig;
29106
29281
  "web": "expo start --web"
29107
29282
  },
29108
29283
  "dependencies": {
29109
- "@expo/metro-runtime": "~55.0.10",
29284
+ "@expo/metro-runtime": "~56.0.11",
29110
29285
  "@expo/vector-icons": "^15.1.1",
29111
- "@gorhom/bottom-sheet": "^5.2.10",
29112
- "@react-navigation/drawer": "^7.9.4",
29113
- "@react-navigation/elements": "^2.9.14",
29286
+ "@gorhom/bottom-sheet": "^5.2.14",
29114
29287
  {{#if (includes examples "ai")}}
29115
29288
  "@stardazed/streams-text-encoding": "^1.0.2",
29116
29289
  "@ungap/structured-clone": "^1.3.0",
29117
29290
  {{/if}}
29118
- "expo": "^55.0.17",
29119
- "expo-constants": "~55.0.15",
29120
- "expo-font": "~55.0.6",
29121
- "expo-haptics": "~55.0.14",
29122
- "expo-linking": "~55.0.14",
29123
- "expo-network": "~55.0.13",
29124
- "expo-router": "~55.0.13",
29125
- "expo-secure-store": "~55.0.13",
29126
- "expo-status-bar": "~55.0.5",
29127
- "expo-web-browser": "~55.0.14",
29128
- "heroui-native": "^1.0.2",
29129
- "react": "19.2.0",
29130
- "react-dom": "19.2.0",
29131
- "react-native": "0.83.6",
29132
- "react-native-gesture-handler": "~2.30.0",
29133
- "react-native-keyboard-controller": "1.20.7",
29134
- "react-native-reanimated": "4.2.1",
29135
- "react-native-safe-area-context": "~5.6.2",
29136
- "react-native-screens": "~4.23.0",
29137
- "react-native-svg": "15.15.3",
29291
+ "expo": "~56.0.3",
29292
+ "expo-constants": "~56.0.14",
29293
+ "expo-font": "~56.0.5",
29294
+ "expo-haptics": "~56.0.3",
29295
+ "expo-linking": "~56.0.11",
29296
+ "expo-network": "~56.0.4",
29297
+ "expo-router": "~56.2.5",
29298
+ "expo-secure-store": "~56.0.4",
29299
+ "expo-status-bar": "~56.0.4",
29300
+ "expo-web-browser": "~56.0.5",
29301
+ "heroui-native": "^1.0.3",
29302
+ "react": "19.2.3",
29303
+ "react-dom": "19.2.3",
29304
+ "react-native": "0.85.3",
29305
+ "react-native-gesture-handler": "~2.31.1",
29306
+ "react-native-keyboard-controller": "1.21.6",
29307
+ "react-native-reanimated": "4.3.1",
29308
+ "react-native-safe-area-context": "~5.7.0",
29309
+ "react-native-screens": "4.25.1",
29310
+ "react-native-svg": "15.15.4",
29138
29311
  "react-native-web": "~0.21.0",
29139
- "react-native-worklets": "0.7.4",
29140
- "tailwind-merge": "^3.5.0",
29312
+ "react-native-worklets": "0.8.3",
29313
+ "tailwind-merge": "^3.6.0",
29141
29314
  "tailwind-variants": "^3.2.2",
29142
- "tailwindcss": "^4.2.4",
29143
- "uniwind": "^1.6.3"
29315
+ "tailwindcss": "^4.3.0",
29316
+ "uniwind": "^1.7.0"
29144
29317
  },
29145
29318
  "devDependencies": {
29146
- "@types/node": "^24.10.0",
29147
- "@types/react": "~19.2.10",
29148
- "typescript": "~5.9.2"
29319
+ "@types/node": "^25.9.1",
29320
+ "@types/react": "~19.2.14",
29321
+ "typescript": "^6"
29149
29322
  }
29150
29323
  }
29151
29324
  `],
@@ -29165,6 +29338,8 @@ module.exports = uniwindConfig;
29165
29338
  ]
29166
29339
  }`],
29167
29340
  ["frontend/native/uniwind/uniwind-env.d.ts", `/// <reference types="uniwind/types" />
29341
+
29342
+ declare module "*.css";
29168
29343
  `],
29169
29344
  ["frontend/nuxt/_gitignore", `# Nuxt dev/build outputs
29170
29345
  .output
@@ -29505,15 +29680,15 @@ initOpenNextCloudflareForDev();
29505
29680
  "lucide-react": "^0.546.0",
29506
29681
  "next": "^16.2.0",
29507
29682
  "next-themes": "^0.4.6",
29508
- "react": "^19.2.3",
29509
- "react-dom": "^19.2.3",
29683
+ "react": "^19.2.6",
29684
+ "react-dom": "^19.2.6",
29510
29685
  "sonner": "^2.0.5",
29511
29686
  "babel-plugin-react-compiler": "^1.0.0"
29512
29687
  },
29513
29688
  "devDependencies": {
29514
29689
  "@tailwindcss/postcss": "^4.1.18",
29515
29690
  "@types/node": "^20",
29516
- "@types/react": "^19.2.10",
29691
+ "@types/react": "^19.2.15",
29517
29692
  "@types/react-dom": "^19.2.3",
29518
29693
  "tailwindcss": "^4.1.18"
29519
29694
  }
@@ -29910,8 +30085,8 @@ export function ThemeProvider({
29910
30085
  "isbot": "^5.1.39",
29911
30086
  "lucide-react": "^1.8.0",
29912
30087
  "next-themes": "^0.4.6",
29913
- "react": "^19.2.5",
29914
- "react-dom": "^19.2.5",
30088
+ "react": "^19.2.6",
30089
+ "react-dom": "^19.2.6",
29915
30090
  "react-router": "^7.14.1",
29916
30091
  "sonner": "^2.0.7"
29917
30092
  },
@@ -29919,7 +30094,7 @@ export function ThemeProvider({
29919
30094
  "@react-router/dev": "^7.14.1",
29920
30095
  "@tailwindcss/vite": "^4.2.2",
29921
30096
  "@types/node": "^20",
29922
- "@types/react": "^19.2.14",
30097
+ "@types/react": "^19.2.15",
29923
30098
  "@types/react-dom": "^19.2.3",
29924
30099
  "react-router-devtools": "^1.1.0",
29925
30100
  "tailwindcss": "^4.2.2",
@@ -30085,13 +30260,7 @@ export default function App() {
30085
30260
  {{else}}
30086
30261
  export default function App() {
30087
30262
  {{/if}}
30088
- {{#if (eq auth "better-auth")}}
30089
- const convex = new ConvexReactClient(env.VITE_CONVEX_URL, {
30090
- expectAuth: true,
30091
- });
30092
- {{else}}
30093
30263
  const convex = new ConvexReactClient(env.VITE_CONVEX_URL);
30094
- {{/if}}
30095
30264
  {{#if (eq auth "clerk")}}
30096
30265
  return (
30097
30266
  <ClerkProvider loaderData={loaderData}>
@@ -30453,15 +30622,15 @@ export default defineConfig({
30453
30622
  "@tanstack/react-router": "^1.168.22",
30454
30623
  "lucide-react": "^1.8.0",
30455
30624
  "next-themes": "^0.4.6",
30456
- "react": "^19.2.5",
30457
- "react-dom": "^19.2.5",
30625
+ "react": "^19.2.6",
30626
+ "react-dom": "^19.2.6",
30458
30627
  "sonner": "^2.0.7"
30459
30628
  },
30460
30629
  "devDependencies": {
30461
30630
  "@tanstack/react-router-devtools": "^1.166.13",
30462
30631
  "@tanstack/router-plugin": "^1.167.22",
30463
30632
  "@types/node": "^22.13.14",
30464
- "@types/react": "^19.2.14",
30633
+ "@types/react": "^19.2.15",
30465
30634
  "@types/react-dom": "^19.2.3",
30466
30635
  "@vitejs/plugin-react": "^6.0.1",
30467
30636
  "postcss": "^8.5.10",
@@ -30545,13 +30714,7 @@ import { routeTree } from "./routeTree.gen";
30545
30714
  {{else}}
30546
30715
  import { ConvexProvider } from "convex/react";
30547
30716
  {{/if}}
30548
- {{#if (eq auth "better-auth")}}
30549
- const convex = new ConvexReactClient(env.VITE_CONVEX_URL, {
30550
- expectAuth: true,
30551
- });
30552
- {{else}}
30553
30717
  const convex = new ConvexReactClient(env.VITE_CONVEX_URL);
30554
- {{/if}}
30555
30718
  {{/if}}
30556
30719
 
30557
30720
  {{#if (and (eq auth "clerk") (ne backend "convex") (ne api "none"))}}
@@ -30904,8 +31067,8 @@ export default defineConfig({
30904
31067
  "@tanstack/react-start": "^1.167.41",
30905
31068
  "lucide-react": "^1.8.0",
30906
31069
  "next-themes": "^0.4.6",
30907
- "react": "^19.2.5",
30908
- "react-dom": "^19.2.5",
31070
+ "react": "^19.2.6",
31071
+ "react-dom": "^19.2.6",
30909
31072
  "sonner": "^2.0.7",
30910
31073
  "tailwindcss": "^4.2.2"
30911
31074
  },
@@ -30913,7 +31076,7 @@ export default defineConfig({
30913
31076
  "@tanstack/react-router-devtools": "^1.166.13",
30914
31077
  "@testing-library/dom": "^10.4.1",
30915
31078
  "@testing-library/react": "^16.3.2",
30916
- "@types/react": "^19.2.14",
31079
+ "@types/react": "^19.2.15",
30917
31080
  "@types/react-dom": "^19.2.3",
30918
31081
  "@vitejs/plugin-react": "^6.0.1",
30919
31082
  "jsdom": "^29.0.2",
@@ -30933,12 +31096,10 @@ import { setupRouterSsrQueryIntegration } from "@tanstack/react-router-ssr-query
30933
31096
  import { ConvexQueryClient } from "@convex-dev/react-query";
30934
31097
  import { routeTree } from "./routeTree.gen";
30935
31098
  import Loader from "./components/loader";
30936
- import "./index.css";
30937
31099
  import { env } from "@{{projectName}}/env/web";
30938
31100
  {{else}}
30939
31101
  import { createRouter as createTanStackRouter } from "@tanstack/react-router";
30940
31102
  import Loader from "./components/loader";
30941
- import "./index.css";
30942
31103
  import { routeTree } from "./routeTree.gen";
30943
31104
  {{#if (eq api "trpc")}}
30944
31105
  import { QueryCache, QueryClient } from "@tanstack/react-query";
@@ -33106,14 +33267,14 @@ await app.finalize();
33106
33267
  "clsx": "^2.1.1",
33107
33268
  "lucide-react": "^0.546.0",
33108
33269
  "next-themes": "^0.4.6",
33109
- "react": "^19.2.3",
33110
- "react-dom": "^19.2.3",
33270
+ "react": "^19.2.6",
33271
+ "react-dom": "^19.2.6",
33111
33272
  "sonner": "^2.0.5",
33112
33273
  "tailwind-merge": "^3.3.1",
33113
33274
  "tw-animate-css": "^1.3.4"
33114
33275
  },
33115
33276
  "devDependencies": {
33116
- "@types/react": "^19.2.10",
33277
+ "@types/react": "^19.2.15",
33117
33278
  "@types/react-dom": "^19.2.3",
33118
33279
  "tailwindcss": "^4.1.18"
33119
33280
  },
@@ -34004,7 +34165,7 @@ function SuccessPage() {
34004
34165
  </div>
34005
34166
  `]
34006
34167
  ]);
34007
- const TEMPLATE_COUNT = 467;
34168
+ const TEMPLATE_COUNT = 474;
34008
34169
  //#endregion
34009
34170
  export { EMBEDDED_TEMPLATES, GeneratorError, Handlebars, TEMPLATE_COUNT, VirtualFileSystem, dependencyVersionMap, generate, generateReproducibleCommand, isBinaryFile, processAddonTemplates, processAddonsDeps, processFileContent, processTemplateString, transformFilename, writeBtsConfigToVfs };
34010
34171