update: playwright and solid, tanstack-start exmaple
Run Tests / test (push) Successful in 6m46s

This commit is contained in:
2026-09-28 18:42:26 +05:30
parent dfa37b5244
commit a0454f82d6
10 changed files with 87 additions and 89 deletions
+2 -2
View File
@@ -3,7 +3,7 @@
"type": "module", "type": "module",
"sideEffects": false, "sideEffects": false,
"bin": "./dist/cli/index.js", "bin": "./dist/cli/index.js",
"version": "0.0.1", "version": "0.0.2",
"description": "Lightweight Firebase/Supabase alternative built to run anywhere — incl. Next.js, React Router, Astro, Cloudflare, Bun, Node, AWS Lambda & more.", "description": "Lightweight Firebase/Supabase alternative built to run anywhere — incl. Next.js, React Router, Astro, Cloudflare, Bun, Node, AWS Lambda & more.",
"homepage": "https://gitea.bsws.in/shishantbiswas/userbase", "homepage": "https://gitea.bsws.in/shishantbiswas/userbase",
"repository": { "repository": {
@@ -88,7 +88,7 @@
"@libsql/client": "^0.15.15", "@libsql/client": "^0.15.15",
"@mantine/modals": "^7.17.1", "@mantine/modals": "^7.17.1",
"@mantine/notifications": "^7.17.1", "@mantine/notifications": "^7.17.1",
"@playwright/test": "^1.56.1", "@playwright/test": "^1.63.0",
"@rjsf/core": "5.22.2", "@rjsf/core": "5.22.2",
"@rjsf/utils": "5.22.0", "@rjsf/utils": "5.22.0",
"@standard-schema/spec": "^1.0.0", "@standard-schema/spec": "^1.0.0",
+4 -6
View File
@@ -59,7 +59,7 @@
"@libsql/client": "^0.15.15", "@libsql/client": "^0.15.15",
"@mantine/modals": "^7.17.1", "@mantine/modals": "^7.17.1",
"@mantine/notifications": "^7.17.1", "@mantine/notifications": "^7.17.1",
"@playwright/test": "^1.56.1", "@playwright/test": "^1.63.0",
"@rjsf/core": "5.22.2", "@rjsf/core": "5.22.2",
"@rjsf/utils": "5.22.0", "@rjsf/utils": "5.22.0",
"@standard-schema/spec": "^1.0.0", "@standard-schema/spec": "^1.0.0",
@@ -806,7 +806,7 @@
"@plasmicapp/watcher": ["@plasmicapp/watcher@1.0.83", "", { "dependencies": { "socket.io-client": "^4.1.2" } }, "sha512-0XTCsTydTBfOE5al9QOyW89kigjzU9az2JL4n7e3+fVlStEqbhYSB9PIa+Bhiv1iVN91V7xjUSyVJXloWJgwPg=="], "@plasmicapp/watcher": ["@plasmicapp/watcher@1.0.83", "", { "dependencies": { "socket.io-client": "^4.1.2" } }, "sha512-0XTCsTydTBfOE5al9QOyW89kigjzU9az2JL4n7e3+fVlStEqbhYSB9PIa+Bhiv1iVN91V7xjUSyVJXloWJgwPg=="],
"@playwright/test": ["@playwright/test@1.56.1", "", { "dependencies": { "playwright": "1.56.1" }, "bin": { "playwright": "cli.js" } }, "sha512-vSMYtL/zOcFpvJCW71Q/OEGQb7KYBPAdKh35WNSkaZA75JlAO8ED8UN6GUNTm3drWomcbcqRPFqQbLae8yBTdg=="], "@playwright/test": ["@playwright/test@1.63.0", "", { "dependencies": { "playwright": "1.63.0" }, "bin": { "playwright": "cli.js" } }, "sha512-oxMK4vllB9RK5NQ2l1pq1IfOf2AvnEuj/vYGDj0H2nMtmtZpKtCwt/l00GEO6xjGfpBNAvjovvYdCm50dRQkpQ=="],
"@poppinss/colors": ["@poppinss/colors@4.1.5", "", { "dependencies": { "kleur": "^4.1.5" } }, "sha512-FvdDqtcRCtz6hThExcFOgW0cWX+xwSMWcRuQe5ZEb2m7cVQOAVZOIMt+/v9RxGiD9/OY16qJBXK4CVKWAPalBw=="], "@poppinss/colors": ["@poppinss/colors@4.1.5", "", { "dependencies": { "kleur": "^4.1.5" } }, "sha512-FvdDqtcRCtz6hThExcFOgW0cWX+xwSMWcRuQe5ZEb2m7cVQOAVZOIMt+/v9RxGiD9/OY16qJBXK4CVKWAPalBw=="],
@@ -2744,9 +2744,9 @@
"pkg-types": ["pkg-types@1.3.1", "", { "dependencies": { "confbox": "^0.1.8", "mlly": "^1.7.4", "pathe": "^2.0.1" } }, "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ=="], "pkg-types": ["pkg-types@1.3.1", "", { "dependencies": { "confbox": "^0.1.8", "mlly": "^1.7.4", "pathe": "^2.0.1" } }, "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ=="],
"playwright": ["playwright@1.56.1", "", { "dependencies": { "playwright-core": "1.56.1" }, "optionalDependencies": { "fsevents": "2.3.2" }, "bin": { "playwright": "cli.js" } }, "sha512-aFi5B0WovBHTEvpM3DzXTUaeN6eN0qWnTkKx4NQaH4Wvcmc153PdaY2UBdSYKaGYw+UyWXSVyxDUg5DoPEttjw=="], "playwright": ["playwright@1.63.0", "", { "dependencies": { "playwright-core": "1.63.0" }, "bin": { "playwright": "cli.js" } }, "sha512-+7ziBLidS4NaNCdt57SUDT+wYmmd5fmiQejUic/kb+YsYSCPyOOE9sebzMjNmQrsnNpDJqd4WHvV/8lfKfUDUg=="],
"playwright-core": ["playwright-core@1.56.1", "", { "bin": { "playwright-core": "cli.js" } }, "sha512-hutraynyn31F+Bifme+Ps9Vq59hKuUCz7H1kDOcBs+2oGguKkWTU50bBWrtz34OUWmIwpBTWDxaRPXrIXkgvmQ=="], "playwright-core": ["playwright-core@1.63.0", "", { "bin": { "playwright-core": "cli.js" } }, "sha512-rYCsBF/M5HjUch52bbtVONEFjv6Xu8sm8h72dNlR5bzIE1fvC/bxgspzkjSfU+MweEMmPM8KJebG6nnyxo5mCg=="],
"plimit-lit": ["plimit-lit@1.6.1", "", { "dependencies": { "queue-lit": "^1.5.1" } }, "sha512-B7+VDyb8Tl6oMJT9oSO2CW8XC/T4UcJGrwOVoNGwOQsQYhlpfajmrMj5xeejqaASq3V/EqThyOeATEOMuSEXiA=="], "plimit-lit": ["plimit-lit@1.6.1", "", { "dependencies": { "queue-lit": "^1.5.1" } }, "sha512-B7+VDyb8Tl6oMJT9oSO2CW8XC/T4UcJGrwOVoNGwOQsQYhlpfajmrMj5xeejqaASq3V/EqThyOeATEOMuSEXiA=="],
@@ -4168,8 +4168,6 @@
"pino/process-warning": ["process-warning@5.0.0", "", {}, "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA=="], "pino/process-warning": ["process-warning@5.0.0", "", {}, "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA=="],
"playwright/fsevents": ["fsevents@2.3.2", "", { "os": "darwin" }, "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA=="],
"postcss/nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], "postcss/nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
"pretty-format/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="], "pretty-format/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
-55
View File
@@ -1,55 +0,0 @@
import { em, entity, text, boolean } from "userbase";
import { registerLocalMediaAdapter } from "userbase/adapter/node";
import { TanstackStartConfig } from "userbase/adapter/tanstack-start";
const local = registerLocalMediaAdapter();
const schema = em({
todos: entity("todos", {
title: text(),
done: boolean(),
}),
});
// register your schema to get automatic type completion
type Database = (typeof schema)["DB"];
declare module "userbase" {
interface DB extends Database {}
}
export default {
connection: {
url: "file:data.db",
},
options: {
// the seed option is only executed if the database was empty
seed: async (ctx) => {
// create some entries
await ctx.em.mutator("todos").insertMany([
{ title: "Learn userbase", done: true },
{ title: "Build something cool", done: false },
]);
// and create a user
await ctx.app.module.auth.createUser({
email: "test@userbase.io",
password: "12345678",
});
},
},
config: {
data: schema.toJSON(),
auth: {
enabled: true,
jwt: {
secret: "random_gibberish_please_change_this",
},
},
media: {
enabled: true,
adapter: local({
path: "./public/uploads",
}),
},
},
} satisfies TanstackStartConfig;
+22 -22
View File
@@ -9,24 +9,19 @@
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
import { Route as rootRouteImport } from './routes/__root' import { Route as rootRouteImport } from './routes/__root'
import { Route as SsrRouteImport } from './routes/ssr'
import { Route as IndexRouteImport } from './routes/index' import { Route as IndexRouteImport } from './routes/index'
import { Route as ApiSplatRouteImport } from './routes/api.$' import { Route as SsrRouteImport } from './routes/ssr'
import { Route as AdminSplatRouteImport } from './routes/admin.$' import { Route as AdminSplatRouteImport } from './routes/admin.$'
import { Route as ApiSplatRouteImport } from './routes/api.$'
const SsrRoute = SsrRouteImport.update({
id: '/ssr',
path: '/ssr',
getParentRoute: () => rootRouteImport,
} as any)
const IndexRoute = IndexRouteImport.update({ const IndexRoute = IndexRouteImport.update({
id: '/', id: '/',
path: '/', path: '/',
getParentRoute: () => rootRouteImport, getParentRoute: () => rootRouteImport,
} as any) } as any)
const ApiSplatRoute = ApiSplatRouteImport.update({ const SsrRoute = SsrRouteImport.update({
id: '/api/$', id: '/ssr',
path: '/api/$', path: '/ssr',
getParentRoute: () => rootRouteImport, getParentRoute: () => rootRouteImport,
} as any) } as any)
const AdminSplatRoute = AdminSplatRouteImport.update({ const AdminSplatRoute = AdminSplatRouteImport.update({
@@ -34,6 +29,11 @@ const AdminSplatRoute = AdminSplatRouteImport.update({
path: '/admin/$', path: '/admin/$',
getParentRoute: () => rootRouteImport, getParentRoute: () => rootRouteImport,
} as any) } as any)
const ApiSplatRoute = ApiSplatRouteImport.update({
id: '/api/$',
path: '/api/$',
getParentRoute: () => rootRouteImport,
} as any)
export interface FileRoutesByFullPath { export interface FileRoutesByFullPath {
'/': typeof IndexRoute '/': typeof IndexRoute
@@ -71,13 +71,6 @@ export interface RootRouteChildren {
declare module '@tanstack/react-router' { declare module '@tanstack/react-router' {
interface FileRoutesByPath { interface FileRoutesByPath {
'/ssr': {
id: '/ssr'
path: '/ssr'
fullPath: '/ssr'
preLoaderRoute: typeof SsrRouteImport
parentRoute: typeof rootRouteImport
}
'/': { '/': {
id: '/' id: '/'
path: '/' path: '/'
@@ -85,11 +78,11 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof IndexRouteImport preLoaderRoute: typeof IndexRouteImport
parentRoute: typeof rootRouteImport parentRoute: typeof rootRouteImport
} }
'/api/$': { '/ssr': {
id: '/api/$' id: '/ssr'
path: '/api/$' path: '/ssr'
fullPath: '/api/$' fullPath: '/ssr'
preLoaderRoute: typeof ApiSplatRouteImport preLoaderRoute: typeof SsrRouteImport
parentRoute: typeof rootRouteImport parentRoute: typeof rootRouteImport
} }
'/admin/$': { '/admin/$': {
@@ -99,6 +92,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof AdminSplatRouteImport preLoaderRoute: typeof AdminSplatRouteImport
parentRoute: typeof rootRouteImport parentRoute: typeof rootRouteImport
} }
'/api/$': {
id: '/api/$'
path: '/api/$'
fullPath: '/api/$'
preLoaderRoute: typeof ApiSplatRouteImport
parentRoute: typeof rootRouteImport
}
} }
} }
+3 -3
View File
@@ -8,7 +8,7 @@ import { Footer } from "@/components/Footer";
import { List } from "@/components/List"; import { List } from "@/components/List";
export const completeTodo = createServerFn({ method: "POST" }) export const completeTodo = createServerFn({ method: "POST" })
.inputValidator( .validator(
(data) => data as { done: boolean; id: number; title: string }, (data) => data as { done: boolean; id: number; title: string },
) )
.handler(async ({ data: todo }) => { .handler(async ({ data: todo }) => {
@@ -24,7 +24,7 @@ export const completeTodo = createServerFn({ method: "POST" })
}); });
export const deleteTodo = createServerFn({ method: "POST" }) export const deleteTodo = createServerFn({ method: "POST" })
.inputValidator((data) => data as { id: number }) .validator((data) => data as { id: number })
.handler(async ({ data }) => { .handler(async ({ data }) => {
try { try {
const api = await getApi({}); const api = await getApi({});
@@ -36,7 +36,7 @@ export const deleteTodo = createServerFn({ method: "POST" })
}); });
export const createTodo = createServerFn({ method: "POST" }) export const createTodo = createServerFn({ method: "POST" })
.inputValidator((data) => data as { title: string }) .validator((data) => data as { title: string })
.handler(async ({ data }) => { .handler(async ({ data }) => {
try { try {
const api = await getApi({}); const api = await getApi({});
@@ -0,0 +1,55 @@
import { em, entity, text, boolean } from "userbase";
import { registerLocalMediaAdapter } from "userbase/adapter/node";
import type { TanstackStartConfig } from "userbase/adapter/tanstack-start";
const local = registerLocalMediaAdapter();
const schema = em({
todos: entity("todos", {
title: text(),
done: boolean(),
}),
});
// register your schema to get automatic type completion
type Database = (typeof schema)["DB"];
declare module "userbase" {
interface DB extends Database {}
}
export default {
connection: {
url: "file:data.db",
},
options: {
// the seed option is only executed if the database was empty
seed: async (ctx) => {
// create some entries
await ctx.em.mutator("todos").insertMany([
{ title: "Learn userbase", done: true },
{ title: "Build something cool", done: false },
]);
// and create a user
await ctx.app.module.auth.createUser({
email: "test@userbase.io",
password: "12345678",
});
},
},
config: {
data: schema.toJSON(),
auth: {
enabled: true,
jwt: {
secret: "random_gibberish_please_change_this",
},
},
media: {
enabled: true,
adapter: local({
path: "./public/uploads",
}),
},
},
} satisfies TanstackStartConfig;
+1 -1
View File
@@ -3,7 +3,7 @@ import { devtools } from "@tanstack/devtools-vite";
import { tanstackStart } from "@tanstack/react-start/plugin/vite"; import { tanstackStart } from "@tanstack/react-start/plugin/vite";
import viteReact from "@vitejs/plugin-react"; import viteReact from "@vitejs/plugin-react";
import viteTsConfigPaths from "vite-tsconfig-paths"; import viteTsConfigPaths from "vite-tsconfig-paths";
import { fileURLToPath, URL } from "url"; import { fileURLToPath, URL } from "node:url";
import tailwindcss from "@tailwindcss/vite"; import tailwindcss from "@tailwindcss/vite";
import { nitro } from "nitro/vite"; import { nitro } from "nitro/vite";