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
29 changed files with 1741 additions and 2501 deletions
+3 -3
View File
@@ -25,15 +25,15 @@ jobs:
--health-retries 5 --health-retries 5
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v7
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v4 uses: actions/setup-node@v7
with: with:
node-version: "22.x" node-version: "22.x"
- name: Setup Bun - name: Setup Bun
uses: oven-sh/setup-bun@v1 uses: oven-sh/setup-bun@v2
with: with:
bun-version: "1.3.3" bun-version: "1.3.3"
+2 -2
View File
@@ -107,7 +107,7 @@ bknd/
index.ts # Main package exports index.ts # Main package exports
__test__/ # Unit tests (mirrors src/ structure) __test__/ # Unit tests (mirrors src/ structure)
e2e/ # End-to-end tests (Playwright) e2e/ # End-to-end tests (Playwright)
build.ts # Build script (tsup/esbuild) build.ts # Build script (tsdown/rolldown)
build.cli.ts # CLI build script build.cli.ts # CLI build script
packages/ # Small satellite packages packages/ # Small satellite packages
cli/ # Standalone CLI package (bknd-cli) 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 ### 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 - **API** -- the core backend
- **UI** -- the admin interface - **UI** -- the admin interface
+1 -13
View File
@@ -1,6 +1,6 @@
import { describe, expect, test } from "bun:test"; import { describe, expect, test } from "bun:test";
import { Entity } from "data/entities"; import { Entity } from "data/entities";
import { NumberField, TextField, PrimaryField } from "data/fields"; import { NumberField, TextField } from "data/fields";
describe("[data] Entity", async () => { describe("[data] Entity", async () => {
const entity = new Entity("test", [ const entity = new Entity("test", [
@@ -47,16 +47,4 @@ describe("[data] Entity", async () => {
entity.addField(field); entity.addField(field);
expect(entity.getField("new_field")).toBe(field); expect(entity.getField("new_field")).toBe(field);
}); });
test("custom primary", async () => {
const customPrimaryEntity = new Entity("custom", [
new PrimaryField("custom", { format: "uuid" }),
new TextField("name"),
]);
expect(customPrimaryEntity.getPrimaryField().name).toEqual("custom");
expect(customPrimaryEntity.getPrimaryField().format).toEqual("uuid");
expect(customPrimaryEntity.getSelect()).toEqual(["custom", "name"]);
expect(customPrimaryEntity.getField("id")).toBeUndefined(); // no auto id
});
}); });
@@ -2,7 +2,7 @@ import { describe, expect, test } from "bun:test";
import { PrimaryField } from "data/fields"; import { PrimaryField } from "data/fields";
describe("[data] PrimaryField", async () => { describe("[data] PrimaryField", async () => {
const field = new PrimaryField("primary", { fillable: false }); const field = new PrimaryField("primary");
test("name", async () => { test("name", async () => {
expect(field.name).toBe("primary"); expect(field.name).toBe("primary");
@@ -64,23 +64,3 @@ describe("[data] PrimaryField", async () => {
}); });
}); });
}); });
describe("[data] PrimaryField (fillable)", async () => {
const field = new PrimaryField("primary", { fillable: true });
test("isFillable", async () => {
expect(field.isFillable()).toBe(true);
});
test("transformPersist/Retrieve", async () => {
expect(field.transformPersist(null)).resolves.toBeUndefined();
expect(field.transformRetrieve(1)).toBe(1);
});
test("format", () => {
const uuid = new PrimaryField("integer", { format: "uuid" });
expect(uuid.transformPersist(null)).toBeDefined();
});
});
+349 -328
View File
@@ -1,9 +1,9 @@
import { $ } from "bun"; import { build, type UserConfig } from "tsdown";
import * as tsup from "tsup";
import pkg from "./package.json" with { type: "json" }; import pkg from "./package.json" with { type: "json" };
import c from "picocolors"; import c from "picocolors";
import { watch as fsWatch, readdirSync, rmSync } from "node:fs"; 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 args = process.argv.slice(2);
const watch = args.includes("--watch"); const watch = args.includes("--watch");
@@ -11,18 +11,19 @@ const minify = args.includes("--minify");
const types = args.includes("--types"); const types = args.includes("--types");
const sourcemap = args.includes("--sourcemap"); const sourcemap = args.includes("--sourcemap");
const clean = args.includes("--clean"); const clean = args.includes("--clean");
const dev = args.includes("--dev");
// silence tsup // silence tsdown
const oldConsole = { const oldConsole = {
log: console.log, log: console.log,
warn: console.warn, warn: console.warn,
}; };
console.log = () => {}; console.log = () => { };
console.warn = () => {}; console.warn = () => { };
const define = { const define = {
__isDev: "0", __isDev: "0",
__version: JSON.stringify(pkg.version), __version: JSON.stringify(pkg.version),
}; };
if (clean) { if (clean) {
@@ -36,156 +37,167 @@ if (clean) {
const entryPath = join(distPath, entry); const entryPath = join(distPath, entry);
rmSync(entryPath, { recursive: true, force: true }); rmSync(entryPath, { recursive: true, force: true });
} }
} catch (e) { } catch {
// dist may not exist yet, ignore // dist may not exist yet, ignore
} }
} }
let types_running = false; let types_running = false;
function buildTypes() { async function buildTypes() {
if (types_running || !types) return; if (types_running || !types) return;
types_running = true; types_running = true;
Bun.spawn(["bun", "build:types"], { try {
stdout: "inherit", await runTypesBuild();
onExit: () => {
oldConsole.log(c.cyan("[Types]"), c.green("built")); oldConsole.log(c.cyan("[Types]"), c.green("built"));
Bun.spawn(["bun", "tsc-alias"], { } finally {
stdout: "inherit", types_running = false;
onExit: () => { }
oldConsole.log(c.cyan("[Types]"), c.green("aliased"));
types_running = false;
},
});
},
});
}
if (types && !watch) {
buildTypes();
} }
let watcher_timeout: any; let watcher_timeout: any;
function delayTypes() { function delayTypes(delayTime = 1000) {
if (!watch || !types) return; if (!watch || !types) return;
if (watcher_timeout) { if (watcher_timeout) {
clearTimeout(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); const dependencies = Object.keys(pkg.dependencies);
// collection of always-external packages // collection of always-neverBundle packages
const external = [ const neverBundle = [
...dependencies, ...dependencies,
"bun:test", "bun:test",
"node:test", "node:test",
"node:assert/strict", "node:assert/strict",
"@libsql/client", "@libsql/client",
"bknd", "bknd",
/^bknd\/.*/, /^bknd\/.*/,
"jsonv-ts", "jsonv-ts",
/^jsonv-ts\/.*/, /^jsonv-ts\/.*/,
] as const; ] as const;
/** /**
* Building backend and general API * Building backend and general API
*/ */
async function buildApi() { async function buildApi() {
await tsup.build({ await build({
minify, minify,
sourcemap, 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, watch: false,
define, // cleaning is handled manually (--clean) to preserve dist/static
entry: [ clean: false,
"src/index.ts", dts: false,
"src/core/utils/index.ts", define,
"src/plugins/index.ts", entry: [
"src/modes/index.ts", "src/index.ts",
], "src/core/utils/index.ts",
outDir: "dist", "src/plugins/index.ts",
external: [...external], "src/modes/index.ts",
metafile: true, ],
target: "esnext", unbundle: false,
platform: "browser", outDir: "dist",
removeNodeProtocol: false, deps: {
format: ["esm"], neverBundle: [
splitting: false, ...neverBundle
loader: { ],
".svg": "dataurl", },
},
onSuccess: async () => { target: "esnext",
delayTypes(); platform: "browser",
oldConsole.log(c.cyan("[API]"), c.green("built")); nodeProtocol: true,
}, format: "esm",
});
loader: {
".svg": "dataurl",
},
onSuccess: async () => {
delayTypes();
oldConsole.log(c.cyan("[API]"), c.green("built"));
},
});
} }
async function rewriteClient(path: string) { async function rewriteClient(path: string) {
const bundle = await Bun.file(path).text(); const bundle = await Bun.file(path).text();
await Bun.write( await Bun.write(path, '"use client";\n' + bundle.replaceAll("ui/client", "bknd/client"));
path,
'"use client";\n' + bundle.replaceAll("ui/client", "bknd/client"),
);
} }
/** /**
* Building UI for direct imports * Building UI for direct imports
*/ */
async function buildUi() { async function buildUi() {
const base = { const base = {
minify, minify,
sourcemap, sourcemap,
watch: false, watch: false,
define, // cleaning is handled manually (--clean) to preserve dist/static
external: [ clean: false,
...external, dts: false,
"react", define,
"react-dom", css: {
"react/jsx-runtime", transformer: "postcss",
"react/jsx-dev-runtime", postcss: "postcss.config.js",
"use-sync-external-store", minify: true,
/codemirror/, splitting: false
"@xyflow/react", },
"@mantine/core", deps: {
], neverBundle: [
metafile: true, ...neverBundle,
platform: "browser", "react",
format: ["esm"], "react-dom",
splitting: false, "react/jsx-runtime",
bundle: true, "react/jsx-dev-runtime",
treeshake: true, "use-sync-external-store",
loader: { /codemirror/,
".svg": "dataurl", "@xyflow/react",
}, "@mantine/core",
esbuildOptions: (options) => { ],
options.logLevel = "silent"; },
}, devtools: dev,
} satisfies tsup.Options; platform: "browser",
format: "esm",
unbundle: false,
treeshake: true,
loader: {
".svg": "dataurl",
},
await tsup.build({ logLevel: "silent",
...base, } satisfies UserConfig;
entry: ["src/ui/index.ts", "src/ui/main.css", "src/ui/styles.css"],
outDir: "dist/ui",
onSuccess: async () => {
await rewriteClient("./dist/ui/index.js");
delayTypes();
oldConsole.log(c.cyan("[UI]"), c.green("built"));
},
});
await tsup.build({
...base,
entry: ["src/ui/client/index.ts"], await Promise.all([
outDir: "dist/ui/client", build({
onSuccess: async () => { ...base,
await rewriteClient("./dist/ui/client/index.js"); entry: ["src/ui/index.ts", "src/ui/main.css", "src/ui/styles.css"],
delayTypes(); outDir: "dist/ui",
oldConsole.log(c.cyan("[UI]"), "Client", c.green("built")); onSuccess: async () => {
}, await rewriteClient("./dist/ui/index.js");
}); delayTypes();
oldConsole.log(c.cyan("[UI]"), c.green("built"));
},
}),
build({
...base,
entry: ["src/ui/client/index.ts"],
outDir: "dist/ui/client",
onSuccess: async () => {
await rewriteClient("./dist/ui/client/index.js");
delayTypes();
oldConsole.log(c.cyan("[UI]"), "Client", c.green("built"));
},
}),
]);
} }
/** /**
@@ -194,238 +206,247 @@ async function buildUi() {
* - ui/client is external, and after built replaced with "bknd/client" * - ui/client is external, and after built replaced with "bknd/client"
*/ */
async function buildUiElements() { async function buildUiElements() {
await tsup.build({ await build({
minify, minify,
sourcemap, sourcemap,
watch: false, watch: false,
define, // cleaning is handled manually (--clean) to preserve dist/static
entry: ["src/ui/elements/index.ts"], clean: false,
outDir: "dist/ui/elements", dts: false,
external: [ define,
"ui/client", entry: ["src/ui/elements/index.ts"],
"bknd", outDir: "dist/ui/elements",
/^bknd\/.*/, deps: {
"wouter", neverBundle: [
"react", "ui/client",
"react-dom", "bknd",
"react/jsx-runtime", /^bknd\/.*/,
"react/jsx-dev-runtime", "wouter",
"use-sync-external-store", "react",
], "react-dom",
metafile: true, "react/jsx-runtime",
platform: "browser", "react/jsx-dev-runtime",
format: ["esm"], "use-sync-external-store",
splitting: false, ],
bundle: true, },
treeshake: true, platform: "browser",
loader: { format: "esm",
".svg": "dataurl", bundle: true,
}, treeshake: true,
esbuildOptions: (options) => { loader: {
options.alias = { ".svg": "dataurl",
// not important for elements, mock to reduce bundle },
"tailwind-merge": "./src/ui/elements/mocks/tailwind-merge.ts", alias: {
}; // not important for elements, mock to reduce bundle
}, "tailwind-merge": resolve(import.meta.dir, "src/ui/elements/mocks/tailwind-merge.ts"),
onSuccess: async () => { },
await rewriteClient("./dist/ui/elements/index.js");
delayTypes(); onSuccess: async () => {
oldConsole.log(c.cyan("[UI]"), "Elements", c.green("built")); await rewriteClient("./dist/ui/elements/index.js");
}, delayTypes();
}); oldConsole.log(c.cyan("[UI]"), "Elements", c.green("built"));
},
});
} }
/** /**
* Building adapters * Building adapters
*/ */
function baseConfig( function baseConfig(adapter: string, overrides: Partial<UserConfig> = {}): UserConfig {
adapter: string, return {
overrides: Partial<tsup.Options> = {}, minify,
): tsup.Options { sourcemap,
return { watch: false,
minify, // cleaning is handled manually (--clean) to preserve dist/static
sourcemap, clean: false,
watch: false, dts: false,
entry: [`src/adapter/${adapter}/index.ts`], entry: [`src/adapter/${adapter}/index.ts`],
format: ["esm"], format: "esm",
platform: "neutral", platform: "neutral",
outDir: `dist/adapter/${adapter}`, outDir: `dist/adapter/${adapter}`,
metafile: true, devtools: dev,
splitting: false, nodeProtocol: true,
removeNodeProtocol: false, onSuccess: async () => {
onSuccess: async () => { delayTypes();
delayTypes(); oldConsole.log(c.cyan("[Adapter]"), adapter || "base", c.green("built"));
oldConsole.log(c.cyan("[Adapter]"), adapter || "base", c.green("built")); },
}, ...overrides,
...overrides, define: {
define: { ...define,
...define, ...overrides.define,
...overrides.define, },
}, deps: {
external: [ neverBundle: [
/^cloudflare*/, /^cloudflare*/,
/^@?hono.*?/, /^@?hono.*?/,
/^(bknd|react|next|node).*?/, /^(bknd|react|next|node).*?/,
/.*\.(html)$/, /.*\.(html)$/,
...external, ...neverBundle,
...(Array.isArray(overrides.external) ? overrides.external : []), ...(Array.isArray(overrides.deps?.neverBundle) ? overrides.deps.neverBundle : []),
], ],
}; },
};
} }
async function buildAdapters() { async function buildAdapters() {
await Promise.all([ await Promise.all([
// base adapter handles // base adapter handles
tsup.build({ build({
...baseConfig(""), ...baseConfig(""),
target: "esnext", target: "esnext",
platform: "neutral", platform: "neutral",
entry: ["src/adapter/index.ts"], entry: ["src/adapter/index.ts"],
outDir: "dist/adapter", outDir: "dist/adapter",
// only way to keep @vite-ignore comments // only way to keep @vite-ignore comments
minify: false, minify: false,
}), dts: false,
// specific adatpers
tsup.build(baseConfig("react-router")),
tsup.build(
baseConfig("browser", {
external: [/^sqlocal\/?.*?/, "wouter"],
}), }),
),
tsup.build( // specific adapters
baseConfig("bun", { build(baseConfig("react-router")),
external: [/^bun\:.*/], build(
baseConfig("browser", {
deps: {
neverBundle: [/^sqlocal\/?.*?/, "wouter"],
},
}),
),
build(
baseConfig("bun", {
deps: {
neverBundle: [/^bun:.*/],
},
}),
),
build(baseConfig("astro")),
build(baseConfig("aws")),
build(
baseConfig("cloudflare", {
deps: {
neverBundle: ["wrangler", "node:process", "miniflare"],
},
}),
),
build(
baseConfig("cloudflare/proxy", {
target: "esnext",
entry: ["src/adapter/cloudflare/proxy.ts"],
outDir: "dist/adapter/cloudflare",
devtools: false,
deps: {
neverBundle: [/bknd/, "wrangler", "node:process", "miniflare"],
},
}),
),
build({
...baseConfig("vite"),
platform: "node",
}), }),
),
tsup.build(baseConfig("astro")), build({
tsup.build(baseConfig("aws")), ...baseConfig("nextjs"),
tsup.build( platform: "node",
baseConfig("cloudflare", {
external: ["wrangler", "node:process"],
}), }),
),
tsup.build( build({
baseConfig("cloudflare/proxy", { ...baseConfig("tanstack-start"),
target: "esnext", platform: "node",
entry: ["src/adapter/cloudflare/proxy.ts"],
outDir: "dist/adapter/cloudflare",
metafile: false,
external: [/bknd/, "wrangler", "node:process"],
}), }),
),
tsup.build({ build({
...baseConfig("vite"), ...baseConfig("sveltekit"),
platform: "node", platform: "node",
}), }),
tsup.build({ build({
...baseConfig("nextjs"), ...baseConfig("nuxt"),
platform: "node", platform: "node",
}), }),
tsup.build({ build({
...baseConfig("tanstack-start"), ...baseConfig("node"),
platform: "node", platform: "node",
}), }),
tsup.build({ build({
...baseConfig("sveltekit"), ...baseConfig("sqlite/edge"),
platform: "node", entry: ["src/adapter/sqlite/edge.ts"],
}), outDir: "dist/adapter/sqlite",
devtools: false,
}),
tsup.build({ build({
...baseConfig("nuxt"), ...baseConfig("sqlite/node"),
platform: "node", entry: ["src/adapter/sqlite/node.ts"],
}), outDir: "dist/adapter/sqlite",
platform: "node",
devtools: false,
}),
tsup.build({ build(
...baseConfig("node"), baseConfig("sqlite/bun", {
platform: "node", entry: ["src/adapter/sqlite/bun.ts"],
}), outDir: "dist/adapter/sqlite",
devtools: false,
tsup.build({ deps: {
...baseConfig("sqlite/edge"), neverBundle: [/^bun:.*/],
entry: ["src/adapter/sqlite/edge.ts"], },
outDir: "dist/adapter/sqlite", }),
metafile: false, ),
}), ]);
tsup.build({
...baseConfig("sqlite/node"),
entry: ["src/adapter/sqlite/node.ts"],
outDir: "dist/adapter/sqlite",
platform: "node",
metafile: false,
}),
tsup.build({
...baseConfig("sqlite/bun"),
entry: ["src/adapter/sqlite/bun.ts"],
outDir: "dist/adapter/sqlite",
metafile: false,
external: [/^bun\:.*/],
}),
]);
} }
async function buildAll() { const buildPipeline = [buildApi, buildUi, buildUiElements, buildAdapters];
await Promise.all([
buildApi(), // Run type generation in parallel, since it can be slow and doesn't block the main build
buildUi(), if (types) {
buildUiElements(), buildPipeline.push(async () => await buildTypes());
buildAdapters(),
]);
} }
// initial build // initial build
await buildAll(); 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) { if (watch) {
oldConsole.log(c.cyan("[Watch]"), "watching for changes in src/..."); oldConsole.log(c.cyan("[Watch]"), "watching for changes in src/...");
let debounceTimer: ReturnType<typeof setTimeout> | null = null; let debounceTimer: ReturnType<typeof setTimeout> | null = null;
let isBuilding = false; let isBuilding = false;
const rebuild = async () => { const rebuild = async () => {
if (isBuilding) return; if (isBuilding) return;
isBuilding = true; isBuilding = true;
oldConsole.log(c.cyan("[Watch]"), "rebuilding..."); oldConsole.log(c.cyan("[Watch]"), "rebuilding...");
try { try {
await buildAll(); await buildAll();
oldConsole.log(c.cyan("[Watch]"), c.green("done")); oldConsole.log(c.cyan("[Watch]"), c.green("done"));
} catch (e) { } catch (e) {
oldConsole.warn(c.cyan("[Watch]"), c.red("build failed"), e); oldConsole.warn(c.cyan("[Watch]"), c.red("build failed"), e);
} }
isBuilding = false; isBuilding = false;
}; };
const debouncedRebuild = () => { const debouncedRebuild = () => {
if (debounceTimer) clearTimeout(debounceTimer); if (debounceTimer) clearTimeout(debounceTimer);
debounceTimer = setTimeout(rebuild, 100); debounceTimer = setTimeout(rebuild, 100);
}; };
// watch src directory recursively // watch src directory recursively
fsWatch( fsWatch(join(import.meta.dir, "src"), { recursive: true }, (event, filename) => {
join(import.meta.dir, "src"),
{ recursive: true },
(event, filename) => {
if (!filename) return; if (!filename) return;
// ignore non-source files // ignore non-source files
if ( if (!filename.endsWith(".ts") && !filename.endsWith(".tsx") && !filename.endsWith(".css"))
!filename.endsWith(".ts") && return;
!filename.endsWith(".tsx") &&
!filename.endsWith(".css")
)
return;
oldConsole.log(c.cyan("[Watch]"), c.dim(`${event}: ${filename}`)); oldConsole.log(c.cyan("[Watch]"), c.dim(`${event}: ${filename}`));
debouncedRebuild(); debouncedRebuild();
}, });
);
// keep process alive // keep process alive
await new Promise(() => {}); 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;
}
}
+107 -78
View File
@@ -15,7 +15,8 @@
}, },
"packageManager": "bun@1.3.3", "packageManager": "bun@1.3.3",
"engines": { "engines": {
"node": ">=22.13" "node": ">=22.18.0",
"bun": ">=1.3.3"
}, },
"scripts": { "scripts": {
"dev": "BKND_CLI_LOG_LEVEL=debug vite", "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: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: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:cli": "bun run build.cli.ts",
"build:types": "bun run build.types.ts",
"build:static": "vite build", "build:static": "vite build",
"watch": "bun run build.ts --types --watch", "watch": "bun run build.ts --types --watch",
"types": "bun tsc -p tsconfig.build.json --noEmit", "types": "bun tsc -p tsconfig.build.json --noEmit",
"clean:types": "find ./dist -name '*.d.ts' -delete && rm -f ./dist/tsconfig.tsbuildinfo", "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", "updater": "bun x npm-check-updates -ui",
"cli": "LOCAL=1 bun src/cli/index.ts", "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 ./", "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", "@rjsf/utils": "5.22.0",
"@standard-schema/spec": "^1.0.0", "@standard-schema/spec": "^1.0.0",
"@tabler/icons-react": "3.35.0", "@tabler/icons-react": "3.35.0",
"@tailwindcss/postcss": "^4.1.16", "@tailwindcss/postcss": "^4.2.2",
"@tailwindcss/vite": "^4.1.16", "@tailwindcss/vite": "^4.2.4",
"@tanstack/react-store": "^0.8.0", "@tanstack/react-store": "^0.8.0",
"@testing-library/jest-dom": "^6.6.3", "@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.2.0", "@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/pg": "^8.15.6",
"@types/react": "^19.0.10", "@types/react": "^19.2.16",
"@types/react-dom": "^19.0.4", "@types/react-dom": "^19.0.4",
"@vitejs/plugin-react": "^5.1.0", "@vitejs/plugin-react": "^6.0.2",
"@vitest/coverage-v8": "3.0.9", "@vitest/coverage-v8": "4.1.10",
"autoprefixer": "^10.4.21", "autoprefixer": "^10.4.21",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"commander": "^14.0.2", "commander": "^14.0.2",
@@ -114,89 +116,87 @@
"kysely-postgres-js": "^2.0.0", "kysely-postgres-js": "^2.0.0",
"libsql": "^0.5.22", "libsql": "^0.5.22",
"libsql-stateless-easy": "^1.8.0", "libsql-stateless-easy": "^1.8.0",
"miniflare": "^4.20251011.2", "miniflare": "^4.20260317.3",
"open": "^10.2.0", "open": "^10.2.0",
"openapi-types": "^12.1.3", "openapi-types": "^12.1.3",
"pg": "^8.16.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-preset-mantine": "^1.18.0",
"postcss-simple-vars": "^7.0.1", "postcss-simple-vars": "^7.0.1",
"postgres": "^3.4.7", "postgres": "^3.4.7",
"posthog-js-lite": "^3.6.0", "posthog-js-lite": "^3.6.0",
"react": "^19.0.0", "react": "^19.2.7",
"react-dom": "^19.0.0", "react-dom": "^19.2.7",
"react-hook-form": "^7.66.0", "react-hook-form": "^7.66.0",
"react-icons": "5.5.0", "react-icons": "5.5.0",
"react-json-view-lite": "^2.5.0", "react-json-view-lite": "^2.5.0",
"sql-formatter": "^15.6.10", "sql-formatter": "^15.6.10",
"sqlocal": "^0.16.0", "sqlocal": "^0.16.0",
"tailwind-merge": "^3.0.2", "tailwind-merge": "^3.0.2",
"tailwindcss": "^4.1.16", "tailwindcss": "^4.3.3",
"tailwindcss-animate": "^1.0.7", "tailwindcss-animate": "^1.0.7",
"tsc-alias": "^1.8.16", "tsdown": "^0.22.14",
"tsup": "^8.5.0",
"tsx": "^4.20.6",
"uuid": "^13.0.0", "uuid": "^13.0.0",
"vite": "^7.1.12", "vite": "^8.0.16",
"vite-plugin-circular-dependency": "^0.5.0", "vite-plugin-circular-dependency": "^0.6.0",
"vite-tsconfig-paths": "^5.1.4", "vitest": "4.1.10",
"vitest": "3.0.9",
"wouter": "^3.7.1", "wouter": "^3.7.1",
"wrangler": "^4.45.4" "wrangler": "^4.78.0"
}, },
"optionalDependencies": { "optionalDependencies": {
"@hono/node-server": "^1.19.6" "@hono/node-server": "^1.19.6"
}, },
"peerDependencies": { "peerDependencies": {
"react": ">=19", "react": ">=19.2.6",
"react-dom": ">=19" "react-dom": ">=19.2.6"
}, },
"main": "./dist/index.js", "main": "./dist/index.js",
"module": "./dist/index.js", "module": "./dist/index.js",
"types": "./dist/types/index.d.ts", "types": "./dist/types/index.d.mts",
"exports": { "exports": {
".": { ".": {
"types": "./dist/types/index.d.ts", "types": "./dist/types/index.d.mts",
"import": "./dist/index.js", "import": "./dist/index.js",
"require": "./dist/index.js" "require": "./dist/index.js"
}, },
"./ui": { "./ui": {
"types": "./dist/types/ui/index.d.ts", "types": "./dist/types/ui/index.d.mts",
"import": "./dist/ui/index.js", "import": "./dist/ui/index.js",
"require": "./dist/ui/index.js" "require": "./dist/ui/index.js"
}, },
"./elements": { "./elements": {
"types": "./dist/types/ui/elements/index.d.ts", "types": "./dist/types/ui/elements/index.d.mts",
"import": "./dist/ui/elements/index.js", "import": "./dist/ui/elements/index.js",
"require": "./dist/ui/elements/index.js" "require": "./dist/ui/elements/index.js"
}, },
"./client": { "./client": {
"types": "./dist/types/ui/client/index.d.ts", "types": "./dist/types/ui/client/index.d.mts",
"import": "./dist/ui/client/index.js", "import": "./dist/ui/client/index.js",
"require": "./dist/ui/client/index.js" "require": "./dist/ui/client/index.js"
}, },
"./utils": { "./utils": {
"types": "./dist/types/core/utils/index.d.ts", "types": "./dist/types/core/utils/index.d.mts",
"import": "./dist/core/utils/index.js", "import": "./dist/core/utils/index.js",
"require": "./dist/core/utils/index.js" "require": "./dist/core/utils/index.js"
}, },
"./cli": { "./cli": {
"types": "./dist/types/cli/index.d.ts", "types": "./dist/types/cli/index.d.mts",
"import": "./dist/cli/index.js", "import": "./dist/cli/index.js",
"require": "./dist/cli/index.js" "require": "./dist/cli/index.js"
}, },
"./plugins": { "./plugins": {
"types": "./dist/types/plugins/index.d.ts", "types": "./dist/types/plugins/index.d.mts",
"import": "./dist/plugins/index.js", "import": "./dist/plugins/index.js",
"require": "./dist/plugins/index.js" "require": "./dist/plugins/index.js"
}, },
"./modes": { "./modes": {
"types": "./dist/types/modes/index.d.ts", "types": "./dist/types/modes/index.d.mts",
"import": "./dist/modes/index.js", "import": "./dist/modes/index.js",
"require": "./dist/modes/index.js" "require": "./dist/modes/index.js"
}, },
"./adapter/sqlite": { "./adapter/sqlite": {
"types": "./dist/types/adapter/sqlite/edge.d.ts", "types": "./dist/types/adapter/sqlite/edge.d.mts",
"import": { "import": {
"workerd": "./dist/adapter/sqlite/edge.js", "workerd": "./dist/adapter/sqlite/edge.js",
"edge-light": "./dist/adapter/sqlite/edge.js", "edge-light": "./dist/adapter/sqlite/edge.js",
@@ -204,101 +204,130 @@
"vercel": "./dist/adapter/sqlite/edge.js", "vercel": "./dist/adapter/sqlite/edge.js",
"browser": "./dist/adapter/sqlite/edge.js", "browser": "./dist/adapter/sqlite/edge.js",
"bun": "./dist/adapter/sqlite/bun.js", "bun": "./dist/adapter/sqlite/bun.js",
"node": "./dist/adapter/sqlite/node.js", "node": "./dist/adapter/sqlite/node.mjs",
"default": "./dist/adapter/sqlite/node.js" "default": "./dist/adapter/sqlite/node.mjs"
}, },
"require": "./dist/adapter/sqlite/node.js" "require": "./dist/adapter/sqlite/node.mjs"
}, },
"./adapter/cloudflare": { "./adapter/cloudflare": {
"types": "./dist/types/adapter/cloudflare/index.d.ts", "types": "./dist/types/adapter/cloudflare/index.d.mts",
"import": "./dist/adapter/cloudflare/index.js", "import": "./dist/adapter/cloudflare/index.js",
"require": "./dist/adapter/cloudflare/index.js" "require": "./dist/adapter/cloudflare/index.js"
}, },
"./adapter/cloudflare/proxy": { "./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", "import": "./dist/adapter/cloudflare/proxy.js",
"require": "./dist/adapter/cloudflare/proxy.js" "require": "./dist/adapter/cloudflare/proxy.js"
}, },
"./adapter": { "./adapter": {
"types": "./dist/types/adapter/index.d.ts", "types": "./dist/types/adapter/index.d.mts",
"import": "./dist/adapter/index.js" "import": "./dist/adapter/index.js"
}, },
"./adapter/vite": { "./adapter/vite": {
"types": "./dist/types/adapter/vite/index.d.ts", "types": "./dist/types/adapter/vite/index.d.mts",
"import": "./dist/adapter/vite/index.js", "import": "./dist/adapter/vite/index.mjs",
"require": "./dist/adapter/vite/index.js" "require": "./dist/adapter/vite/index.mjs"
}, },
"./adapter/nextjs": { "./adapter/nextjs": {
"types": "./dist/types/adapter/nextjs/index.d.ts", "types": "./dist/types/adapter/nextjs/index.d.mts",
"import": "./dist/adapter/nextjs/index.js", "import": "./dist/adapter/nextjs/index.mjs",
"require": "./dist/adapter/nextjs/index.js" "require": "./dist/adapter/nextjs/index.mjs"
}, },
"./adapter/nuxt": { "./adapter/nuxt": {
"types": "./dist/types/adapter/nuxt/index.d.ts", "types": "./dist/types/adapter/nuxt/index.d.mts",
"import": "./dist/adapter/nuxt/index.js", "import": "./dist/adapter/nuxt/index.mjs",
"require": "./dist/adapter/nuxt/index.js" "require": "./dist/adapter/nuxt/index.mjs"
}, },
"./adapter/react-router": { "./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", "import": "./dist/adapter/react-router/index.js",
"require": "./dist/adapter/react-router/index.js" "require": "./dist/adapter/react-router/index.js"
}, },
"./adapter/bun": { "./adapter/bun": {
"types": "./dist/types/adapter/bun/index.d.ts", "types": "./dist/types/adapter/bun/index.d.mts",
"import": "./dist/adapter/bun/index.js", "import": "./dist/adapter/bun/index.js",
"require": "./dist/adapter/bun/index.js" "require": "./dist/adapter/bun/index.js"
}, },
"./adapter/node": { "./adapter/node": {
"types": "./dist/types/adapter/node/index.d.ts", "types": "./dist/types/adapter/node/index.d.mts",
"import": "./dist/adapter/node/index.js", "import": "./dist/adapter/node/index.mjs",
"require": "./dist/adapter/node/index.js" "require": "./dist/adapter/node/index.mjs"
}, },
"./adapter/astro": { "./adapter/astro": {
"types": "./dist/types/adapter/astro/index.d.ts", "types": "./dist/types/adapter/astro/index.d.mts",
"import": "./dist/adapter/astro/index.js", "import": "./dist/adapter/astro/index.js",
"require": "./dist/adapter/astro/index.js" "require": "./dist/adapter/astro/index.js"
}, },
"./adapter/sveltekit": { "./adapter/sveltekit": {
"types": "./dist/types/adapter/sveltekit/index.d.ts", "types": "./dist/types/adapter/sveltekit/index.d.mts",
"import": "./dist/adapter/sveltekit/index.js", "import": "./dist/adapter/sveltekit/index.mjs",
"require": "./dist/adapter/sveltekit/index.js" "require": "./dist/adapter/sveltekit/index.mjs"
}, },
"./adapter/aws": { "./adapter/aws": {
"types": "./dist/types/adapter/aws/index.d.ts", "types": "./dist/types/adapter/aws/index.d.mts",
"import": "./dist/adapter/aws/index.js", "import": "./dist/adapter/aws/index.js",
"require": "./dist/adapter/aws/index.js" "require": "./dist/adapter/aws/index.js"
}, },
"./adapter/browser": { "./adapter/browser": {
"types": "./dist/types/adapter/browser/index.d.ts", "types": "./dist/types/adapter/browser/index.d.mts",
"import": "./dist/adapter/browser/index.js", "import": "./dist/adapter/browser/index.js",
"require": "./dist/adapter/browser/index.js" "require": "./dist/adapter/browser/index.js"
}, },
"./adapter/tanstack-start": { "./adapter/tanstack-start": {
"types": "./dist/types/adapter/tanstack-start/index.d.ts", "types": "./dist/types/adapter/tanstack-start/index.d.mts",
"import": "./dist/adapter/tanstack-start/index.js", "import": "./dist/adapter/tanstack-start/index.mjs",
"require": "./dist/adapter/tanstack-start/index.js" "require": "./dist/adapter/tanstack-start/index.mjs"
}, },
"./dist/main.css": "./dist/ui/main.css", "./dist/main.css": "./dist/ui/style.css",
"./dist/styles.css": "./dist/ui/styles.css", "./dist/styles.css": "./dist/ui/style.css",
"./dist/style.css": "./dist/ui/style.css",
"./dist/manifest.json": "./dist/static/.vite/manifest.json", "./dist/manifest.json": "./dist/static/.vite/manifest.json",
"./static/*": "./dist/static/*" "./static/*": "./dist/static/*"
}, },
"typesVersions": { "typesVersions": {
"*": { "*": {
"utils": ["./dist/types/core/utils/index.d.ts"], "utils": [
"cli": ["./dist/types/cli/index.d.ts"], "./dist/types/core/utils/index.d.mts"
"plugins": ["./dist/types/plugins/index.d.ts"], ],
"adapter": ["./dist/types/adapter/index.d.ts"], "cli": [
"adapter/cloudflare": ["./dist/types/adapter/cloudflare/index.d.ts"], "./dist/types/cli/index.d.mts"
"adapter/vite": ["./dist/types/adapter/vite/index.d.ts"], ],
"adapter/nextjs": ["./dist/types/adapter/nextjs/index.d.ts"], "plugins": [
"adapter/nuxt": ["./dist/types/adapter/nuxt/index.d.ts"], "./dist/types/plugins/index.d.mts"
"adapter/react-router": ["./dist/types/adapter/react-router/index.d.ts"], ],
"adapter/bun": ["./dist/types/adapter/bun/index.d.ts"], "adapter": [
"adapter/node": ["./dist/types/adapter/node/index.d.ts"], "./dist/types/adapter/index.d.mts"
"adapter/sveltekit": ["./dist/types/adapter/sveltekit/index.d.ts"], ],
"adapter/tanstack-start": ["./dist/types/adapter/tanstack-start/index.d.ts"], "adapter/cloudflare": [
"adapter/sqlite": ["./dist/types/adapter/sqlite/edge.d.ts"] "./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": { "publishConfig": {
@@ -8,6 +8,12 @@ import { $console } from "bknd/utils";
import { createRuntimeApp } from "bknd/adapter"; import { createRuntimeApp } from "bknd/adapter";
import { registerAsyncsExecutionContext, makeConfig, type CloudflareContext } from "./config"; 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 { declare global {
namespace Cloudflare { namespace Cloudflare {
interface Env {} interface Env {}
@@ -38,6 +44,19 @@ export async function createApp<Env extends CloudflareEnv = CloudflareEnv>(
ctx: Partial<CloudflareContext<Env>> = {}, ctx: Partial<CloudflareContext<Env>> = {},
) { ) {
const appConfig = await makeConfig(config, ctx); 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>( return await createRuntimeApp<Env>(
{ {
...appConfig, ...appConfig,
+7 -1
View File
@@ -14,6 +14,12 @@ export interface EmitsEvents {
emgr: EventManager; 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 // for compatibility, moved it to Event.ts
export type { EventClass }; export type { EventClass };
@@ -65,7 +71,7 @@ export class EventManager<
return [...this.listeners]; return [...this.listeners];
} }
get Events(): { [K in keyof RegisteredEvents]: RegisteredEvents[K] } { get Events(): EventRegistryMap<RegisteredEvents> {
// proxy class to access events // proxy class to access events
return new Proxy(this, { return new Proxy(this, {
get: (_, prop: string) => { get: (_, prop: string) => {
+1 -1
View File
@@ -31,7 +31,7 @@ export type IndexMetadata = {
}; };
export interface SelectQueryBuilderExpression<O> extends AliasableExpression<O> { export interface SelectQueryBuilderExpression<O> extends AliasableExpression<O> {
get isSelectQueryBuilder(): true; readonly isSelectQueryBuilder: true;
toOperationNode(): SelectQueryNode; toOperationNode(): SelectQueryNode;
} }
+3 -7
View File
@@ -15,7 +15,7 @@ export const entityConfigSchema = s
name: s.string(), name: s.string(),
name_singular: s.string(), name_singular: s.string(),
description: s.string(), description: s.string(),
sort_field: s.string(), sort_field: s.string({ default: config.data.default_primary_field }),
sort_dir: s.string({ enum: ["asc", "desc"], default: "asc" }), sort_dir: s.string({ enum: ["asc", "desc"], default: "asc" }),
primary_format: s.string({ enum: primaryFieldTypes }), primary_format: s.string({ enum: primaryFieldTypes }),
}, },
@@ -82,10 +82,6 @@ export class Entity<
fields.forEach((field) => this.addField(field)); fields.forEach((field) => this.addField(field));
} }
if (!this.config.sort_field) {
this.config.sort_field = this.getPrimaryField().name;
}
if (type) this.type = type; if (type) this.type = type;
this[ENTITY_SYMBOL] = true; this[ENTITY_SYMBOL] = true;
} }
@@ -119,7 +115,7 @@ export class Entity<
getDefaultSort() { getDefaultSort() {
return { return {
by: this.config.sort_field ?? this.getPrimaryField().name, by: this.config.sort_field ?? "id",
dir: this.config.sort_dir ?? "asc", dir: this.config.sort_dir ?? "asc",
}; };
} }
@@ -171,7 +167,7 @@ export class Entity<
} }
getPrimaryField(): PrimaryField { getPrimaryField(): PrimaryField {
return this.fields.find((field) => field.type === "primary") as PrimaryField; return this.fields[0] as PrimaryField;
} }
id(): PrimaryField { id(): PrimaryField {
+2 -12
View File
@@ -24,11 +24,7 @@ export class PrimaryField<Required extends true | false = false> extends Field<
override readonly type = "primary"; override readonly type = "primary";
constructor(name: string = config.data.default_primary_field, cfg?: PrimaryFieldConfig) { constructor(name: string = config.data.default_primary_field, cfg?: PrimaryFieldConfig) {
super(name, { super(name, { ...cfg, fillable: false, required: false });
...cfg,
fillable: cfg?.fillable ?? name !== config.data.default_primary_field,
required: false,
});
} }
override isRequired(): boolean { override isRequired(): boolean {
@@ -64,13 +60,7 @@ export class PrimaryField<Required extends true | false = false> extends Field<
return undefined; return undefined;
} }
override async transformPersist(value: any): Promise<any> { override async transformPersist(value: any): Promise<number> {
if (this.isFillable()) {
if (value == null) {
return this.getNewValue();
}
return value;
}
throw new Error("PrimaryField: This function should not be called"); throw new Error("PrimaryField: This function should not be called");
} }
+1 -10
View File
@@ -21,8 +21,6 @@ import {
type NumberFieldConfig, type NumberFieldConfig,
TextField, TextField,
type TextFieldConfig, type TextFieldConfig,
PrimaryField,
type PrimaryFieldConfig,
} from "data/fields"; } from "data/fields";
import { Entity, type EntityConfig, type TEntityType } from "data/entities"; import { Entity, type EntityConfig, type TEntityType } from "data/entities";
@@ -53,8 +51,6 @@ type Options<Config = any> = {
}; };
const FieldMap = { const FieldMap = {
primary: (o: Omit<Options, "required">) =>
new PrimaryField(o.field_name, { ...o.config, required: true }),
text: (o: Options) => new TextField(o.field_name, { ...o.config, required: o.is_required }), text: (o: Options) => new TextField(o.field_name, { ...o.config, required: o.is_required }),
number: (o: Options) => new NumberField(o.field_name, { ...o.config, required: o.is_required }), number: (o: Options) => new NumberField(o.field_name, { ...o.config, required: o.is_required }),
date: (o: Options) => new DateField(o.field_name, { ...o.config, required: o.is_required }), date: (o: Options) => new DateField(o.field_name, { ...o.config, required: o.is_required }),
@@ -112,11 +108,6 @@ export class FieldPrototype {
} }
} }
export function primary(
config?: Omit<PrimaryFieldConfig, "required">,
): PrimaryField<false> & { required: () => PrimaryField<true> } {
return new FieldPrototype("primary", config, false) as any;
}
export function text( export function text(
config?: Omit<TextFieldConfig, "required">, config?: Omit<TextFieldConfig, "required">,
): TextField<false> & { required: () => TextField<true> } { ): TextField<false> & { required: () => TextField<true> } {
@@ -419,5 +410,5 @@ export type Schemas<T extends Record<string, Entity>> = {
}; };
export type InsertSchema<T> = Simplify<OptionalUndefined<InferEntityFields<T>>>; export type InsertSchema<T> = Simplify<OptionalUndefined<InferEntityFields<T>>>;
export type Schema<T> = Simplify<{ id: Generated<number> | string } & InsertSchema<T>>; export type Schema<T> = Simplify<{ id: Generated<number> } & InsertSchema<T>>;
export type FieldSchema<T> = Simplify<OptionalUndefined<InferFields<T>>>; export type FieldSchema<T> = Simplify<OptionalUndefined<InferFields<T>>>;
+5 -3
View File
@@ -82,7 +82,7 @@ export * as AuthPermissions from "auth/auth-permissions";
*/ */
export { getExtensionFromName, getRandomizedFilename } from "media/utils"; export { getExtensionFromName, getRandomizedFilename } from "media/utils";
import * as StorageEvents from "media/storage/events"; import * as StorageEvents from "media/storage/events";
export const MediaEvents = { export const MediaEvents: typeof StorageEvents = {
...StorageEvents, ...StorageEvents,
}; };
export * as MediaPermissions from "media/media-permissions"; export * as MediaPermissions from "media/media-permissions";
@@ -104,7 +104,10 @@ export { StorageCloudinaryAdapter } from "media/storage/adapters/cloudinary/Stor
* Data * Data
*/ */
import { MutatorEvents, RepositoryEvents } from "data/events"; import { MutatorEvents, RepositoryEvents } from "data/events";
export const DatabaseEvents = { ...MutatorEvents, ...RepositoryEvents }; export const DatabaseEvents: typeof MutatorEvents & typeof RepositoryEvents = {
...MutatorEvents,
...RepositoryEvents,
};
export type { export type {
RepoQuery, RepoQuery,
RepoQueryIn, RepoQueryIn,
@@ -173,7 +176,6 @@ export {
// data prototype // data prototype
export { export {
primary,
text, text,
number, number,
date, date,
@@ -1,12 +1,11 @@
import { retry, type TestRunner } from "core/test"; import { retry, type TestRunner } from "core/test";
import type { StorageAdapter } from "media/storage/StorageAdapter"; import type { StorageAdapter } from "media/storage/StorageAdapter";
import { randomString } from "bknd/utils"; import { randomString } from "bknd/utils";
import type { BunFile } from "bun";
export async function adapterTestSuite( export async function adapterTestSuite(
testRunner: TestRunner, testRunner: TestRunner,
_adapter: StorageAdapter | (() => StorageAdapter), _adapter: StorageAdapter | (() => StorageAdapter),
file: File | BunFile, file: File | import("bun").BunFile,
opts?: { opts?: {
retries?: number; retries?: number;
retryTimeout?: number; retryTimeout?: number;
+15
View File
@@ -55,6 +55,21 @@ export class AppServer extends Module<AppServerConfig> {
const origin = this.config.cors.origin ?? "*"; const origin = this.config.cors.origin ?? "*";
const origins = origin.includes(",") ? origin.split(",").map((o) => o.trim()) : [origin]; const origins = origin.includes(",") ? origin.split(",").map((o) => o.trim()) : [origin];
const all_origins = origins.includes("*"); 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( this.client.use(
"*", "*",
cors({ cors({
+1 -1
View File
@@ -1,6 +1,6 @@
import { MantineProvider } from "@mantine/core"; import { MantineProvider } from "@mantine/core";
import { Notifications } from "@mantine/notifications"; import { Notifications } from "@mantine/notifications";
import React, { type ReactNode } from "react"; import { type ReactNode } from "react";
import { BkndProvider } from "ui/client/bknd"; import { BkndProvider } from "ui/client/bknd";
import { useTheme, type AppTheme } from "ui/client/use-theme"; import { useTheme, type AppTheme } from "ui/client/use-theme";
import { Logo } from "ui/components/display/Logo"; import { Logo } from "ui/components/display/Logo";
+4 -3
View File
@@ -102,9 +102,10 @@ body,
/* Chrome, Edge, and Safari */ /* Chrome, Edge, and Safari */
& *::-webkit-scrollbar { & *::-webkit-scrollbar {
@apply w-1; @apply w-1;
&:horizontal { }
@apply h-px;
} & *::-webkit-scrollbar:horizontal {
@apply h-px;
} }
& *::-webkit-scrollbar-track { & *::-webkit-scrollbar-track {
+3 -1
View File
@@ -3,8 +3,10 @@
"compilerOptions": { "compilerOptions": {
"outDir": "./dist/types", "outDir": "./dist/types",
"rootDir": "./src", "rootDir": "./src",
"declaration": true,
"incremental": true,
"tsBuildInfoFile": "./dist/tsconfig.tsbuildinfo" "tsBuildInfoFile": "./dist/tsconfig.tsbuildinfo"
}, },
"include": ["./src/**/*.ts", "./src/**/*.tsx"], "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, "noImplicitAny": false,
"allowJs": true, "allowJs": true,
"verbatimModuleSyntax": true, "verbatimModuleSyntax": true,
"declaration": true, "declaration": false,
"declarationMap": false,
"strict": true, "strict": true,
"allowUnusedLabels": false, "allowUnusedLabels": false,
"allowUnreachableCode": false, "allowUnreachableCode": false,
@@ -26,7 +27,7 @@
"esModuleInterop": true, "esModuleInterop": true,
"skipLibCheck": true, "skipLibCheck": true,
"rootDir": ".", "rootDir": ".",
"outDir": "./dist/types", "outDir": "./dist",
"paths": { "paths": {
"*": ["./src/*"], "*": ["./src/*"],
"bknd": ["./src/index.ts"], "bknd": ["./src/index.ts"],
@@ -47,5 +48,5 @@
"__test__", "__test__",
"e2e/**/*.ts" "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 devServer from "@hono/vite-dev-server";
import react from "@vitejs/plugin-react"; import react from "@vitejs/plugin-react";
import { defineConfig } from "vite"; import { defineConfig } from "vite";
import tsconfigPaths from "vite-tsconfig-paths";
import { devServerConfig } from "./src/adapter/vite/dev-server-config"; 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" }; import pkg from "./package.json" with { type: "json" };
// https://vitejs.dev/config/ // https://vitejs.dev/config/
export default defineConfig({ export default defineConfig({
css: {
postcss: {
plugins: [tailwindcss()],
},
},
define: { define: {
__isDev: process.env.NODE_ENV === "production" ? "0" : "1", __isDev: process.env.NODE_ENV === "production" ? "0" : "1",
__version: JSON.stringify(pkg.version), __version: JSON.stringify(pkg.version),
@@ -21,23 +25,21 @@ export default defineConfig({
overlay: true, overlay: true,
}, },
}, },
resolve: {
tsconfigPaths: true
},
plugins: [ plugins: [
react(), react(),
tsconfigPaths({
// otherwise it'll throw an error because of examples/astro
ignoreConfigErrors: true,
}),
devServer({ devServer({
...devServerConfig, ...devServerConfig,
entry: "./vite.dev.ts", entry: "./vite.dev.ts",
//entry: "./vite.dev.code.ts", //entry: "./vite.dev.code.ts",
}), }),
tailwindcss(),
], ],
build: { build: {
manifest: true, manifest: true,
outDir: "./dist/static", outDir: "./dist/static",
rollupOptions: { rolldownOptions: {
input: "./src/ui/main.tsx", input: "./src/ui/main.tsx",
}, },
}, },
+3 -7
View File
@@ -1,13 +1,9 @@
import { defineConfig } from "vitest/config"; import { defineConfig } from "vitest/config";
import tsconfigPaths from "vite-tsconfig-paths";
export default defineConfig({ export default defineConfig({
plugins: [ resolve: {
tsconfigPaths({ tsconfigPaths: true,
root: ".", },
ignoreConfigErrors: true,
}) as any,
],
test: { test: {
include: ["**/*.vi-test.ts", "**/*.vitest.ts"], include: ["**/*.vi-test.ts", "**/*.vitest.ts"],
}, },
+1 -1
View File
@@ -35,7 +35,7 @@
"files": { "files": {
"includes": [ "includes": [
"**", "**",
"!!**/.tsup", "!!**/.tsdown",
"!!**/node_modules", "!!**/node_modules",
"!!**/.cache", "!!**/.cache",
"!!**/.wrangler", "!!**/.wrangler",
+1148 -1983
View File
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -10,11 +10,11 @@
"astro": "astro" "astro": "astro"
}, },
"dependencies": { "dependencies": {
"@astrojs/check": "^0.9.4", "@astrojs/check": "^0.9.9",
"@astrojs/react": "^4.2.2", "@astrojs/react": "^6.0.1",
"@types/react": "^19.0.12", "@types/react": "^19.0.12",
"@types/react-dom": "^19.0.4", "@types/react-dom": "^19.0.4",
"astro": "^5.16.9", "astro": "^7.0.7",
"bknd": "file:../../app", "bknd": "file:../../app",
"react": "^19.1.0", "react": "^19.1.0",
"react-dom": "^19.1.0", "react-dom": "^19.1.0",
-1
View File
@@ -1,5 +1,4 @@
--- ---
import Card from "../components/Card.astro";
interface Props { interface Props {
title: string; title: string;
} }
+2 -2
View File
@@ -5,7 +5,7 @@
"type": "module", "type": "module",
"packageManager": "bun@1.3.3", "packageManager": "bun@1.3.3",
"engines": { "engines": {
"node": ">=22.13" "node": ">=22.18"
}, },
"scripts": { "scripts": {
"updater": "bun x npm-check-updates -ui", "updater": "bun x npm-check-updates -ui",
@@ -18,7 +18,7 @@
"devDependencies": { "devDependencies": {
"@biomejs/biome": "2.3.3", "@biomejs/biome": "2.3.3",
"@tsconfig/strictest": "^2.0.8", "@tsconfig/strictest": "^2.0.8",
"@types/bun": "^1.3.1", "@types/bun": "^1.3.13",
"@types/lodash-es": "^4.17.12", "@types/lodash-es": "^4.17.12",
"miniflare": "^3.20250718.2", "miniflare": "^3.20250718.2",
"typescript": "^6.0.2", "typescript": "^6.0.2",
+3 -2
View File
@@ -12,13 +12,14 @@
"README.md" "README.md"
], ],
"dependencies": { "dependencies": {
"@libsql/client": "^0.14.0", "@libsql/client": "^0.17.2",
"hono": "^4.6.7" "hono": "^4.6.7"
}, },
"tsup": { "tsdown": {
"entry": [ "entry": [
"src/index.ts" "src/index.ts"
], ],
"dts": false,
"minify": true, "minify": true,
"outDir": "dist", "outDir": "dist",
"format": [ "format": [
+7 -5
View File
@@ -4,14 +4,15 @@
"type": "module", "type": "module",
"sideEffects": false, "sideEffects": false,
"scripts": { "scripts": {
"build": "rm -rf dist && bun tsup && bun run build:types", "build": "rm -rf dist && bun tsdown && bun run build:types",
"watch": "bun tsup --watch --onSuccess 'bun run build:types'", "watch": "bun tsdown --watch --onSuccess 'bun run build:types'",
"build:only": "rm -rf dist && bun tsup", "build:only": "rm -rf dist && bun tsdown",
"types": "bun tsc -p tsconfig.json --noEmit --skipLibCheck", "types": "bun tsc -p tsconfig.json --noEmit --skipLibCheck",
"build:types": "bun tsc --emitDeclarationOnly", "build:types": "bun tsc --emitDeclarationOnly",
"updater": "bun x npm-check-updates -ui", "updater": "bun x npm-check-updates -ui",
"pack": "rm -rf *.tgz && npm pack && mv *.tgz latest.tgz", "pack": "rm -rf *.tgz && npm pack && mv *.tgz latest.tgz",
"prepublishOnly": "bun run build" "prepublishOnly": "bun run build",
"tsdown": "bunx tsdown"
}, },
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"
@@ -33,11 +34,12 @@
"optionalDependencies": { "optionalDependencies": {
"@plasmicapp/loader-nextjs": "^1.0.0" "@plasmicapp/loader-nextjs": "^1.0.0"
}, },
"tsup": { "tsdown": {
"entry": [ "entry": [
"src/index.ts", "src/index.ts",
"src/nextjs/index.tsx" "src/nextjs/index.tsx"
], ],
"dts": false,
"minify": true, "minify": true,
"clean": true, "clean": true,
"external": [ "external": [