48 Commits
Author SHA1 Message Date
Jonas Perusquia Morales c59dde38b7 chore: Update GitHub Actions versions in test workflow to latest 2026-08-06 12:34:58 -06:00
Jonas Perusquia Morales 7ff8d6aa86 chore: Remove unused tsx dependency 2026-08-06 11:57:22 -06:00
Jonas Perusquia Morales 2cb985f16d chore: Update package dependencies
Removed stale @aws-crypto packages and bumped multiple dependencies
including: @tsdown/css, @types/node, @vitest/coverage-v8, postcss,
tailwindcss, tsdown, vitest, and various AWS SDK packages to their
latest versions.
2026-08-06 11:56:26 -06:00
Jonas Perusquia Morales 1072b1e69a fix: Disable automatic clean to preserve dist/static 2026-08-06 11:47:31 -06:00
Jonas Perusquia Morales 0c37afd595 fix: Fix admin UI manifest loading in Cloudflare Workers
Use static import so the bundler includes the manifest. Dynamic imports
are not resolved by workerd via package.json exports.
2026-07-14 11:44:55 -06:00
Jonas Perusquia Morales 817441f01d chore: add middleware to normalize trailing slashes in routes 2026-07-13 12:39:05 -06:00
Jonas Perusquia Morales f5824efc24 chore: upgrade astro example from v5 to v7 2026-07-10 13:51:50 -06:00
Jonas Perusquia Morales 7231a7a57b chore: Remove unused Card import from Layout 2026-07-10 13:51:27 -06:00
Jonas Perusquia Morales ec29dc5317 chore: Bump dependencies to latest patch versions 2026-06-19 11:22:50 -06:00
Jonas Perusquia Morales 031123988a chore: ugpraded dependencies 2026-06-05 14:39:52 -06:00
Jonas Perusquia Morales 68723c375f fix: deepwiki feedback addressed 2026-05-15 13:31:50 -06:00
Jonas Perusquia Morales a5dd4d4977 chore: upgrade vite related dependencies 2026-05-13 10:44:06 -06:00
Jonas Perusquia Morales 94aad8af56 chore: incremental build 2026-05-08 10:31:36 -06:00
Jonas Perusquia Morales c904e74a4d chore: upgrade tsdown to v0.22.0
- Bumped minimum Node version from 22.13 to 22.18 (tsdown)
2026-05-08 10:31:12 -06:00
Jonas Perusquia Morales 1b92fd21e0 chore: upgrade postcss to latest 2026-05-05 09:27:00 -06:00
Jonas Perusquia Morales 27b294a1b9 chore: remove unused import 2026-04-30 22:52:10 -06:00
Jonas Perusquia Morales a5a350a23a fix: node tests 2026-04-30 10:41:54 -06:00
Jonas Perusquia Morales cd3f51dd10 chore: upgrade vitest from v3 to v4 2026-04-30 10:41:43 -06:00
Jonas Perusquia Morales 140afa7d83 chore: update PostCSS with Tailwind CSS intergation and adjust dependencies 2026-04-29 18:23:36 -06:00
Jonas Perusquia Morales 845afe10e2 fix: update TypeScript type definitions to use .d.mts extension 2026-04-28 10:32:06 -06:00
Jonas Perusquia Morales 93d4caef25 chore: add alias resolution for sqlite adapter in vitest configuration 2026-04-28 10:31:51 -06:00
Jonas Perusquia MoralesandCopilot 821321539f chore: ensure output format is set to esm in buildTypes function
Co-authored-by: Copilot <copilot@github.com>
2026-04-28 10:31:40 -06:00
Jonas Perusquia MoralesandCopilot 8d62186ddc chore: update build configuration to use string format for esm
Co-authored-by: Copilot <copilot@github.com>
2026-04-28 10:31:20 -06:00
Jonas Perusquia Morales f69a56fb18 fix: update BunFile import to use dynamic import syntax 2026-04-28 09:50:59 -06:00
Jonas Perusquia Morales a3f20b1034 chore: update TypeScript output directory for app workspace 2026-04-28 09:50:46 -06:00
Jonas Perusquia Morales 69ecc27d8e chore: update @tsdown/css and tsdown to v0.21.10
chore: update bun types to latest v1.3.13
2026-04-28 09:50:11 -06:00
Jonas Perusquia Morales 5e983ca1bd chore: update bun.lock 2026-04-21 10:19:06 -06:00
Jonás Perusquía Morales c955c962b3 Merge branch 'release/0.21' into chore/migrate-tsup-to-tsdown 2026-04-16 16:29:25 -06:00
Jonas Perusquia Morales fecf53dc25 chore: upgrade tsdown to v0.21.9 2026-04-16 13:36:52 -06:00
Jonas Perusquia Morales 3f0e924f69 chore: format code and improve readability in build.ts 2026-04-16 13:14:36 -06:00
Jonas Perusquia Morales 01e54fcbc9 chore: Bump tsdown and @tsdown/css to 0.21.8 2026-04-15 16:19:05 -06:00
Jonas Perusquia Morales b2bad5a7fb chore: update build process to improve type generation and configuration 2026-04-02 13:01:51 -06:00
Jonas Perusquia Morales 4c2bd9fc01 chore: simplify exclude paths in tsconfig.build.json 2026-04-02 12:54:38 -06:00
Jonas Perusquia Morales 672784503a chore: disable declaration generation in tsdown configuration for cli and plasmic packages 2026-04-02 12:42:22 -06:00
Jonas Perusquia Morales 10c65a8d0e chore: add entry point to buildTypes function and enable declaration in tsconfig 2026-04-01 18:14:43 -06:00
Jonas Perusquia Morales 7f950a041c chore: update buildTypes function configuration for improved type generation 2026-04-01 16:50:35 -06:00
Jonas Perusquia Morales dbcbeff876 chore: disable declaration and declarationMap in tsconfig.json 2026-04-01 16:49:01 -06:00
Jonas Perusquia Morales a19ed5255c chore: update tailwindcss and related dependencies to version 4.2.2 2026-04-01 16:48:48 -06:00
Jonas Perusquia Morales c783125c0f fix: matched alias not found for tailwind-merge 2026-04-01 14:35:47 -06:00
Jonas Perusquia Morales 90195f9209 chore: simplify buildTypes function by removing unused typeEntryGroups and streamlining build options 2026-03-30 13:10:44 -06:00
Jonas Perusquia Morales bea716c1df chore: update postcss dependency to version 8.5.8 2026-03-30 13:07:58 -06:00
Jonas Perusquia Morales 9a4fee48cb chore: add type annotations for MediaEvents and DatabaseEvents exports 2026-03-30 13:07:34 -06:00
Jonas Perusquia Morales 63f2a2fc3f chore: refactor EventRegistryMap type for clarity and update Events getter to use it 2026-03-30 13:07:26 -06:00
Jonas Perusquia Morales 0489058e3c chore: change isSelectQueryBuilder property to readonly in SelectQueryBuilderExpression interface 2026-03-30 13:07:17 -06:00
Jonas Perusquia Morales 13f78ffa83 chore: update Node.js engine requirement and upgrade TypeScript version 2026-03-30 13:06:36 -06:00
Jonas Perusquia Morales 7140351c4b chore: comment out typeEntryGroups and related code in buildTypes function 2026-03-30 11:59:13 -06:00
Jonas Perusquia Morales 2591be2491 chore: re-add clean:types script to remove type declaration files 2026-03-30 11:13:00 -06:00
Jonas Perusquia Morales 6d30e9ec96 chore: update dependencies and migrate from tsup to tsdown in cli and plasmic packages 2026-03-30 11:08:45 -06:00
63 changed files with 1733 additions and 3972 deletions
+3 -3
View File
@@ -25,15 +25,15 @@ jobs:
--health-retries 5
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
- name: Setup Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v7
with:
node-version: "22.x"
- name: Setup Bun
uses: oven-sh/setup-bun@v1
uses: oven-sh/setup-bun@v2
with:
bun-version: "1.3.3"
+2 -2
View File
@@ -107,7 +107,7 @@ bknd/
index.ts # Main package exports
__test__/ # Unit tests (mirrors src/ structure)
e2e/ # End-to-end tests (Playwright)
build.ts # Build script (tsup/esbuild)
build.ts # Build script (tsdown/rolldown)
build.cli.ts # CLI build script
packages/ # Small satellite packages
cli/ # Standalone CLI package (bknd-cli)
@@ -146,7 +146,7 @@ Plugins (in `app/src/plugins/`) hook into the app lifecycle via callbacks like `
### Build System
The build is handled by a custom `app/build.ts` script using tsup (esbuild under the hood). It builds four targets in parallel:
The build is handled by a custom `app/build.ts` script using tsdown (rolldown under the hood). It builds four targets in parallel:
- **API** -- the core backend
- **UI** -- the admin interface
+138 -122
View File
@@ -1,9 +1,9 @@
import { $ } from "bun";
import * as tsup from "tsup";
import { build, type UserConfig } from "tsdown";
import pkg from "./package.json" with { type: "json" };
import c from "picocolors";
import { watch as fsWatch, readdirSync, rmSync } from "node:fs";
import { join } from "node:path";
import { join, resolve } from "node:path";
import { buildTypes as runTypesBuild } from "./build.types";
const args = process.argv.slice(2);
const watch = args.includes("--watch");
@@ -11,8 +11,9 @@ const minify = args.includes("--minify");
const types = args.includes("--types");
const sourcemap = args.includes("--sourcemap");
const clean = args.includes("--clean");
const dev = args.includes("--dev");
// silence tsup
// silence tsdown
const oldConsole = {
log: console.log,
warn: console.warn,
@@ -36,48 +37,41 @@ if (clean) {
const entryPath = join(distPath, entry);
rmSync(entryPath, { recursive: true, force: true });
}
} catch (e) {
} catch {
// dist may not exist yet, ignore
}
}
let types_running = false;
function buildTypes() {
async function buildTypes() {
if (types_running || !types) return;
types_running = true;
Bun.spawn(["bun", "build:types"], {
stdout: "inherit",
onExit: () => {
try {
await runTypesBuild();
oldConsole.log(c.cyan("[Types]"), c.green("built"));
Bun.spawn(["bun", "tsc-alias"], {
stdout: "inherit",
onExit: () => {
oldConsole.log(c.cyan("[Types]"), c.green("aliased"));
} finally {
types_running = false;
},
});
},
});
}
if (types && !watch) {
buildTypes();
}
let watcher_timeout: any;
function delayTypes() {
function delayTypes(delayTime = 1000) {
if (!watch || !types) return;
if (watcher_timeout) {
clearTimeout(watcher_timeout);
}
watcher_timeout = setTimeout(buildTypes, 1000);
watcher_timeout = setTimeout(() => {
void buildTypes().catch((error) => {
oldConsole.warn(c.cyan("[Types]"), c.red("build failed"), error);
});
}, delayTime);
}
const dependencies = Object.keys(pkg.dependencies);
// collection of always-external packages
const external = [
// collection of always-neverBundle packages
const neverBundle = [
...dependencies,
"bun:test",
"node:test",
@@ -93,11 +87,14 @@ const external = [
* Building backend and general API
*/
async function buildApi() {
await tsup.build({
await build({
minify,
sourcemap,
// don't use tsup's broken watch, we'll handle it ourselves
// don't use tsdown's broken watch, we'll handle it ourselves
watch: false,
// cleaning is handled manually (--clean) to preserve dist/static
clean: false,
dts: false,
define,
entry: [
"src/index.ts",
@@ -105,14 +102,19 @@ async function buildApi() {
"src/plugins/index.ts",
"src/modes/index.ts",
],
unbundle: false,
outDir: "dist",
external: [...external],
metafile: true,
deps: {
neverBundle: [
...neverBundle
],
},
target: "esnext",
platform: "browser",
removeNodeProtocol: false,
format: ["esm"],
splitting: false,
nodeProtocol: true,
format: "esm",
loader: {
".svg": "dataurl",
},
@@ -125,10 +127,7 @@ async function buildApi() {
async function rewriteClient(path: string) {
const bundle = await Bun.file(path).text();
await Bun.write(
path,
'"use client";\n' + bundle.replaceAll("ui/client", "bknd/client"),
);
await Bun.write(path, '"use client";\n' + bundle.replaceAll("ui/client", "bknd/client"));
}
/**
@@ -139,9 +138,19 @@ async function buildUi() {
minify,
sourcemap,
watch: false,
// cleaning is handled manually (--clean) to preserve dist/static
clean: false,
dts: false,
define,
external: [
...external,
css: {
transformer: "postcss",
postcss: "postcss.config.js",
minify: true,
splitting: false
},
deps: {
neverBundle: [
...neverBundle,
"react",
"react-dom",
"react/jsx-runtime",
@@ -151,21 +160,23 @@ async function buildUi() {
"@xyflow/react",
"@mantine/core",
],
metafile: true,
},
devtools: dev,
platform: "browser",
format: ["esm"],
splitting: false,
bundle: true,
format: "esm",
unbundle: false,
treeshake: true,
loader: {
".svg": "dataurl",
},
esbuildOptions: (options) => {
options.logLevel = "silent";
},
} satisfies tsup.Options;
await tsup.build({
logLevel: "silent",
} satisfies UserConfig;
await Promise.all([
build({
...base,
entry: ["src/ui/index.ts", "src/ui/main.css", "src/ui/styles.css"],
outDir: "dist/ui",
@@ -174,9 +185,9 @@ async function buildUi() {
delayTypes();
oldConsole.log(c.cyan("[UI]"), c.green("built"));
},
});
}),
await tsup.build({
build({
...base,
entry: ["src/ui/client/index.ts"],
outDir: "dist/ui/client",
@@ -185,7 +196,8 @@ async function buildUi() {
delayTypes();
oldConsole.log(c.cyan("[UI]"), "Client", c.green("built"));
},
});
}),
]);
}
/**
@@ -194,14 +206,18 @@ async function buildUi() {
* - ui/client is external, and after built replaced with "bknd/client"
*/
async function buildUiElements() {
await tsup.build({
await build({
minify,
sourcemap,
watch: false,
// cleaning is handled manually (--clean) to preserve dist/static
clean: false,
dts: false,
define,
entry: ["src/ui/elements/index.ts"],
outDir: "dist/ui/elements",
external: [
deps: {
neverBundle: [
"ui/client",
"bknd",
/^bknd\/.*/,
@@ -212,21 +228,19 @@ async function buildUiElements() {
"react/jsx-dev-runtime",
"use-sync-external-store",
],
metafile: true,
},
platform: "browser",
format: ["esm"],
splitting: false,
format: "esm",
bundle: true,
treeshake: true,
loader: {
".svg": "dataurl",
},
esbuildOptions: (options) => {
options.alias = {
alias: {
// not important for elements, mock to reduce bundle
"tailwind-merge": "./src/ui/elements/mocks/tailwind-merge.ts",
};
"tailwind-merge": resolve(import.meta.dir, "src/ui/elements/mocks/tailwind-merge.ts"),
},
onSuccess: async () => {
await rewriteClient("./dist/ui/elements/index.js");
delayTypes();
@@ -238,21 +252,20 @@ async function buildUiElements() {
/**
* Building adapters
*/
function baseConfig(
adapter: string,
overrides: Partial<tsup.Options> = {},
): tsup.Options {
function baseConfig(adapter: string, overrides: Partial<UserConfig> = {}): UserConfig {
return {
minify,
sourcemap,
watch: false,
// cleaning is handled manually (--clean) to preserve dist/static
clean: false,
dts: false,
entry: [`src/adapter/${adapter}/index.ts`],
format: ["esm"],
format: "esm",
platform: "neutral",
outDir: `dist/adapter/${adapter}`,
metafile: true,
splitting: false,
removeNodeProtocol: false,
devtools: dev,
nodeProtocol: true,
onSuccess: async () => {
delayTypes();
oldConsole.log(c.cyan("[Adapter]"), adapter || "base", c.green("built"));
@@ -262,21 +275,23 @@ function baseConfig(
...define,
...overrides.define,
},
external: [
deps: {
neverBundle: [
/^cloudflare*/,
/^@?hono.*?/,
/^(bknd|react|next|node).*?/,
/.*\.(html)$/,
...external,
...(Array.isArray(overrides.external) ? overrides.external : []),
...neverBundle,
...(Array.isArray(overrides.deps?.neverBundle) ? overrides.deps.neverBundle : []),
],
},
};
}
async function buildAdapters() {
await Promise.all([
// base adapter handles
tsup.build({
build({
...baseConfig(""),
target: "esnext",
platform: "neutral",
@@ -284,111 +299,120 @@ async function buildAdapters() {
outDir: "dist/adapter",
// only way to keep @vite-ignore comments
minify: false,
dts: false,
}),
// specific adatpers
tsup.build(baseConfig("react-router")),
tsup.build(
// specific adapters
build(baseConfig("react-router")),
build(
baseConfig("browser", {
external: [/^sqlocal\/?.*?/, "wouter"],
deps: {
neverBundle: [/^sqlocal\/?.*?/, "wouter"],
},
}),
),
tsup.build(
build(
baseConfig("bun", {
external: [/^bun\:.*/],
deps: {
neverBundle: [/^bun:.*/],
},
}),
),
tsup.build(baseConfig("astro")),
tsup.build(baseConfig("aws")),
tsup.build(
build(baseConfig("astro")),
build(baseConfig("aws")),
build(
baseConfig("cloudflare", {
external: ["wrangler", "node:process"],
deps: {
neverBundle: ["wrangler", "node:process", "miniflare"],
},
}),
),
tsup.build(
build(
baseConfig("cloudflare/proxy", {
target: "esnext",
entry: ["src/adapter/cloudflare/proxy.ts"],
outDir: "dist/adapter/cloudflare",
metafile: false,
external: [/bknd/, "wrangler", "node:process"],
devtools: false,
deps: {
neverBundle: [/bknd/, "wrangler", "node:process", "miniflare"],
},
}),
),
tsup.build({
build({
...baseConfig("vite"),
platform: "node",
}),
tsup.build({
build({
...baseConfig("nextjs"),
platform: "node",
}),
tsup.build({
build({
...baseConfig("tanstack-start"),
platform: "node",
}),
tsup.build({
...baseConfig("web"),
platform: "neutral",
}),
tsup.build({
build({
...baseConfig("sveltekit"),
platform: "node",
}),
tsup.build({
build({
...baseConfig("nuxt"),
platform: "node",
}),
tsup.build({
build({
...baseConfig("node"),
platform: "node",
}),
tsup.build({
build({
...baseConfig("sqlite/edge"),
entry: ["src/adapter/sqlite/edge.ts"],
outDir: "dist/adapter/sqlite",
metafile: false,
devtools: false,
}),
tsup.build({
build({
...baseConfig("sqlite/node"),
entry: ["src/adapter/sqlite/node.ts"],
outDir: "dist/adapter/sqlite",
platform: "node",
metafile: false,
devtools: false,
}),
tsup.build({
...baseConfig("sqlite/bun"),
build(
baseConfig("sqlite/bun", {
entry: ["src/adapter/sqlite/bun.ts"],
outDir: "dist/adapter/sqlite",
metafile: false,
external: [/^bun\:.*/],
devtools: false,
deps: {
neverBundle: [/^bun:.*/],
},
}),
),
]);
}
async function buildAll() {
await Promise.all([
buildApi(),
buildUi(),
buildUiElements(),
buildAdapters(),
]);
const buildPipeline = [buildApi, buildUi, buildUiElements, buildAdapters];
// Run type generation in parallel, since it can be slow and doesn't block the main build
if (types) {
buildPipeline.push(async () => await buildTypes());
}
// initial build
await buildAll();
// custom watcher since tsup's watch is broken in 8.3.5+
async function buildAll() {
await Promise.all(buildPipeline.map(fn => fn()));
}
// custom watcher since tsdown's watch is broken in v0.21.7
if (watch) {
oldConsole.log(c.cyan("[Watch]"), "watching for changes in src/...");
@@ -414,22 +438,14 @@ if (watch) {
};
// watch src directory recursively
fsWatch(
join(import.meta.dir, "src"),
{ recursive: true },
(event, filename) => {
fsWatch(join(import.meta.dir, "src"), { recursive: true }, (event, filename) => {
if (!filename) return;
// ignore non-source files
if (
!filename.endsWith(".ts") &&
!filename.endsWith(".tsx") &&
!filename.endsWith(".css")
)
if (!filename.endsWith(".ts") && !filename.endsWith(".tsx") && !filename.endsWith(".css"))
return;
oldConsole.log(c.cyan("[Watch]"), c.dim(`${event}: ${filename}`));
debouncedRebuild();
},
);
});
// keep process alive
await new Promise(() => { });
+35
View File
@@ -0,0 +1,35 @@
import { build } from "tsdown";
import c from "picocolors";
export async function buildTypes() {
await build({
tsconfig: "tsconfig.build.json",
watch: false,
clean: true,
logLevel: "silent",
unbundle: true,
root: "src",
outDir: "dist/types",
entry: ["./src/**/*.ts", "./src/**/*.tsx"],
format: "esm",
dts: {
emitDtsOnly: true,
resolver: "oxc",
eager: true,
incremental: true,
},
deps: {
skipNodeModulesBundle: true
},
});
}
if (import.meta.main) {
try {
await buildTypes();
console.log(c.cyan("[Types]"), c.green("built"));
} catch (error) {
console.error(c.cyan("[Types]"), c.red("build failed"), error);
process.exitCode = 1;
}
}
-3
View File
@@ -15,9 +15,6 @@ const configs = {
nextjs: {
base_path: "/admin",
},
meta: {
base_path: "/admin",
},
nuxt: {
base_path: "/admin",
},
+107 -84
View File
@@ -15,7 +15,8 @@
},
"packageManager": "bun@1.3.3",
"engines": {
"node": ">=22.13"
"node": ">=22.18.0",
"bun": ">=1.3.3"
},
"scripts": {
"dev": "BKND_CLI_LOG_LEVEL=debug vite",
@@ -23,11 +24,11 @@
"build:all": "rm -rf dist && bun run build:static && NODE_ENV=production bun run build.ts --minify --types --clean && bun run build:cli",
"build:ci": "mkdir -p dist/static/.vite && echo '{}' > dist/static/.vite/manifest.json && NODE_ENV=production bun run build.ts",
"build:cli": "bun run build.cli.ts",
"build:types": "bun run build.types.ts",
"build:static": "vite build",
"watch": "bun run build.ts --types --watch",
"types": "bun tsc -p tsconfig.build.json --noEmit",
"clean:types": "find ./dist -name '*.d.ts' -delete && rm -f ./dist/tsconfig.tsbuildinfo",
"build:types": "tsc -p tsconfig.build.json --emitDeclarationOnly && tsc-alias",
"updater": "bun x npm-check-updates -ui",
"cli": "LOCAL=1 bun src/cli/index.ts",
"prepublishOnly": "bun run types && bun run test && bun run test:node && NODE_NO_WARNINGS=1 VITE_DB_URL=:memory: bun run test:e2e && bun run build:all && cp ../README.md ./",
@@ -93,17 +94,18 @@
"@rjsf/utils": "5.22.0",
"@standard-schema/spec": "^1.0.0",
"@tabler/icons-react": "3.35.0",
"@tailwindcss/postcss": "^4.1.16",
"@tailwindcss/vite": "^4.1.16",
"@tailwindcss/postcss": "^4.2.2",
"@tailwindcss/vite": "^4.2.4",
"@tanstack/react-store": "^0.8.0",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.2.0",
"@types/node": "^24.10.0",
"@tsdown/css": "^0.22.14",
"@types/node": "^24.13.3",
"@types/pg": "^8.15.6",
"@types/react": "^19.0.10",
"@types/react": "^19.2.16",
"@types/react-dom": "^19.0.4",
"@vitejs/plugin-react": "^5.1.0",
"@vitest/coverage-v8": "3.0.9",
"@vitejs/plugin-react": "^6.0.2",
"@vitest/coverage-v8": "4.1.10",
"autoprefixer": "^10.4.21",
"clsx": "^2.1.1",
"commander": "^14.0.2",
@@ -114,89 +116,87 @@
"kysely-postgres-js": "^2.0.0",
"libsql": "^0.5.22",
"libsql-stateless-easy": "^1.8.0",
"miniflare": "^4.20251011.2",
"miniflare": "^4.20260317.3",
"open": "^10.2.0",
"openapi-types": "^12.1.3",
"pg": "^8.16.3",
"postcss": "^8.5.3",
"postcss": "^8.5.26",
"postcss-import": "^16.1.1",
"postcss-preset-mantine": "^1.18.0",
"postcss-simple-vars": "^7.0.1",
"postgres": "^3.4.7",
"posthog-js-lite": "^3.6.0",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react": "^19.2.7",
"react-dom": "^19.2.7",
"react-hook-form": "^7.66.0",
"react-icons": "5.5.0",
"react-json-view-lite": "^2.5.0",
"sql-formatter": "^15.6.10",
"sqlocal": "^0.16.0",
"tailwind-merge": "^3.0.2",
"tailwindcss": "^4.1.16",
"tailwindcss": "^4.3.3",
"tailwindcss-animate": "^1.0.7",
"tsc-alias": "^1.8.16",
"tsup": "^8.5.0",
"tsx": "^4.20.6",
"tsdown": "^0.22.14",
"uuid": "^13.0.0",
"vite": "^7.1.12",
"vite-plugin-circular-dependency": "^0.5.0",
"vite-tsconfig-paths": "^5.1.4",
"vitest": "3.0.9",
"vite": "^8.0.16",
"vite-plugin-circular-dependency": "^0.6.0",
"vitest": "4.1.10",
"wouter": "^3.7.1",
"wrangler": "^4.45.4"
"wrangler": "^4.78.0"
},
"optionalDependencies": {
"@hono/node-server": "^1.19.6"
},
"peerDependencies": {
"react": ">=19",
"react-dom": ">=19"
"react": ">=19.2.6",
"react-dom": ">=19.2.6"
},
"main": "./dist/index.js",
"module": "./dist/index.js",
"types": "./dist/types/index.d.ts",
"types": "./dist/types/index.d.mts",
"exports": {
".": {
"types": "./dist/types/index.d.ts",
"types": "./dist/types/index.d.mts",
"import": "./dist/index.js",
"require": "./dist/index.js"
},
"./ui": {
"types": "./dist/types/ui/index.d.ts",
"types": "./dist/types/ui/index.d.mts",
"import": "./dist/ui/index.js",
"require": "./dist/ui/index.js"
},
"./elements": {
"types": "./dist/types/ui/elements/index.d.ts",
"types": "./dist/types/ui/elements/index.d.mts",
"import": "./dist/ui/elements/index.js",
"require": "./dist/ui/elements/index.js"
},
"./client": {
"types": "./dist/types/ui/client/index.d.ts",
"types": "./dist/types/ui/client/index.d.mts",
"import": "./dist/ui/client/index.js",
"require": "./dist/ui/client/index.js"
},
"./utils": {
"types": "./dist/types/core/utils/index.d.ts",
"types": "./dist/types/core/utils/index.d.mts",
"import": "./dist/core/utils/index.js",
"require": "./dist/core/utils/index.js"
},
"./cli": {
"types": "./dist/types/cli/index.d.ts",
"types": "./dist/types/cli/index.d.mts",
"import": "./dist/cli/index.js",
"require": "./dist/cli/index.js"
},
"./plugins": {
"types": "./dist/types/plugins/index.d.ts",
"types": "./dist/types/plugins/index.d.mts",
"import": "./dist/plugins/index.js",
"require": "./dist/plugins/index.js"
},
"./modes": {
"types": "./dist/types/modes/index.d.ts",
"types": "./dist/types/modes/index.d.mts",
"import": "./dist/modes/index.js",
"require": "./dist/modes/index.js"
},
"./adapter/sqlite": {
"types": "./dist/types/adapter/sqlite/edge.d.ts",
"types": "./dist/types/adapter/sqlite/edge.d.mts",
"import": {
"workerd": "./dist/adapter/sqlite/edge.js",
"edge-light": "./dist/adapter/sqlite/edge.js",
@@ -204,107 +204,130 @@
"vercel": "./dist/adapter/sqlite/edge.js",
"browser": "./dist/adapter/sqlite/edge.js",
"bun": "./dist/adapter/sqlite/bun.js",
"node": "./dist/adapter/sqlite/node.js",
"default": "./dist/adapter/sqlite/node.js"
"node": "./dist/adapter/sqlite/node.mjs",
"default": "./dist/adapter/sqlite/node.mjs"
},
"require": "./dist/adapter/sqlite/node.js"
"require": "./dist/adapter/sqlite/node.mjs"
},
"./adapter/cloudflare": {
"types": "./dist/types/adapter/cloudflare/index.d.ts",
"types": "./dist/types/adapter/cloudflare/index.d.mts",
"import": "./dist/adapter/cloudflare/index.js",
"require": "./dist/adapter/cloudflare/index.js"
},
"./adapter/cloudflare/proxy": {
"types": "./dist/types/adapter/cloudflare/proxy.d.ts",
"types": "./dist/types/adapter/cloudflare/proxy.d.mts",
"import": "./dist/adapter/cloudflare/proxy.js",
"require": "./dist/adapter/cloudflare/proxy.js"
},
"./adapter": {
"types": "./dist/types/adapter/index.d.ts",
"types": "./dist/types/adapter/index.d.mts",
"import": "./dist/adapter/index.js"
},
"./adapter/vite": {
"types": "./dist/types/adapter/vite/index.d.ts",
"import": "./dist/adapter/vite/index.js",
"require": "./dist/adapter/vite/index.js"
"types": "./dist/types/adapter/vite/index.d.mts",
"import": "./dist/adapter/vite/index.mjs",
"require": "./dist/adapter/vite/index.mjs"
},
"./adapter/nextjs": {
"types": "./dist/types/adapter/nextjs/index.d.ts",
"import": "./dist/adapter/nextjs/index.js",
"require": "./dist/adapter/nextjs/index.js"
},
"./adapter/web": {
"types": "./dist/types/adapter/web/index.d.ts",
"import": "./dist/adapter/web/index.js",
"require": "./dist/adapter/web/index.js"
"types": "./dist/types/adapter/nextjs/index.d.mts",
"import": "./dist/adapter/nextjs/index.mjs",
"require": "./dist/adapter/nextjs/index.mjs"
},
"./adapter/nuxt": {
"types": "./dist/types/adapter/nuxt/index.d.ts",
"import": "./dist/adapter/nuxt/index.js",
"require": "./dist/adapter/nuxt/index.js"
"types": "./dist/types/adapter/nuxt/index.d.mts",
"import": "./dist/adapter/nuxt/index.mjs",
"require": "./dist/adapter/nuxt/index.mjs"
},
"./adapter/react-router": {
"types": "./dist/types/adapter/react-router/index.d.ts",
"types": "./dist/types/adapter/react-router/index.d.mts",
"import": "./dist/adapter/react-router/index.js",
"require": "./dist/adapter/react-router/index.js"
},
"./adapter/bun": {
"types": "./dist/types/adapter/bun/index.d.ts",
"types": "./dist/types/adapter/bun/index.d.mts",
"import": "./dist/adapter/bun/index.js",
"require": "./dist/adapter/bun/index.js"
},
"./adapter/node": {
"types": "./dist/types/adapter/node/index.d.ts",
"import": "./dist/adapter/node/index.js",
"require": "./dist/adapter/node/index.js"
"types": "./dist/types/adapter/node/index.d.mts",
"import": "./dist/adapter/node/index.mjs",
"require": "./dist/adapter/node/index.mjs"
},
"./adapter/astro": {
"types": "./dist/types/adapter/astro/index.d.ts",
"types": "./dist/types/adapter/astro/index.d.mts",
"import": "./dist/adapter/astro/index.js",
"require": "./dist/adapter/astro/index.js"
},
"./adapter/sveltekit": {
"types": "./dist/types/adapter/sveltekit/index.d.ts",
"import": "./dist/adapter/sveltekit/index.js",
"require": "./dist/adapter/sveltekit/index.js"
"types": "./dist/types/adapter/sveltekit/index.d.mts",
"import": "./dist/adapter/sveltekit/index.mjs",
"require": "./dist/adapter/sveltekit/index.mjs"
},
"./adapter/aws": {
"types": "./dist/types/adapter/aws/index.d.ts",
"types": "./dist/types/adapter/aws/index.d.mts",
"import": "./dist/adapter/aws/index.js",
"require": "./dist/adapter/aws/index.js"
},
"./adapter/browser": {
"types": "./dist/types/adapter/browser/index.d.ts",
"types": "./dist/types/adapter/browser/index.d.mts",
"import": "./dist/adapter/browser/index.js",
"require": "./dist/adapter/browser/index.js"
},
"./adapter/tanstack-start": {
"types": "./dist/types/adapter/tanstack-start/index.d.ts",
"import": "./dist/adapter/tanstack-start/index.js",
"require": "./dist/adapter/tanstack-start/index.js"
"types": "./dist/types/adapter/tanstack-start/index.d.mts",
"import": "./dist/adapter/tanstack-start/index.mjs",
"require": "./dist/adapter/tanstack-start/index.mjs"
},
"./dist/main.css": "./dist/ui/main.css",
"./dist/styles.css": "./dist/ui/styles.css",
"./dist/main.css": "./dist/ui/style.css",
"./dist/styles.css": "./dist/ui/style.css",
"./dist/style.css": "./dist/ui/style.css",
"./dist/manifest.json": "./dist/static/.vite/manifest.json",
"./static/*": "./dist/static/*"
},
"typesVersions": {
"*": {
"utils": ["./dist/types/core/utils/index.d.ts"],
"cli": ["./dist/types/cli/index.d.ts"],
"plugins": ["./dist/types/plugins/index.d.ts"],
"adapter": ["./dist/types/adapter/index.d.ts"],
"adapter/cloudflare": ["./dist/types/adapter/cloudflare/index.d.ts"],
"adapter/vite": ["./dist/types/adapter/vite/index.d.ts"],
"adapter/nextjs": ["./dist/types/adapter/nextjs/index.d.ts"],
"adapter/web": ["./dist/types/adapter/web/index.d.ts"],
"adapter/nuxt": ["./dist/types/adapter/nuxt/index.d.ts"],
"adapter/react-router": ["./dist/types/adapter/react-router/index.d.ts"],
"adapter/bun": ["./dist/types/adapter/bun/index.d.ts"],
"adapter/node": ["./dist/types/adapter/node/index.d.ts"],
"adapter/sveltekit": ["./dist/types/adapter/sveltekit/index.d.ts"],
"adapter/tanstack-start": ["./dist/types/adapter/tanstack-start/index.d.ts"],
"adapter/sqlite": ["./dist/types/adapter/sqlite/edge.d.ts"]
"utils": [
"./dist/types/core/utils/index.d.mts"
],
"cli": [
"./dist/types/cli/index.d.mts"
],
"plugins": [
"./dist/types/plugins/index.d.mts"
],
"adapter": [
"./dist/types/adapter/index.d.mts"
],
"adapter/cloudflare": [
"./dist/types/adapter/cloudflare/index.d.mts"
],
"adapter/vite": [
"./dist/types/adapter/vite/index.d.mts"
],
"adapter/nextjs": [
"./dist/types/adapter/nextjs/index.d.mts"
],
"adapter/nuxt": [
"./dist/types/adapter/nuxt/index.d.mts"
],
"adapter/react-router": [
"./dist/types/adapter/react-router/index.d.mts"
],
"adapter/bun": [
"./dist/types/adapter/bun/index.d.mts"
],
"adapter/node": [
"./dist/types/adapter/node/index.d.mts"
],
"adapter/sveltekit": [
"./dist/types/adapter/sveltekit/index.d.mts"
],
"adapter/tanstack-start": [
"./dist/types/adapter/tanstack-start/index.d.mts"
],
"adapter/sqlite": [
"./dist/types/adapter/sqlite/edge.d.mts"
]
}
},
"publishConfig": {
@@ -8,6 +8,12 @@ import { $console } from "bknd/utils";
import { createRuntimeApp } from "bknd/adapter";
import { registerAsyncsExecutionContext, makeConfig, type CloudflareContext } from "./config";
// Statically import the admin UI build manifest so the bundler (wrangler/esbuild)
// resolves and bundles it at build time, rather than leaving a runtime dynamic
// import that workerd cannot resolve via package.json exports maps.
// @ts-ignore
import adminUiManifest from "bknd/dist/manifest.json";
declare global {
namespace Cloudflare {
interface Env {}
@@ -38,6 +44,19 @@ export async function createApp<Env extends CloudflareEnv = CloudflareEnv>(
ctx: Partial<CloudflareContext<Env>> = {},
) {
const appConfig = await makeConfig(config, ctx);
// The AdminController can't dynamically import "bknd/dist/manifest.json"
// at runtime in the Workers runtime (workerd ignores package.json exports
// for runtime imports). Statically import it here so esbuild bundles the
// manifest into the Worker and pass it as the admin controller's manifest
// unless the caller explicitly provides one (or disables the admin).
if (appConfig.adminOptions !== false && !(appConfig.adminOptions as any)?.manifest) {
appConfig.adminOptions = {
...(appConfig.adminOptions as any),
manifest: adminUiManifest,
};
}
return await createRuntimeApp<Env>(
{
...appConfig,
-1
View File
@@ -1 +0,0 @@
export * from "./web.adapter";
-104
View File
@@ -1,104 +0,0 @@
import { afterAll, beforeAll, describe, test, expect } from "bun:test";
import { createBknd } from "./web.adapter";
import { disableConsoleLog, enableConsoleLog } from "core/utils";
import { adapterTestSuite } from "adapter/adapter-test-suite";
import { bunTestRunner } from "adapter/bun/test";
beforeAll(disableConsoleLog);
afterAll(enableConsoleLog);
describe("web adapter via createBknd", () => {
adapterTestSuite(bunTestRunner, {
makeApp: (options, args) => createBknd({ mode: "api", options }, args).getApp(),
makeHandler: (options, args) => createBknd({ mode: "api", options: options ?? {} }, args).serve(),
});
// ------------------------ MODE API ------------------------
test("caches app instance", async () => {
const bknd = createBknd({ mode: "api", options: { connection: { url: ":memory:" } } });
const app1 = await bknd.getApp();
const app2 = await bknd.getApp();
expect(app1).toBe(app2);
});
test("getApi returns api", async () => {
const bknd = createBknd({ mode: "api", options: { connection: { url: ":memory:" } } });
const api = await bknd.getApi();
expect(api).toBeDefined();
});
test("uses createFrameworkApp ", async () => {
const bknd = createBknd({ mode: "api", options: { connection: { url: ":memory:" } } });
const app = await bknd.getApp();
expect(app).toBeDefined();
expect(app.isBuilt()).toBe(true);
});
test("serve returns a fetch handler", async () => {
const bknd = createBknd({ mode: "api", options: { connection: { url: ":memory:" } } });
const handler = bknd.serve();
const res = await handler(new Request("http://localhost:3000/api/system/config"));
expect(res.status).toBe(200);
});
});
// ------------------------ MODE STANDALONE ------------------------
describe("web adapter via createBknd in standalone mode", () => {
adapterTestSuite(bunTestRunner, {
makeApp: (options, args) => createBknd({ mode: "standalone", options }, args).getApp(),
makeHandler: (options, args) => createBknd({ mode: "standalone", options: options ?? {} }, args).serve(),
});
test("caches app instance", async () => {
const bknd = createBknd({ mode: "standalone", options: { connection: { url: ":memory:" } } });
const app1 = await bknd.getApp();
const app2 = await bknd.getApp();
expect(app1).toBe(app2);
});
test("getApi returns api", async () => {
const bknd = createBknd({ mode: "standalone", options: { connection: { url: ":memory:" } } });
const api = await bknd.getApi();
expect(api).toBeDefined();
});
test("uses createRuntimeApp", async () => {
const bknd = createBknd({
mode: "standalone",
options: {
connection: { url: ":memory:" },
adminOptions: { adminBasepath: "/admin" },
}
});
const app = await bknd.getApp();
expect(app).toBeDefined();
expect(app.isBuilt()).toBe(true);
});
test("serve returns a fetch handler", async () => {
const bknd = createBknd({
mode: "standalone",
options: {
connection: { url: ":memory:" },
adminOptions: { adminBasepath: "/admin" },
}
});
const handler = bknd.serve();
const res = await handler(new Request("http://localhost:3000/api/system/config"));
expect(res.status).toBe(200);
});
test("check admin route", async () => {
const bknd = createBknd({
mode: "standalone",
options: {
connection: { url: ":memory:" },
adminOptions: { adminBasepath: "/admin" },
}
});
const handler = bknd.serve();
const res = await handler(new Request("http://localhost:3000/admin"));
expect(res.status).toBe(200);
});
});
-68
View File
@@ -1,68 +0,0 @@
import {
createFrameworkApp,
createRuntimeApp,
type FrameworkBkndConfig,
type RuntimeBkndConfig,
} from "bknd/adapter";
import { $console } from "core/utils";
import type { App } from "App";
export type AdapterModeWithOptions<Env = Record<string, string | undefined>> =
| {
mode: "standalone";
options: RuntimeBkndConfig<Env>;
}
| {
mode: "api";
options: FrameworkBkndConfig<Env>;
};
export function createBknd<Env>(config: AdapterModeWithOptions<Env>, env?: Env) {
let appPromise: Promise<App> | undefined;
const { mode, options } = config;
async function getApp(): Promise<App> {
if (!appPromise) {
if (mode === "standalone") {
if (options.adminOptions && !options.serveStatic) {
$console.warn(
"adminOptions provided without serveStatic — admin UI assets may not be served. " +
"See `serveStatic`, `serveStaticViaImport`, or add a `package.json` script that runs `bknd copy-assets --out {relative_static_assets_directory_path}`.",
);
}
appPromise = createRuntimeApp(options, env);
} else {
appPromise = createFrameworkApp(options, env);
}
}
return appPromise;
}
async function getApi(opts?: { headers?: Headers; verify?: boolean }) {
const app = await getApp();
if (opts?.verify) {
const api = app.getApi({ headers: opts.headers });
await api.verifyAuth();
return api;
}
return app.getApi(opts?.headers ? { headers: opts.headers } : undefined);
}
function serve() {
return async (req: Request) => {
const app = await getApp();
return app.fetch(req);
};
}
return { getApp, getApi, serve };
}
/** Utility type to determine the config type based on mode,
* Usage `Config<"standalone">` or `Config<"api">`
*/
export type Config<T extends AdapterModeWithOptions["mode"]> = Extract<
Parameters<typeof createBknd>[0],
{ mode: T }
>['options'];
+7 -1
View File
@@ -14,6 +14,12 @@ export interface EmitsEvents {
emgr: EventManager;
}
export type EventRegistryMap<
RegisteredEvents extends Record<string, EventClass> = Record<string, EventClass>,
> = {
[K in keyof RegisteredEvents]: RegisteredEvents[K];
};
// for compatibility, moved it to Event.ts
export type { EventClass };
@@ -65,7 +71,7 @@ export class EventManager<
return [...this.listeners];
}
get Events(): { [K in keyof RegisteredEvents]: RegisteredEvents[K] } {
get Events(): EventRegistryMap<RegisteredEvents> {
// proxy class to access events
return new Proxy(this, {
get: (_, prop: string) => {
+1 -1
View File
@@ -31,7 +31,7 @@ export type IndexMetadata = {
};
export interface SelectQueryBuilderExpression<O> extends AliasableExpression<O> {
get isSelectQueryBuilder(): true;
readonly isSelectQueryBuilder: true;
toOperationNode(): SelectQueryNode;
}
+5 -2
View File
@@ -82,7 +82,7 @@ export * as AuthPermissions from "auth/auth-permissions";
*/
export { getExtensionFromName, getRandomizedFilename } from "media/utils";
import * as StorageEvents from "media/storage/events";
export const MediaEvents = {
export const MediaEvents: typeof StorageEvents = {
...StorageEvents,
};
export * as MediaPermissions from "media/media-permissions";
@@ -104,7 +104,10 @@ export { StorageCloudinaryAdapter } from "media/storage/adapters/cloudinary/Stor
* Data
*/
import { MutatorEvents, RepositoryEvents } from "data/events";
export const DatabaseEvents = { ...MutatorEvents, ...RepositoryEvents };
export const DatabaseEvents: typeof MutatorEvents & typeof RepositoryEvents = {
...MutatorEvents,
...RepositoryEvents,
};
export type {
RepoQuery,
RepoQueryIn,
@@ -1,12 +1,11 @@
import { retry, type TestRunner } from "core/test";
import type { StorageAdapter } from "media/storage/StorageAdapter";
import { randomString } from "bknd/utils";
import type { BunFile } from "bun";
export async function adapterTestSuite(
testRunner: TestRunner,
_adapter: StorageAdapter | (() => StorageAdapter),
file: File | BunFile,
file: File | import("bun").BunFile,
opts?: {
retries?: number;
retryTimeout?: number;
+15
View File
@@ -55,6 +55,21 @@ export class AppServer extends Module<AppServerConfig> {
const origin = this.config.cors.origin ?? "*";
const origins = origin.includes(",") ? origin.split(",").map((o) => o.trim()) : [origin];
const all_origins = origins.includes("*");
// Normalize trailing slashes so that /admin/ matches /admin.
// Hono's app.route doesn't match trailing slashes; host frameworks
// like Nuxt/Nitro redirect /admin -> /admin/. When running standalone
// we emit a 308 ourselves so the route layer sees a clean path without
// re-running the middleware chain (which would re-consume c.req.raw).
this.client.use("*", async (c, next) => {
const url = new URL(c.req.url);
if (url.pathname.length > 1 && url.pathname.endsWith("/")) {
url.pathname = url.pathname.replace(/\/+$/, "");
return c.redirect(url.toString(), 308);
}
return next();
});
this.client.use(
"*",
cors({
+1 -1
View File
@@ -1,6 +1,6 @@
import { MantineProvider } from "@mantine/core";
import { Notifications } from "@mantine/notifications";
import React, { type ReactNode } from "react";
import { type ReactNode } from "react";
import { BkndProvider } from "ui/client/bknd";
import { useTheme, type AppTheme } from "ui/client/use-theme";
import { Logo } from "ui/components/display/Logo";
+3 -2
View File
@@ -102,9 +102,10 @@ body,
/* Chrome, Edge, and Safari */
& *::-webkit-scrollbar {
@apply w-1;
&:horizontal {
@apply h-px;
}
& *::-webkit-scrollbar:horizontal {
@apply h-px;
}
& *::-webkit-scrollbar-track {
+3 -1
View File
@@ -3,8 +3,10 @@
"compilerOptions": {
"outDir": "./dist/types",
"rootDir": "./src",
"declaration": true,
"incremental": true,
"tsBuildInfoFile": "./dist/tsconfig.tsbuildinfo"
},
"include": ["./src/**/*.ts", "./src/**/*.tsx"],
"exclude": ["./node_modules", "./__test__", "./e2e"]
"exclude": ["node_modules", "__test__", "e2e"]
}
+4 -3
View File
@@ -10,7 +10,8 @@
"noImplicitAny": false,
"allowJs": true,
"verbatimModuleSyntax": true,
"declaration": true,
"declaration": false,
"declarationMap": false,
"strict": true,
"allowUnusedLabels": false,
"allowUnreachableCode": false,
@@ -26,7 +27,7 @@
"esModuleInterop": true,
"skipLibCheck": true,
"rootDir": ".",
"outDir": "./dist/types",
"outDir": "./dist",
"paths": {
"*": ["./src/*"],
"bknd": ["./src/index.ts"],
@@ -47,5 +48,5 @@
"__test__",
"e2e/**/*.ts"
],
"exclude": ["node_modules", "dist", "dist/types", "**/*.d.ts"]
"exclude": ["node_modules", "dist", "**/*.d.ts"]
}
+10 -8
View File
@@ -1,13 +1,17 @@
import devServer from "@hono/vite-dev-server";
import react from "@vitejs/plugin-react";
import { defineConfig } from "vite";
import tsconfigPaths from "vite-tsconfig-paths";
import { devServerConfig } from "./src/adapter/vite/dev-server-config";
import tailwindcss from "@tailwindcss/vite";
import tailwindcss from '@tailwindcss/postcss'
import pkg from "./package.json" with { type: "json" };
// https://vitejs.dev/config/
export default defineConfig({
css: {
postcss: {
plugins: [tailwindcss()],
},
},
define: {
__isDev: process.env.NODE_ENV === "production" ? "0" : "1",
__version: JSON.stringify(pkg.version),
@@ -21,23 +25,21 @@ export default defineConfig({
overlay: true,
},
},
resolve: {
tsconfigPaths: true
},
plugins: [
react(),
tsconfigPaths({
// otherwise it'll throw an error because of examples/astro
ignoreConfigErrors: true,
}),
devServer({
...devServerConfig,
entry: "./vite.dev.ts",
//entry: "./vite.dev.code.ts",
}),
tailwindcss(),
],
build: {
manifest: true,
outDir: "./dist/static",
rollupOptions: {
rolldownOptions: {
input: "./src/ui/main.tsx",
},
},
+3 -7
View File
@@ -1,13 +1,9 @@
import { defineConfig } from "vitest/config";
import tsconfigPaths from "vite-tsconfig-paths";
export default defineConfig({
plugins: [
tsconfigPaths({
root: ".",
ignoreConfigErrors: true,
}) as any,
],
resolve: {
tsconfigPaths: true,
},
test: {
include: ["**/*.vi-test.ts", "**/*.vitest.ts"],
},
+1 -1
View File
@@ -35,7 +35,7 @@
"files": {
"includes": [
"**",
"!!**/.tsup",
"!!**/.tsdown",
"!!**/node_modules",
"!!**/.cache",
"!!**/.wrangler",
+1148 -1983
View File
File diff suppressed because it is too large Load Diff
@@ -5,7 +5,6 @@
"astro",
"sveltekit",
"tanstack-start",
"web",
"vite",
"nuxt"
]
@@ -1,277 +0,0 @@
---
title: "Web Adapter"
description: "Bring bknd to any web framework or runtime"
tags: ["documentation"]
---
## What is the Web Adapter?
The web adapter (`bknd/adapter/web`) is a framework/runtime agnostic adapter which can integrate `bknd` into **any** web framework or runtime — even ones without a dedicated adapter.
**Use the web adapter when:**
- Your framework doesn't have a dedicated bknd adapter
- You want full control over your server setup
- You're building a custom server or edge function
**Use a platform-specific adapter when:**
- You're using a supported framework (Next.js, SvelteKit, Nuxt, Astro, etc.)
- You prefer opinionated, zero-config setup
## Installation
<Tabs groupId='package-manager' persist items={[ 'npm', 'pnpm', 'yarn', 'bun']}>
```bash tab="npm"
npm install bknd
```
```bash tab="pnpm"
pnpm install bknd
```
```bash tab="yarn"
yarn add bknd
```
```bash tab="bun"
bun add bknd
```
</Tabs>
## Basic Setup
Start by creating a config file:
```typescript title="bknd.config.ts"
import type { Config } from "bknd/adapter/web";
export default {
connection: {
url: "file:data.db",
},
} satisfies Config<"api">;
```
<Callout type="info" title="The Config type helper">
The `Config` type is a helper type which maps the correct config type based on mode.
Use `Config<"api">` when you are using a React-based framework otherwise use `Config<"standalone">` to serve both the Admin UI and bknd api using the same handler
</Callout>
### Helper Singleton
```typescript title="lib/bknd.ts"
import { createBknd } from "bknd/adapter/web";
import config from "../bknd.config";
export const bknd = createBknd({ mode: "standalone", options: config }, process.env);
// ^ use "api" if using a React-based framework
```
`createBknd` returns three methods:
| Method | Returns | Description |
|--------|---------|-------------|
| `getApp(env: Env)` | `Promise<App>` | The built bknd app instance |
| `getApi({ headers, verify })` | `Promise<Api>` | Convenience wrapper around `app.getApi()` |
| `serve()` | `(req: Request) => Promise<Response>` | A fetch handler for your server |
## Choosing an Admin UI Path
The web adapter supports two approaches to serving the admin UI:
| Path | Best For | How |
|------|----------|-----|
| **Client-side** (default) | React frameworks: Next.js, Astro, React Router, Tanstack Start, Waku | Import and render `<Admin />` in your own React route |
| **Server-side** | Non-React frameworks: SvelteKit, Nuxt, Bun, Node, Deno | The server serves the Admin UI assets and API |
## Path 1: Client-Side Admin (Default)
For React-capable frameworks, render the admin UI directly in a route:
```tsx title="app/admin/[[...admin]]/page.tsx"
import { Admin } from "bknd/ui";
import "bknd/dist/styles.css";
import { bknd } from "@/bknd";
export default async function AdminPage() {
const api = await bknd.getApi({ verify: true });
return (
<Admin
withProvider={{ user: api.getUser() }}
config={{ basepath: "/admin" }}
/>
);
}
```
No server-side admin configuration needed. The admin UI runs entirely in the browser.
## Path 2: Server-Side Admin
For non-React frameworks or standalone servers, enable the admin controller:
```typescript title="src/bknd.ts"
import { createBknd } from "bknd/adapter/web";
import config from "../bknd.config";
export const bknd = createBknd({ mode: "standalone", options: config }, env);
// ^ use "api" if using a React-based framework
```
You'll also need to serve the admin's static assets (JS, CSS). Choose one of three strategies:
### Strategy 1: serveStatic Middleware
**Best for:** Bun, Node, standalone servers with filesystem access
Use Hono's platform-specific `serveStatic` in `bknd.config.ts` to serve assets from `node_modules/bknd/dist/static/`:
```typescript title="bknd.config.ts"
import type { Config } from "bknd/adapter/web";
import { serveStatic } from "hono/bun"; // or "@hono/node-server/serve-static"
export default {
connection: {
url: "file:data.db",
serveStatic: serveStatic({ root: "./node_modules/bknd/dist/static" }),
adminOptions: {
adminBasepath: "/admin",
},
},
} satisfies Config<"standalone">;
```
### Strategy 2: copy-assets Postinstall
**Best for:** SvelteKit, Nuxt, any framework with a static directory
Copy assets at install time and let your framework serve them:
```json title="package.json"
{
"scripts": {
"postinstall": "bknd copy-assets --out static"
}
}
```
Per-framework output paths:
| Framework | `--out` flag |
|-----------|-------------|
| SvelteKit | `--out static` |
| Nuxt | `--out public` |
| Next.js | `--out public` |
| Astro | `--out public` |
| Qwik | `--out public` |
### Strategy 3: serveStaticViaImport
**Best for:** Edge/serverless runtimes (Deno Deploy, Cloudflare Workers)
For environments without filesystem access:
```typescript title="bknd.config.ts"
import type { Config } from "bknd/adapter/web";
import { serveStaticViaImport } from "bknd/adapter";
export default {
connection: {
url: "file:data.db",
serveStatic: serveStaticViaImport(),
adminOptions: {
adminBasepath: "/admin",
},
},
} satisfies Config<"standalone">;
```
## Serving Requests
Example use `bknd.serve()` as a fetch handler in your server:
```typescript title="server.ts"
import { bknd } from "./bknd";
// Bun
Bun.serve({ fetch: bknd.serve(), port: 3000 });
// Node (with @hono/node-server)
import { serve } from "@hono/node-server";
serve({ fetch: bknd.serve(), port: 3000 });
// Next.js
const handler = bknd.serve(); // here you'll use "api" for mode when setting up `bknd` instance
export const GET = handler;
export const POST = handler;
export const PUT = handler;
export const PATCH = handler;
export const DELETE = handler;
// Qwik City (Middleware)
export const onRequest: RequestHandler = async ({
url,
next,
status,
headers,
request,
redirect,
getWritableStream,
}) => {
const pathname = url.pathname;
if (pathname.startsWith("/api") || pathname !== "/") {
const response = await handler(request);
// skips unknown paths
if (response.status === 404) {
await next();
return;
}
// adds the set-cookie header
response.headers.forEach((value, key) => {
headers.set(key, value);
});
// for redirect
if (response.status >= 300 && response.status < 400) {
const location = response.headers.get("location");
if (location) {
throw redirect(response.status as any, location);
}
}
// stream back the body
status(response.status);
if (response.body) {
await response.body?.pipeTo(getWritableStream());
return;
}
}
};
```
Or use `bknd.getApp()` to integrate with an existing Hono or other router setup.
## Reference: Config\<T\>
```typescript
export type AdapterModeWithOptions<Env = Record<string, string | undefined>> =
| {
mode: "standalone";
options: RuntimeBkndConfig<Env>;
}
| {
mode: "api";
options: FrameworkBkndConfig<Env>;
};
export type Config<T extends AdapterModeWithOptions["mode"]> = Extract<
Parameters<typeof createBknd>[0],
{ mode: T }
>['options'];
```
@@ -45,12 +45,6 @@ bknd seamlessly integrates with popular frameworks, allowing you to use what you
href="/integration/nuxt"
/>
<Card
icon={<Icon icon="tabler:world" className="text-fd-primary !size-6" />}
title="Web Adapter"
href="/integration/web"
/>
<Card title="Yours missing?" href="https://github.com/bknd-io/bknd/issues/new">
Create a new issue to request a guide for your framework.
</Card>
@@ -168,12 +168,6 @@ Pick your framework or runtime to get started.
href="/integration/aws"
/>
<Card
icon={<Icon icon="tabler:world" className="text-fd-primary !size-6" />}
title="Web Adapter"
href="/integration/web"
/>
<Card
icon={<Icon icon="simple-icons:vite" className="text-fd-primary !size-6" />}
title="Vite"
+3 -3
View File
@@ -10,11 +10,11 @@
"astro": "astro"
},
"dependencies": {
"@astrojs/check": "^0.9.4",
"@astrojs/react": "^4.2.2",
"@astrojs/check": "^0.9.9",
"@astrojs/react": "^6.0.1",
"@types/react": "^19.0.12",
"@types/react-dom": "^19.0.4",
"astro": "^5.16.9",
"astro": "^7.0.7",
"bknd": "file:../../app",
"react": "^19.1.0",
"react-dom": "^19.1.0",
-1
View File
@@ -1,5 +1,4 @@
---
import Card from "../components/Card.astro";
interface Props {
title: string;
}
-45
View File
@@ -1,45 +0,0 @@
# Build
/dist
/lib
/lib-types
/server
# Development
node_modules
.env
*.local
# Cache
.cache
.mf
.rollup.cache
tsconfig.tsbuildinfo
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
# Editor
.vscode/*
!.vscode/launch.json
!.vscode/*.code-snippets
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
# Yarn
.yarn/*
!.yarn/releases
data.db
public/admin
tmp
-37
View File
@@ -1,37 +0,0 @@
**/*.log
**/.DS_Store
*.
.vscode/settings.json
.history
.yarn
bazel-*
bazel-bin
bazel-out
bazel-qwik
bazel-testlogs
dist
dist-dev
lib
lib-types
etc
external
node_modules
temp
tsc-out
tsdoc-metadata.json
target
output
rollup.config.js
build
.cache
.vscode
.rollup.cache
tsconfig.tsbuildinfo
vite.config.ts
*.spec.tsx
*.spec.ts
.netlify
pnpm-lock.yaml
package-lock.json
yarn.lock
server
-24
View File
@@ -1,24 +0,0 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Launch Chrome",
"request": "launch",
"type": "chrome",
"url": "http://localhost:5173",
"webRoot": "${workspaceFolder}"
},
{
"type": "node",
"name": "dev.debug",
"request": "launch",
"skipFiles": ["<node_internals>/**"],
"cwd": "${workspaceFolder}",
"program": "${workspaceFolder}/node_modules/vite/bin/vite.js",
"args": ["--mode", "ssr", "--force"]
}
]
}
-36
View File
@@ -1,36 +0,0 @@
{
"onRequest": {
"scope": "javascriptreact,typescriptreact",
"prefix": "qonRequest",
"description": "onRequest function for a route index",
"body": [
"export const onRequest: RequestHandler = (request) => {",
" $0",
"};",
],
},
"loader$": {
"scope": "javascriptreact,typescriptreact",
"prefix": "qloader$",
"description": "loader$()",
"body": ["export const $1 = routeLoader$(() => {", " $0", "});"],
},
"action$": {
"scope": "javascriptreact,typescriptreact",
"prefix": "qaction$",
"description": "action$()",
"body": ["export const $1 = routeAction$((data) => {", " $0", "});"],
},
"Full Page": {
"scope": "javascriptreact,typescriptreact",
"prefix": "qpage",
"description": "Simple page component",
"body": [
"import { component$ } from '@builder.io/qwik';",
"",
"export default component$(() => {",
" $0",
"});",
],
},
}
-78
View File
@@ -1,78 +0,0 @@
{
"Qwik component (simple)": {
"scope": "javascriptreact,typescriptreact",
"prefix": "qcomponent$",
"description": "Simple Qwik component",
"body": [
"export const ${1:${TM_FILENAME_BASE/(.*)/${1:/pascalcase}/}} = component$(() => {",
" return <${2:div}>$4</$2>",
"});",
],
},
"Qwik component (props)": {
"scope": "typescriptreact",
"prefix": "qcomponent$ + props",
"description": "Qwik component w/ props",
"body": [
"export interface ${1:${TM_FILENAME_BASE/(.*)/${1:/pascalcase}/}}Props {",
" $2",
"}",
"",
"export const $1 = component$<$1Props>((props) => {",
" const ${2:count} = useSignal(0);",
" return (",
" <${3:div} on${4:Click}$={(ev) => {$5}}>",
" $6",
" </${3}>",
" );",
"});",
],
},
"Qwik signal": {
"scope": "javascriptreact,typescriptreact",
"prefix": "quseSignal",
"description": "useSignal() declaration",
"body": ["const ${1:foo} = useSignal($2);", "$0"],
},
"Qwik store": {
"scope": "javascriptreact,typescriptreact",
"prefix": "quseStore",
"description": "useStore() declaration",
"body": ["const ${1:state} = useStore({", " $2", "});", "$0"],
},
"$ hook": {
"scope": "javascriptreact,typescriptreact",
"prefix": "q$",
"description": "$() function hook",
"body": ["$(() => {", " $0", "});", ""],
},
"useVisibleTask": {
"scope": "javascriptreact,typescriptreact",
"prefix": "quseVisibleTask",
"description": "useVisibleTask$() function hook",
"body": ["useVisibleTask$(({ track }) => {", " $0", "});", ""],
},
"useTask": {
"scope": "javascriptreact,typescriptreact",
"prefix": "quseTask$",
"description": "useTask$() function hook",
"body": [
"useTask$(({ track }) => {",
" track(() => $1);",
" $0",
"});",
"",
],
},
"useResource": {
"scope": "javascriptreact,typescriptreact",
"prefix": "quseResource$",
"description": "useResource$() declaration",
"body": [
"const $1 = useResource$(({ track, cleanup }) => {",
" $0",
"});",
"",
],
},
}
-65
View File
@@ -1,65 +0,0 @@
# Qwik City App ⚡️
- [Qwik Docs](https://qwik.dev/)
- [Discord](https://qwik.dev/chat)
- [Qwik GitHub](https://github.com/QwikDev/qwik)
- [@QwikDev](https://twitter.com/QwikDev)
- [Vite](https://vitejs.dev/)
---
## Project Structure
This project is using Qwik with [QwikCity](https://qwik.dev/qwikcity/overview/). QwikCity is just an extra set of tools on top of Qwik to make it easier to build a full site, including directory-based routing, layouts, and more.
Inside your project, you'll see the following directory structure:
```
├── public/
│ └── ...
└── src/
├── components/
│ └── ...
└── routes/
└── ...
```
- `src/routes`: Provides the directory-based routing, which can include a hierarchy of `layout.tsx` layout files, and an `index.tsx` file as the page. Additionally, `index.ts` files are endpoints. Please see the [routing docs](https://qwik.dev/qwikcity/routing/overview/) for more info.
- `src/components`: Recommended directory for components.
- `public`: Any static assets, like images, can be placed in the public directory. Please see the [Vite public directory](https://vitejs.dev/guide/assets.html#the-public-directory) for more info.
## Add Integrations and deployment
Use the `bun qwik add` command to add additional integrations. Some examples of integrations includes: Cloudflare, Netlify or Express Server, and the [Static Site Generator (SSG)](https://qwik.dev/qwikcity/guides/static-site-generation/).
```shell
bun qwik add # or `bun qwik add`
```
## Development
Development mode uses [Vite's development server](https://vitejs.dev/). The `dev` command will server-side render (SSR) the output during development.
```shell
npm start # or `bun start`
```
> Note: during dev mode, Vite may request a significant number of `.js` files. This does not represent a Qwik production build.
## Preview
The preview command will create a production build of the client modules, a production build of `src/entry.preview.tsx`, and run a local server. The preview server is only for convenience to preview a production build locally and should not be used as a production server.
```shell
bun preview # or `bun preview`
```
## Production
The production build will generate client and server modules by running both client and server build commands. The build command will use Typescript to run a type check on the source code.
```shell
bun build # or `bun build`
```
-60
View File
@@ -1,60 +0,0 @@
import { em, entity, text, boolean } from "bknd";
import { registerLocalMediaAdapter } from "bknd/adapter/node";
import type { Config } from "bknd/adapter/web";
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 "bknd" {
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 bknd", done: true },
{ title: "Build something cool", done: false },
]);
// and create a user
await ctx.app.module.auth.createUser({
email: "test@bknd.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",
}),
},
},
adminOptions: {
adminBasepath: "/admin",
assetsPath: "/admin/",
logoReturnPath: "../..",
},
} satisfies Config<"standalone">;
-73
View File
@@ -1,73 +0,0 @@
import js from "@eslint/js";
import globals from "globals";
import tseslint from "typescript-eslint";
import { globalIgnores } from "eslint/config";
import { qwikEslint9Plugin } from "eslint-plugin-qwik";
const ignores = [
"**/*.log",
"**/.DS_Store",
"**/*.",
".vscode/settings.json",
"**/.history",
"**/.yarn",
"**/bazel-*",
"**/bazel-bin",
"**/bazel-out",
"**/bazel-qwik",
"**/bazel-testlogs",
"**/dist",
"**/dist-dev",
"**/lib",
"**/lib-types",
"**/etc",
"**/external",
"**/node_modules",
"**/temp",
"**/tsc-out",
"**/tsdoc-metadata.json",
"**/target",
"**/output",
"**/rollup.config.js",
"**/build",
"**/.cache",
"**/.vscode",
"**/.rollup.cache",
"**/dist",
"**/tsconfig.tsbuildinfo",
"**/vite.config.ts",
"**/*.spec.tsx",
"**/*.spec.ts",
"**/.netlify",
"**/pnpm-lock.yaml",
"**/package-lock.json",
"**/yarn.lock",
"**/server",
"eslint.config.js",
];
export default tseslint.config(
globalIgnores(ignores),
js.configs.recommended,
tseslint.configs.recommended,
qwikEslint9Plugin.configs.recommended,
{
languageOptions: {
globals: {
...globals.browser,
...globals.node,
...globals.es2021,
...globals.serviceworker,
},
parserOptions: {
projectService: true,
tsconfigRootDir: import.meta.dirname,
},
},
},
{
rules: {
"@typescript-eslint/no-explicit-any": "off",
},
},
);
-47
View File
@@ -1,47 +0,0 @@
{
"name": "my-qwik-empty-starter",
"description": "Blank project with routing included",
"engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
},
"engines-annotation": "Mostly required by sharp which needs a Node-API v9 compatible runtime",
"private": true,
"type": "module",
"scripts": {
"build": "qwik build",
"build.client": "vite build",
"build.preview": "vite build --ssr src/entry.preview.tsx",
"build.types": "tsc --incremental --noEmit",
"deploy": "echo 'Run \"npm run qwik add\" to install a server adapter'",
"dev": "vite --mode ssr",
"dev.debug": "node --inspect-brk ./node_modules/vite/bin/vite.js --mode ssr --force",
"fmt": "prettier --write .",
"fmt.check": "prettier --check .",
"lint": "eslint \"src/**/*.ts*\"",
"preview": "qwik build preview && vite preview --open",
"start": "vite --open --mode ssr",
"qwik": "qwik",
"typegen": "bunx tsx node_modules/.bin/bknd types --outfile bknd-types.d.ts",
"postinstall": "bun run bknd copy-assets --out public/admin"
},
"dependencies": {
"@tailwindcss/vite": "^4.2.2",
"bknd": "file:../../app"
},
"devDependencies": {
"@builder.io/qwik": "^1.19.2",
"@builder.io/qwik-city": "^1.19.2",
"@eslint/js": "^9",
"@types/node": "20.19.0",
"eslint": "9.32.0",
"eslint-plugin-qwik": "^1.19.2",
"globals": "16.4.0",
"prettier": "3.6.2",
"typescript": "5.4.5",
"typescript-eslint": "8.38.0",
"typescript-plugin-css-modules": "latest",
"undici": "*",
"vite": "7.3.1",
"vite-tsconfig-paths": "^4.2.1"
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

-14
View File
@@ -1,14 +0,0 @@
<svg
width="578"
height="188"
viewBox="0 0 578 188"
fill="black"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M41.5 34C37.0817 34 33.5 37.5817 33.5 42V146C33.5 150.418 37.0817 154 41.5 154H158.5C162.918 154 166.5 150.418 166.5 146V42C166.5 37.5817 162.918 34 158.5 34H41.5ZM123.434 113.942C124.126 111.752 124.5 109.42 124.5 107C124.5 94.2975 114.203 84 101.5 84C99.1907 84 96.9608 84.3403 94.8579 84.9736L87.2208 65.1172C90.9181 63.4922 93.5 59.7976 93.5 55.5C93.5 49.701 88.799 45 83 45C77.201 45 72.5 49.701 72.5 55.5C72.5 61.299 77.201 66 83 66C83.4453 66 83.8841 65.9723 84.3148 65.9185L92.0483 86.0256C87.1368 88.2423 83.1434 92.1335 80.7957 96.9714L65.4253 91.1648C65.4746 90.7835 65.5 90.3947 65.5 90C65.5 85.0294 61.4706 81 56.5 81C51.5294 81 47.5 85.0294 47.5 90C47.5 94.9706 51.5294 99 56.5 99C60.0181 99 63.0648 96.9814 64.5449 94.0392L79.6655 99.7514C78.9094 102.03 78.5 104.467 78.5 107C78.5 110.387 79.2321 113.603 80.5466 116.498L69.0273 123.731C67.1012 121.449 64.2199 120 61 120C55.201 120 50.5 124.701 50.5 130.5C50.5 136.299 55.201 141 61 141C66.799 141 71.5 136.299 71.5 130.5C71.5 128.997 71.1844 127.569 70.6158 126.276L81.9667 119.149C86.0275 125.664 93.2574 130 101.5 130C110.722 130 118.677 124.572 122.343 116.737L132.747 120.899C132.585 121.573 132.5 122.276 132.5 123C132.5 127.971 136.529 132 141.5 132C146.471 132 150.5 127.971 150.5 123C150.5 118.029 146.471 114 141.5 114C138.32 114 135.525 115.649 133.925 118.139L123.434 113.942Z"
/>
<path d="M243.9 151.5C240.4 151.5 237 151 233.7 150C230.4 149 227.4 147.65 224.7 145.95C222 144.15 219.75 142.15 217.95 139.95C216.15 137.65 215 135.3 214.5 132.9L219.3 131.1L218.25 149.7H198.15V39H219.45V89.25L215.4 87.6C216 85.2 217.15 82.9 218.85 80.7C220.55 78.4 222.7 76.4 225.3 74.7C227.9 72.9 230.75 71.5 233.85 70.5C236.95 69.5 240.15 69 243.45 69C250.35 69 256.5 70.8 261.9 74.4C267.3 77.9 271.55 82.75 274.65 88.95C277.85 95.15 279.45 102.25 279.45 110.25C279.45 118.25 277.9 125.35 274.8 131.55C271.7 137.75 267.45 142.65 262.05 146.25C256.75 149.75 250.7 151.5 243.9 151.5ZM238.8 133.35C242.8 133.35 246.25 132.4 249.15 130.5C252.15 128.5 254.5 125.8 256.2 122.4C257.9 118.9 258.75 114.85 258.75 110.25C258.75 105.75 257.9 101.75 256.2 98.25C254.6 94.75 252.3 92.05 249.3 90.15C246.3 88.25 242.8 87.3 238.8 87.3C234.8 87.3 231.3 88.25 228.3 90.15C225.3 92.05 222.95 94.75 221.25 98.25C219.55 101.75 218.7 105.75 218.7 110.25C218.7 114.85 219.55 118.9 221.25 122.4C222.95 125.8 225.3 128.5 228.3 130.5C231.3 132.4 234.8 133.35 238.8 133.35ZM308.312 126.15L302.012 108.6L339.512 70.65H367.562L308.312 126.15ZM288.062 150V39H309.362V150H288.062ZM341.762 150L313.262 114.15L328.262 102.15L367.412 150H341.762ZM371.675 150V70.65H392.075L392.675 86.85L388.475 88.65C389.575 85.05 391.525 81.8 394.325 78.9C397.225 75.9 400.675 73.5 404.675 71.7C408.675 69.9 412.875 69 417.275 69C423.275 69 428.275 70.2 432.275 72.6C436.375 75 439.425 78.65 441.425 83.55C443.525 88.35 444.575 94.3 444.575 101.4V150H423.275V103.05C423.275 99.45 422.775 96.45 421.775 94.05C420.775 91.65 419.225 89.9 417.125 88.8C415.125 87.6 412.625 87.1 409.625 87.3C407.225 87.3 404.975 87.7 402.875 88.5C400.875 89.2 399.125 90.25 397.625 91.65C396.225 93.05 395.075 94.65 394.175 96.45C393.375 98.25 392.975 100.2 392.975 102.3V150H382.475C380.175 150 378.125 150 376.325 150C374.525 150 372.975 150 371.675 150ZM488.536 151.5C481.636 151.5 475.436 149.75 469.936 146.25C464.436 142.65 460.086 137.8 456.886 131.7C453.786 125.5 452.236 118.35 452.236 110.25C452.236 102.35 453.786 95.3 456.886 89.1C460.086 82.9 464.386 78 469.786 74.4C475.286 70.8 481.536 69 488.536 69C492.236 69 495.786 69.6 499.186 70.8C502.686 71.9 505.786 73.45 508.486 75.45C511.286 77.45 513.536 79.7 515.236 82.2C516.936 84.6 517.886 87.15 518.086 89.85L512.686 90.75V39H533.986V150H513.886L512.986 131.7L517.186 132.15C516.986 134.65 516.086 137.05 514.486 139.35C512.886 141.65 510.736 143.75 508.036 145.65C505.436 147.45 502.436 148.9 499.036 150C495.736 151 492.236 151.5 488.536 151.5ZM493.336 133.8C497.336 133.8 500.836 132.8 503.836 130.8C506.836 128.8 509.186 126.05 510.886 122.55C512.586 119.05 513.436 114.95 513.436 110.25C513.436 105.65 512.586 101.6 510.886 98.1C509.186 94.5 506.836 91.75 503.836 89.85C500.836 87.85 497.336 86.85 493.336 86.85C489.336 86.85 485.836 87.85 482.836 89.85C479.936 91.75 477.636 94.5 475.936 98.1C474.336 101.6 473.536 105.65 473.536 110.25C473.536 114.95 474.336 119.05 475.936 122.55C477.636 126.05 479.936 128.8 482.836 130.8C485.836 132.8 489.336 133.8 493.336 133.8Z" />
</svg>

Before

Width:  |  Height:  |  Size: 4.5 KiB

-1
View File
@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 500 500"><g clip-path="url(#a)"><circle cx="250" cy="250" r="250" fill="#fff"/><path fill="#18B6F6" d="m367.87 418.45-61.17-61.18-.94.13v-.67L175.7 227.53l32.05-31.13L188.9 87.73 99.56 199.09c-15.22 15.42-18.03 40.51-7.08 59.03l55.83 93.11a46.82 46.82 0 0 0 40.73 22.81l27.65-.27 151.18 44.68Z"/><path fill="#AC7EF4" d="m401.25 196.94-12.29-22.81-6.41-11.67-2.54-4.56-.26.26-33.66-58.63a47.07 47.07 0 0 0-41.27-23.75l-29.51.8-88.01.28a47.07 47.07 0 0 0-40.33 23.34L93.4 207l95.76-119.54L314.7 226.19l-22.3 22.67 13.35 108.54.13-.26v.26h-.26l.26.27 10.42 10.2 50.62 49.78c2.13 2 5.6-.4 4.13-2.96l-31.25-61.85 54.5-101.3 1.73-2c.67-.81 1.33-1.62 1.87-2.42a46.8 46.8 0 0 0 3.34-50.18Z"/><path fill="#fff" d="M315.1 225.65 189.18 87.6l17.9 108.14L175 227l130.5 130.27-11.75-108.14 21.37-23.48Z"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h500v500H0z"/></clipPath></defs></svg>

Before

Width:  |  Height:  |  Size: 947 B

-1
View File
@@ -1 +0,0 @@
<svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>

Before

Width:  |  Height:  |  Size: 391 B

-1
View File
@@ -1 +0,0 @@
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

-9
View File
@@ -1,9 +0,0 @@
{
"$schema": "https://json.schemastore.org/web-manifest-combined.json",
"name": "qwik-project-name",
"short_name": "Welcome to Qwik",
"start_url": ".",
"display": "standalone",
"background_color": "#fff",
"description": "A Qwik project app."
}
View File
-1
View File
@@ -1 +0,0 @@
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>

Before

Width:  |  Height:  |  Size: 385 B

-4
View File
@@ -1,4 +0,0 @@
// This file can be used to add references for global types like `vite/client`.
// Add global `vite/client` types. For more info, see: https://vitejs.dev/guide/features#client-types
/// <reference types="vite/client" />
-36
View File
@@ -1,36 +0,0 @@
import { Link } from "@builder.io/qwik-city";
import { component$ } from "@builder.io/qwik";
import { useLocation } from "@builder.io/qwik-city";
export default component$(() => {
const loc = useLocation();
return (
<footer class="row-start-3 flex gap-6 flex-wrap items-center justify-center">
<Link
class="flex items-center gap-2 hover:underline hover:underline-offset-4"
href={loc.url.pathname === '/' ? '/user' : '/'}
>
<img aria-hidden src="/file.svg" alt="File icon" width="16" height="16" />
{loc.url.pathname === "/" ? "User" : "Home"}
</Link>
<Link
class="flex items-center gap-2 hover:underline hover:underline-offset-4"
href="/admin/data"
>
<img aria-hidden src="/window.svg" alt="Window icon" width="16" height="16" />
Admin
</Link>
<a
class="flex items-center gap-2 hover:underline hover:underline-offset-4"
href="https://bknd.io"
target="_blank"
rel="noopener noreferrer"
>
<img aria-hidden src="/globe.svg" alt="Globe icon" width="16" height="16" />
Go to bknd.io →
</a>
</footer>
);
});
-17
View File
@@ -1,17 +0,0 @@
import { component$ } from "@builder.io/qwik";
interface Props {
items: string[];
}
export default component$<Props>(({ items }) => {
return (
<ol class="list-inside list-decimal text-sm text-center sm:text-left w-full text-center">
{items.map((item, i) => (
<li key={i} class={`${i < items.length - 1 ? "mb-2" : ""}`}>
<span>{item}</span>
</li>
))}
</ol>
);
});
-17
View File
@@ -1,17 +0,0 @@
/*
* WHAT IS THIS FILE?
*
* Development entry point using only client-side modules:
* - Do not use this mode in production!
* - No SSR
* - No portion of the application is pre-rendered on the server.
* - All of the application is running eagerly in the browser.
* - More code is transferred to the browser than in SSR mode.
* - Optimizer/Serialization/Deserialization code is not exercised!
*/
import { render, type RenderOptions } from "@builder.io/qwik";
import Root from "./root";
export default function (opts: RenderOptions) {
return render(document, <Root />, opts);
}
-21
View File
@@ -1,21 +0,0 @@
/*
* WHAT IS THIS FILE?
*
* It's the bundle entry point for `npm run preview`.
* That is, serving your app built in production mode.
*
* Feel free to modify this file, but don't remove it!
*
* Learn more about Vite's preview command:
* - https://vitejs.dev/config/preview-options.html#preview-options
*
*/
import { createQwikCity } from "@builder.io/qwik-city/middleware/node";
import qwikCityPlan from "@qwik-city-plan";
// make sure qwikCityPlan is imported before entry
import render from "./entry.ssr";
/**
* The default export is the QwikCity adapter used by Vite preview.
*/
export default createQwikCity({ render, qwikCityPlan });
-31
View File
@@ -1,31 +0,0 @@
/**
* WHAT IS THIS FILE?
*
* SSR entry point, in all cases the application is rendered outside the browser, this
* entry point will be the common one.
*
* - Server (express, cloudflare...)
* - npm run start
* - npm run preview
* - npm run build
*
*/
import {
renderToStream,
type RenderToStreamOptions,
} from "@builder.io/qwik/server";
import Root from "./root";
export default function (opts: RenderToStreamOptions) {
return renderToStream(<Root />, {
...opts,
// Use container attributes to set attributes on the html tag.
containerAttributes: {
lang: "en-us",
...opts.containerAttributes,
},
serverData: {
...opts.serverData,
},
});
}
-23
View File
@@ -1,23 +0,0 @@
@import "tailwindcss";
:root {
--background: #ffffff;
--foreground: #171717;
}
@media (prefers-color-scheme: dark) {
:root {
--background: #0a0a0a;
--foreground: #ededed;
}
}
@theme {
--color-background: var(--background);
--color-foreground: var(--foreground);
}
body {
@apply bg-background text-foreground;
font-family: Arial, Helvetica, sans-serif;
}
-13
View File
@@ -1,13 +0,0 @@
import { createBknd } from "bknd/adapter/web";
import bkndConfig from "../../bknd.config";
import type { EnvGetter } from "@builder.io/qwik-city/middleware/request-handler";
export const getApp = async (env?: EnvGetter) => createBknd({ mode: "standalone", options: bkndConfig }, env);
export const handler = async (req: Request, env?: EnvGetter) => {
return (await getApp(env)).serve()(req);
};
export const getApi = async (opts?: { headers?: Headers; verify?: boolean }) => {
return (await getApp()).getApi(opts);
};
-77
View File
@@ -1,77 +0,0 @@
import { component$, isDev } from "@builder.io/qwik";
import { QwikCityProvider, RouterOutlet } from "@builder.io/qwik-city";
import { useDocumentHead, useLocation } from "@builder.io/qwik-city";
import "./global.css";
export default component$(() => {
/**
* The root of a QwikCity site always start with the <QwikCityProvider> component,
* immediately followed by the document's <head> and <body>.
*
* Don't remove the `<head>` and `<body>` elements.
*/
return (
<QwikCityProvider>
<head>
<meta charset="utf-8" />
{!isDev && (
<link
rel="manifest"
href={`${import.meta.env.BASE_URL}manifest.json`}
/>
)}
<RouterHead />
</head>
<body lang="en">
<RouterOutlet />
</body>
</QwikCityProvider>
);
});
export const RouterHead = component$(() => {
const head = useDocumentHead();
const loc = useLocation();
return (
<>
<title>{head.title}</title>
<link rel="canonical" href={loc.url.href} />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
{head.meta.map((m) => (
<meta key={m.key} {...m} />
))}
{head.links.map((l) => (
<link key={l.key} {...l} />
))}
{head.styles.map((s) => (
<style
key={s.key}
{...s.props}
{...(s.props?.dangerouslySetInnerHTML
? {}
: { dangerouslySetInnerHTML: s.style })}
/>
))}
{head.scripts.map((s) => (
<script
key={s.key}
{...s.props}
{...(s.props?.dangerouslySetInnerHTML
? {}
: { dangerouslySetInnerHTML: s.script })}
/>
))}
</>
);
});
-132
View File
@@ -1,132 +0,0 @@
import { component$ } from "@builder.io/qwik";
import { Form, routeAction$, routeLoader$, type DocumentHead } from "@builder.io/qwik-city";
import Footer from "~/components/footer";
import List from "~/components/list";
import { getApi } from "~/lib/bknd";
interface Todo {
title: string | undefined;
done: boolean | undefined;
id: number;
}
export const useGetTodos = routeLoader$(async () => {
const api = await getApi();
const limit = 5;
const todos = await api.data.readMany("todos", { limit, sort: "-id" });
const total = todos.body.meta.total as number;
return { total, todos, limit };
});
export const useCompleteTodo = routeAction$(async (data) => {
const api = await getApi();
const todo = data as unknown as Todo;
await api.data.updateOne("todos", todo.id, {
done: !todo.done,
});
});
export const useDeleteTodo = routeAction$(async (data) => {
const api = await getApi();
const todo = data as unknown as Todo;
await api.data.deleteOne("todos", todo.id);
});
export const useAddTodo = routeAction$(async (data) => {
const api = await getApi();
await api.data.createOne("todos", { title: data.title as string });
});
export default component$(() => {
const {
value: { limit, todos, total },
} = useGetTodos();
const addTodoAction = useAddTodo();
const completeTodoAction = useCompleteTodo();
const deleteTodoAction = useDeleteTodo();
return (
<div class="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20">
<main class="flex flex-col gap-8 row-start-2 items-center sm:items-start">
<div class="flex flex-row items-center justify-evenly min-w-full">
<img class="size-24" src="/favicon.svg" alt="Qwik logo" width="72" height="72" />
<div class="ml-3.5 mr-2 font-mono opacity-70">&amp;</div>
<img class="dark:invert" src="/bknd.svg" alt="bknd logo" width="183" height="59" />
</div>
<List
items={[
"Get started with a full backend.",
"Focus on what matters instead of repetition.",
]}
/>
<div class="flex flex-col border border-black/15 dark:border-white/15 w-full py-4 px-5 gap-2">
<h2 class="font-mono mb-1 opacity-70">
<code>What's next?</code>
</h2>
<div class="flex flex-col w-full gap-2">
{total > limit && (
<div class="bg-foreground/10 flex justify-center p-1 text-xs rounded text-foreground/40">
{total - limit} more todo(s) hidden
</div>
)}
<div class="flex flex-col gap-3">
{todos.map((todo) => (
<div key={String(todo.id)} class="flex flex-row">
<div class="flex flex-row grow items-center gap-3 ml-1">
<input
type="checkbox"
class="shrink-0 cursor-pointer"
checked={Boolean(todo.done)}
onChange$={async() => {
await completeTodoAction.submit(todo);
}}
/>
<div class="text-foreground/90 leading-none">{todo.title}</div>
</div>
<button
type="button"
class="cursor-pointer grayscale transition-all hover:grayscale-0 text-xs"
onclick$={async () => { await deleteTodoAction.submit(todo); }}
>
❌
</button>
</div>
))}
</div>
<Form
class="flex flex-row w-full gap-3 mt-2"
key={todos.map((t) => t.id).join()}
action={addTodoAction}
>
<input
type="text"
name="title"
placeholder="New todo"
class="py-2 px-4 flex grow rounded-sm bg-black/5 focus:bg-black/10 dark:bg-white/5 dark:focus:bg-white/10 transition-colors outline-none"
/>
<button type="submit" class="cursor-pointer">
Add
</button>
</Form>
</div>
</div>
</main>
<Footer />
</div>
);
});
export const head: DocumentHead = {
title: "Qwik City 🤝 Bknd.io",
meta: [
{
name: "description",
content: "Qwik site with bknd",
},
],
};
-45
View File
@@ -1,45 +0,0 @@
import type { RequestHandler } from "@builder.io/qwik-city";
import { handler } from "~/lib/bknd";
export const onRequest: RequestHandler = async ({
env,
url,
next,
status,
headers,
request,
redirect,
getWritableStream,
}) => {
const pathname = url.pathname;
if (pathname.startsWith("/api") || pathname !== "/") {
const response = await handler(request, env);
// skips unknown paths
if (response.status === 404) {
await next();
return;
}
// adds the set-cookie header
response.headers.forEach((value, key) => {
headers.set(key, value);
});
// for redirect
if (response.status >= 300 && response.status < 400) {
const location = response.headers.get("location");
if (location) {
throw redirect(response.status as any, location);
}
}
// stream back the body
status(response.status);
if (response.body) {
await response.body?.pipeTo(getWritableStream());
return;
}
}
};
-95
View File
@@ -1,95 +0,0 @@
import { component$ } from "@builder.io/qwik";
import { Link, routeLoader$ } from "@builder.io/qwik-city";
import List from "~/components/list";
import { getApi } from "~/lib/bknd";
import Footer from "~/components/footer";
export const useGetTodos = routeLoader$(async ({ request }) => {
const api = await getApi({ verify: true, headers: request.headers });
const limit = 5;
const todos = await api.data.readMany("todos", { limit, sort: "-id" });
const total = todos.body.meta.total as number;
const user = api.getUser();
return { total, todos, limit, user };
});
export default component$(() => {
const {
value: { todos, user },
} = useGetTodos();
return (
<div class="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20">
<main class="flex flex-col gap-8 row-start-2 items-center sm:items-start">
<div class="flex flex-row items-center ">
<img
class="dark:invert size-18"
src="/favicon.svg"
alt="Qwik logo"
height={72}
width={72}
/>
<div class="ml-3.5 mr-2 font-mono opacity-70">&amp;</div>
<img class="dark:invert" src="/bknd.svg" alt="bknd logo" width={183} height={59} />
</div>
<List items={todos.map((todo) => todo.title ?? "")} />
<Buttons />
<div>
{user ? (
<>
Logged in as {user.email}.
<Link class="font-medium underline" href={"/api/auth/logout" as string}>
Logout
</Link>
</>
) : (
<div class="flex flex-col gap-1">
<p>
Not logged in.
<Link class="font-medium underline" href={"/admin/auth/login" as string}>
Login
</Link>
</p>
<p class="text-xs opacity-50">
Sign in with:
<b>
<code>test@bknd.io</code>
</b>
/
<b>
<code>12345678</code>
</b>
</p>
</div>
)}
</div>
</main>
<Footer />
</div>
);
});
function Buttons() {
return (
<div class="flex gap-4 items-center flex-col sm:flex-row">
<a
class="rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-foreground gap-2 text-white hover:bg-[#383838] dark:hover:bg-[#ccc] text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5"
href="https://bknd.io/"
target="_blank"
rel="noopener noreferrer"
>
<img class="grayscale" src="/bknd.ico" alt="bknd logomark" width={20} height={20} />
Go To Bknd.io
</a>
<a
class="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:min-w-44"
href="https://docs.bknd.io/integration/meta-framework"
target="_blank"
rel="noopener noreferrer"
>
Read our docs
</a>
</div>
);
}
-26
View File
@@ -1,26 +0,0 @@
{
"compilerOptions": {
"allowJs": true,
"target": "ES2020",
"module": "ES2022",
"lib": ["es2022", "DOM", "WebWorker", "DOM.Iterable"],
"jsx": "react-jsx",
"jsxImportSource": "@builder.io/qwik",
"strict": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"moduleResolution": "Bundler",
"esModuleInterop": true,
"skipLibCheck": true,
"incremental": true,
"isolatedModules": true,
"outDir": "tmp",
"noEmit": true,
"paths": {
"~/*": ["./src/*"]
},
/* if you do not use CSS modules, remove this line and delete the typescript-plugin-css-modules module from package.json */
"plugins": [{ "name": "typescript-plugin-css-modules" }]
},
"include": ["src", "./*.d.ts", "./*.config.ts"]
}
-29
View File
@@ -1,29 +0,0 @@
import { defineConfig, type UserConfig } from "vite";
import { qwikVite } from "@builder.io/qwik/optimizer";
import { qwikCity } from "@builder.io/qwik-city/vite";
import tsconfigPaths from "vite-tsconfig-paths";
import tailwindcss from '@tailwindcss/vite'
export default defineConfig((): UserConfig => {
return {
plugins: [
tailwindcss(),
qwikCity({
trailingSlash: false
}),
qwikVite(),
tsconfigPaths({ root: "." }),
],
server: {
headers: {
"Cache-Control": "public, max-age=0",
},
},
preview: {
headers: {
"Cache-Control": "public, max-age=600",
},
},
};
});
+2 -2
View File
@@ -5,7 +5,7 @@
"type": "module",
"packageManager": "bun@1.3.3",
"engines": {
"node": ">=22.13"
"node": ">=22.18"
},
"scripts": {
"updater": "bun x npm-check-updates -ui",
@@ -18,7 +18,7 @@
"devDependencies": {
"@biomejs/biome": "2.3.3",
"@tsconfig/strictest": "^2.0.8",
"@types/bun": "^1.3.1",
"@types/bun": "^1.3.13",
"@types/lodash-es": "^4.17.12",
"miniflare": "^3.20250718.2",
"typescript": "^6.0.2",
+3 -2
View File
@@ -12,13 +12,14 @@
"README.md"
],
"dependencies": {
"@libsql/client": "^0.14.0",
"@libsql/client": "^0.17.2",
"hono": "^4.6.7"
},
"tsup": {
"tsdown": {
"entry": [
"src/index.ts"
],
"dts": false,
"minify": true,
"outDir": "dist",
"format": [
+7 -5
View File
@@ -4,14 +4,15 @@
"type": "module",
"sideEffects": false,
"scripts": {
"build": "rm -rf dist && bun tsup && bun run build:types",
"watch": "bun tsup --watch --onSuccess 'bun run build:types'",
"build:only": "rm -rf dist && bun tsup",
"build": "rm -rf dist && bun tsdown && bun run build:types",
"watch": "bun tsdown --watch --onSuccess 'bun run build:types'",
"build:only": "rm -rf dist && bun tsdown",
"types": "bun tsc -p tsconfig.json --noEmit --skipLibCheck",
"build:types": "bun tsc --emitDeclarationOnly",
"updater": "bun x npm-check-updates -ui",
"pack": "rm -rf *.tgz && npm pack && mv *.tgz latest.tgz",
"prepublishOnly": "bun run build"
"prepublishOnly": "bun run build",
"tsdown": "bunx tsdown"
},
"publishConfig": {
"access": "public"
@@ -33,11 +34,12 @@
"optionalDependencies": {
"@plasmicapp/loader-nextjs": "^1.0.0"
},
"tsup": {
"tsdown": {
"entry": [
"src/index.ts",
"src/nextjs/index.tsx"
],
"dts": false,
"minify": true,
"clean": true,
"external": [