Compare commits

...

21 Commits

Author SHA1 Message Date
Cameron Pak 99c783e7f0 rm plan doc 2026-03-07 10:34:01 -06:00
Cameron Pak 4defc4d1e7 refactor(cli): use errore for typed error handling in run command
Replace untyped catch blocks and scattered process.exit() with
errore tagged errors and error-as-values pattern. Compile-time
safety for error handling paths.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 10:30:21 -06:00
Cameron Pak fd1323fecc Add simplified command to build bknd 2026-03-07 10:05:15 -06:00
Cameron Pak 44d7c092f8 fix(cli): detect bun: protocol imports and re-exec under Bun
Catch ERR_UNSUPPORTED_ESM_URL_SCHEME with bun: protocol in addition
to ReferenceError "Bun is not defined" when deciding to re-exec.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 10:02:16 -06:00
Cameron Pak 45cf746855 fix(cli): refine type-stripping version check and add build:all script
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 10:02:11 -06:00
Cameron Pak db961b5447 fix(cli): replace jiti with Node native type-stripping for TS configs
Drop jiti dependency. Node >=22.18 imports .ts natively; 22.13–22.17 re-execs with --experimental-strip-types. Bun re-exec unchanged.
2026-03-07 09:50:56 -06:00
Cameron Pak 48d432ddb2 fix(cli): add re-exec loop guard and robust Bun error matching
Prevent infinite re-exec loop when Bun is installed but config still
fails. Use regex for error matching instead of exact string comparison.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 08:55:37 -06:00
Cameron Pak 980886b5c5 fix(cli): fix TS2345 for process.argv[1] type narrowing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 08:48:16 -06:00
Cameron Pak 827e7faa7f fix(cli): support loading bknd.config.ts and detect runtime mismatch
Add jiti to handle TypeScript config imports natively in the CLI, removing the need for npx tsx or --experimental-strip-types workarounds. Detect Bun adapter usage under Node and suggest running CLI with the correct runtime.

Closes #360
2026-03-07 08:41:04 -06:00
Cameron Pak e6b17f6424 fix(cli): support loading bknd.config.ts without tsx workaround
Add jiti as a dependency to handle TypeScript config imports natively in the CLI, removing the need for npx tsx or --experimental-strip-types workarounds.

Closes #360
2026-03-07 08:06:51 -06:00
Cameron Pak 7832328a50 Merge pull request #359 from jonaspm/patch-1
Update client:only attribute in astro.mdx
2026-03-03 11:37:59 -06:00
Jonás Perusquía Morales e5bfc602e1 Update client:only attribute in astro.mdx
Astro requires client:only to specify the frontend library used
2026-03-03 11:27:59 -06:00
Cameron Pak 3894271b17 Merge pull request #353 from jonaspm/docs/auth-change-password
docs: Document changePassword method in auth module
2026-02-28 11:38:33 -06:00
Jonas Perusquia Morales cc1e1976f7 chore: Document error cases for password update method 2026-02-23 13:03:37 -06:00
dswbx 224d98879a Merge pull request #356 from jonaspm/fix/cross-platform-cleanup
Make dist clean cross-platform without shell commands
2026-02-20 18:00:46 +01:00
Jonas Perusquia Morales d1522c97ea Make dist clean cross-platform without shell commands 2026-02-20 09:50:30 -06:00
Jonas Perusquia Morales 8805f017a1 Update auth docs to clarify server module usage 2026-02-18 11:12:14 -06:00
Jonas Perusquia Morales 0ef06375c0 docs: Document changePassword method in auth module 2026-02-18 11:03:58 -06:00
dswbx 3d5c2cd3a8 Merge pull request #350 from xretic/main
refactor adapter tests for improved structure
2026-02-11 10:02:56 +01:00
roma fc60dfd042 refactor adapter tests for improved structure
reduced test duplication by extracting a small helper to strip connection from configs
2026-02-09 03:07:58 +01:00
Cameron Pak c8ee30b3da Merge pull request #348 from bknd-io/intro-docs-updates
Docs: consolidate dual introductions, restructure setup page
2026-02-07 17:23:21 -06:00
9 changed files with 255 additions and 81 deletions
+57 -47
View File
@@ -1,66 +1,76 @@
import { expect, describe, it, beforeAll, afterAll, mock } from "bun:test";
import * as adapter from "adapter";
import { disableConsoleLog, enableConsoleLog } from "core/utils";
import { disableConsoleLog, enableConsoleLog, omitKeys } from "core/utils";
import { adapterTestSuite } from "adapter/adapter-test-suite";
import { bunTestRunner } from "adapter/bun/test";
import { omitKeys } from "core/utils";
const stripConnection = <T extends Record<string, any>>(cfg: T) =>
omitKeys(cfg, ["connection"]);
beforeAll(disableConsoleLog);
afterAll(enableConsoleLog);
describe("adapter", () => {
it("makes config", async () => {
expect(omitKeys(await adapter.makeConfig({}), ["connection"])).toEqual({});
expect(
omitKeys(await adapter.makeConfig({}, { env: { TEST: "test" } }), ["connection"]),
).toEqual({});
describe("makeConfig", () => {
it("returns empty config for empty inputs", async () => {
const cases: Array<Parameters<typeof adapter.makeConfig>> = [
[{}],
[{}, { env: { TEST: "test" } }],
];
// merges everything returned from `app` with the config
expect(
omitKeys(
await adapter.makeConfig(
{ app: (a) => ({ config: { server: { cors: { origin: a.env.TEST } } } }) },
{ env: { TEST: "test" } },
),
["connection"],
),
).toEqual({
config: { server: { cors: { origin: "test" } } },
});
});
for (const args of cases) {
const cfg = await adapter.makeConfig(...(args as any));
expect(stripConnection(cfg)).toEqual({});
}
});
it("allows all properties in app function", async () => {
const called = mock(() => null);
const config = await adapter.makeConfig(
{
app: (env) => ({
connection: { url: "test" },
config: { server: { cors: { origin: "test" } } },
options: {
mode: "db",
},
onBuilt: () => {
called();
expect(env).toEqual({ foo: "bar" });
},
}),
},
{ foo: "bar" },
it("merges app output into config", async () => {
const cfg = await adapter.makeConfig(
{ app: (a) => ({ config: { server: { cors: { origin: a.env.TEST } } } }) },
{ env: { TEST: "test" } },
);
expect(config.connection).toEqual({ url: "test" });
expect(config.config).toEqual({ server: { cors: { origin: "test" } } });
expect(config.options).toEqual({ mode: "db" });
await config.onBuilt?.(null as any);
expect(called).toHaveBeenCalled();
});
adapterTestSuite(bunTestRunner, {
expect(stripConnection(cfg)).toEqual({
config: { server: { cors: { origin: "test" } } },
});
});
it("allows all properties in app() result", async () => {
const called = mock(() => null);
const cfg = await adapter.makeConfig(
{
app: (env) => ({
connection: { url: "test" },
config: { server: { cors: { origin: "test" } } },
options: { mode: "db" as const },
onBuilt: () => {
called();
expect(env).toEqual({ foo: "bar" });
},
}),
},
{ foo: "bar" },
);
expect(cfg.connection).toEqual({ url: "test" });
expect(cfg.config).toEqual({ server: { cors: { origin: "test" } } });
expect(cfg.options).toEqual({ mode: "db" });
await cfg.onBuilt?.({} as any);
expect(called).toHaveBeenCalledTimes(1);
});
});
describe("adapter test suites", () => {
adapterTestSuite(bunTestRunner, {
makeApp: adapter.createFrameworkApp,
label: "framework app",
});
});
adapterTestSuite(bunTestRunner, {
adapterTestSuite(bunTestRunner, {
makeApp: adapter.createRuntimeApp,
label: "runtime app",
});
});
});
});
+13 -2
View File
@@ -2,7 +2,7 @@ import { $ } from "bun";
import * as tsup from "tsup";
import pkg from "./package.json" with { type: "json" };
import c from "picocolors";
import { watch as fsWatch } from "node:fs";
import { watch as fsWatch, readdirSync, rmSync } from "node:fs";
import { join } from "node:path";
const args = process.argv.slice(2);
@@ -27,7 +27,18 @@ const define = {
if (clean) {
console.info("Cleaning dist (w/o static)");
await $`find dist -mindepth 1 ! -path "dist/static/*" ! -path "dist/static" -exec rm -rf {} +`;
// Cross-platform clean: remove all files/folders in dist except static
const distPath = join(import.meta.dir, "dist");
try {
const entries = readdirSync(distPath);
for (const entry of entries) {
if (entry === "static") continue;
const entryPath = join(distPath, entry);
rmSync(entryPath, { recursive: true, force: true });
}
} catch (e) {
// dist may not exist yet, ignore
}
}
let types_running = false;
+2 -1
View File
@@ -63,7 +63,7 @@
"dayjs": "^1.11.19",
"fast-xml-parser": "^5.3.1",
"hono": "4.10.4",
"json-schema-library": "10.0.0-rc7",
"json-schema-library": "10.0.0-rc7",
"json-schema-to-ts": "^3.1.1",
"jsonv-ts": "^0.10.1",
"kysely": "0.28.8",
@@ -108,6 +108,7 @@
"clsx": "^2.1.1",
"commander": "^14.0.2",
"dotenv": "^17.2.3",
"errore": "^0.14.0",
"jotai": "^2.12.2",
"jsdom": "^26.1.0",
"kysely-generic-sqlite": "^1.2.1",
+149 -6
View File
@@ -4,7 +4,9 @@ import type { CliBkndConfig, CliCommand } from "cli/types";
import { Option } from "commander";
import { config, type App, type CreateAppConfig, type MaybePromise, registries } from "bknd";
import dotenv from "dotenv";
import * as errore from "errore";
import c from "picocolors";
import { execFileSync } from "node:child_process";
import path from "node:path";
import {
PLATFORMS,
@@ -18,6 +20,26 @@ import { createRuntimeApp, makeConfig } from "bknd/adapter";
import { colorizeConsole, isBun } from "bknd/utils";
import { withConfigOptions, type WithConfigOptions } from "cli/utils/options";
class ConfigLoadError extends errore.createTaggedError({
name: "ConfigLoadError",
message: "Failed to load config: $reason",
}) {}
class NeedsBunError extends errore.createTaggedError({
name: "NeedsBunError",
message: "Config requires Bun runtime",
}) {}
class NeedsTypeStrippingError extends errore.createTaggedError({
name: "NeedsTypeStrippingError",
message: "Node $version needs --experimental-strip-types for .ts config",
}) {}
class ReexecFailedError extends errore.createTaggedError({
name: "ReexecFailedError",
message: "Re-exec failed: $reason",
}) {}
const env_files = [".env", ".dev.vars"];
dotenv.config({
path: env_files.map((file) => path.resolve(process.cwd(), file)),
@@ -55,6 +77,106 @@ if (!registries.media.has(local)) {
registries.media.register(local, StorageLocalAdapter);
}
function needsTypeStripping(configFilePath: string): boolean {
if (!/\.[mc]?ts$/.test(configFilePath)) return false;
const [major, minor] = process.versions.node.split(".").map(Number);
// Node v22.06 introduced experimental TypeScript support via strip types.
return major === 22 && minor! < 18 && minor! > 5;
}
function reexecWithTypeStripping(): ReexecFailedError | never {
if (process.env.__BKND_REEXEC) {
return new ReexecFailedError({
reason: "TS config still failed after re-exec with --experimental-strip-types",
});
}
const cliPath = path.resolve(process.argv[1]!);
const args = ["--experimental-strip-types", cliPath, ...process.argv.slice(2)];
console.info(
c.yellow("Node <22.18 detected, re-executing with --experimental-strip-types"),
);
const result = errore.try({
try: () =>
execFileSync(process.execPath, args, {
stdio: "inherit",
env: { ...process.env, __BKND_REEXEC: "1" },
}),
catch: (e) =>
new ReexecFailedError({
reason: "Failed to re-exec with --experimental-strip-types",
cause: e,
}),
});
if (result instanceof Error) return result;
process.exit(0);
}
async function loadConfigFile(
configFilePath: string,
): Promise<ConfigLoadError | NeedsBunError | NeedsTypeStrippingError | CliBkndConfig> {
if (needsTypeStripping(configFilePath) && !process.execArgv.includes("--experimental-strip-types")) {
return new NeedsTypeStrippingError({
version: process.versions.node,
});
}
const result = await errore.tryAsync({
try: () => import(configFilePath).then((m) => m.default as CliBkndConfig),
catch: (e) => {
const needsBun =
(e instanceof ReferenceError && /\bBun\b.*not defined/.test(e.message)) ||
(e instanceof Error && "code" in e && e.code === "ERR_UNSUPPORTED_ESM_URL_SCHEME" && /bun:/.test(e.message));
if (needsBun) return new NeedsBunError();
return new ConfigLoadError({
reason: e instanceof Error ? e.message : String(e),
cause: e,
});
},
});
return result;
}
function reexecUnderBun(): ReexecFailedError | never {
if (process.env.__BKND_REEXEC) {
return new ReexecFailedError({
reason: "Config requires Bun but still failed under Bun runtime",
});
}
const bunPath = process.env.BUN_INSTALL
? path.join(process.env.BUN_INSTALL, "bin", "bun")
: "bun";
const cliPath = path.resolve(process.argv[1]!);
const args = [cliPath, ...process.argv.slice(2)];
console.info(
c.yellow("Config requires Bun runtime, re-executing:"),
c.cyan(`bun ${args.join(" ")}`),
);
const result = errore.try({
try: () =>
execFileSync(bunPath, args, {
stdio: "inherit",
env: { ...process.env, __BKND_REEXEC: "1" },
}),
catch: (e) =>
new ReexecFailedError({
reason: "Could not re-exec under Bun. Install Bun (https://bun.sh) or use bknd/adapter/node in your config.",
cause: e,
}),
});
if (result instanceof Error) return result;
process.exit(0);
}
type MakeAppConfig = {
connection?: CreateAppConfig["connection"];
server?: { platform?: Platform };
@@ -99,12 +221,33 @@ export async function makeAppFromEnv(options: Partial<RunOptions> = {}) {
// check configuration file to be present
} else if (configFilePath) {
console.info("Using config from", c.cyan(configFilePath));
try {
const config = (await import(configFilePath).then((m) => m.default)) as CliBkndConfig;
app = await makeConfigApp(config, options.server);
} catch (e) {
console.error("Failed to load config:", e);
process.exit(1);
const configResult = await loadConfigFile(configFilePath);
if (configResult instanceof Error) {
errore.matchError(configResult, {
NeedsTypeStrippingError: () => {
const reexecResult = reexecWithTypeStripping();
// only reached on failure — success calls process.exit(0)
console.error(c.red(reexecResult.message));
process.exit(1);
},
NeedsBunError: () => {
const reexecResult = reexecUnderBun();
// only reached on failure — success calls process.exit(0)
console.error(c.red(reexecResult.message));
process.exit(1);
},
ConfigLoadError: (e) => {
console.error(c.red("Failed to load config:"), e.reason);
process.exit(1);
},
Error: (e) => {
console.error(c.red("Failed to load config:"), e.message);
process.exit(1);
},
});
} else {
app = await makeConfigApp(configResult, options.server);
}
// try to use an in-memory connection
+6 -5
View File
@@ -79,6 +79,7 @@
"clsx": "^2.1.1",
"commander": "^14.0.2",
"dotenv": "^17.2.3",
"errore": "^0.14.0",
"jotai": "^2.12.2",
"jsdom": "^26.1.0",
"kysely-generic-sqlite": "^1.2.1",
@@ -1936,6 +1937,8 @@
"error-stack-parser-es": ["error-stack-parser-es@1.0.5", "", {}, "sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA=="],
"errore": ["errore@0.14.0", "", { "bin": { "errore": "dist/cli.js" } }, "sha512-2RI7BGdxWlJe6yJ3DK0pIvLNzXEE4M41VmF2HrH7C2HldTjYiORQNJ+ufsvAvOUGW/4rRHPFOpooP3ebIRIEXw=="],
"es-abstract": ["es-abstract@1.23.9", "", { "dependencies": { "array-buffer-byte-length": "^1.0.2", "arraybuffer.prototype.slice": "^1.0.4", "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.3", "data-view-buffer": "^1.0.2", "data-view-byte-length": "^1.0.2", "data-view-byte-offset": "^1.0.1", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "es-set-tostringtag": "^2.1.0", "es-to-primitive": "^1.3.0", "function.prototype.name": "^1.1.8", "get-intrinsic": "^1.2.7", "get-proto": "^1.0.0", "get-symbol-description": "^1.1.0", "globalthis": "^1.0.4", "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", "has-proto": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "internal-slot": "^1.1.0", "is-array-buffer": "^3.0.5", "is-callable": "^1.2.7", "is-data-view": "^1.0.2", "is-regex": "^1.2.1", "is-shared-array-buffer": "^1.0.4", "is-string": "^1.1.1", "is-typed-array": "^1.1.15", "is-weakref": "^1.1.0", "math-intrinsics": "^1.1.0", "object-inspect": "^1.13.3", "object-keys": "^1.1.1", "object.assign": "^4.1.7", "own-keys": "^1.0.1", "regexp.prototype.flags": "^1.5.3", "safe-array-concat": "^1.1.3", "safe-push-apply": "^1.0.0", "safe-regex-test": "^1.1.0", "set-proto": "^1.0.0", "string.prototype.trim": "^1.2.10", "string.prototype.trimend": "^1.0.9", "string.prototype.trimstart": "^1.0.8", "typed-array-buffer": "^1.0.3", "typed-array-byte-length": "^1.0.3", "typed-array-byte-offset": "^1.0.4", "typed-array-length": "^1.0.7", "unbox-primitive": "^1.1.0", "which-typed-array": "^1.1.18" } }, "sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA=="],
"es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="],
@@ -2462,7 +2465,7 @@
"jest-worker": ["jest-worker@24.9.0", "", { "dependencies": { "merge-stream": "^2.0.0", "supports-color": "^6.1.0" } }, "sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw=="],
"jiti": ["jiti@1.21.7", "", { "bin": { "jiti": "bin/jiti.js" } }, "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A=="],
"jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="],
"jotai": ["jotai@2.15.1", "", { "peerDependencies": { "@babel/core": ">=7.0.0", "@babel/template": ">=7.0.0", "@types/react": ">=17.0.0", "react": ">=17.0.0" }, "optionalPeers": ["@babel/core", "@babel/template", "@types/react", "react"] }, "sha512-yHT1HAZ3ba2Q8wgaUQ+xfBzEtcS8ie687I8XVCBinfg4bNniyqLIN+utPXWKQE93LMF5fPbQSVRZqgpcN5yd6Q=="],
@@ -3846,7 +3849,7 @@
"@babel/traverse/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
"@bknd/plasmic/@types/bun": ["@types/bun@1.3.8", "", { "dependencies": { "bun-types": "1.3.8" } }, "sha512-3LvWJ2q5GerAXYxO2mffLTqOzEu5qnhEAlh48Vnu8WQfnmSwbgagjGZV6BoHKJztENYEDn6QmVd949W4uESRJA=="],
"@bknd/plasmic/@types/bun": ["@types/bun@1.3.10", "", { "dependencies": { "bun-types": "1.3.10" } }, "sha512-0+rlrUrOrTSskibryHbvQkDOWRJwJZqZlxrUs1u4oOoTln8+WIXBPmAuCF35SWB2z4Zl3E84Nl/D0P7803nigQ=="],
"@bundled-es-modules/tough-cookie/tough-cookie": ["tough-cookie@4.1.4", "", { "dependencies": { "psl": "^1.1.33", "punycode": "^2.1.1", "universalify": "^0.2.0", "url-parse": "^1.5.3" } }, "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag=="],
@@ -4046,8 +4049,6 @@
"@swc/helpers/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@tailwindcss/node/jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="],
"@tailwindcss/node/lightningcss": ["lightningcss@1.30.2", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.30.2", "lightningcss-darwin-arm64": "1.30.2", "lightningcss-darwin-x64": "1.30.2", "lightningcss-freebsd-x64": "1.30.2", "lightningcss-linux-arm-gnueabihf": "1.30.2", "lightningcss-linux-arm64-gnu": "1.30.2", "lightningcss-linux-arm64-musl": "1.30.2", "lightningcss-linux-x64-gnu": "1.30.2", "lightningcss-linux-x64-musl": "1.30.2", "lightningcss-win32-arm64-msvc": "1.30.2", "lightningcss-win32-x64-msvc": "1.30.2" } }, "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ=="],
"@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.6.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-zq/ay+9fNIJJtJiZxdTnXS20PllcYMX3OE23ESc4HK/bdYu3cOWYVhsOhVnXALfU/uqJIxn5NBPd9z4v+SfoSg=="],
@@ -4750,7 +4751,7 @@
"@babel/preset-env/babel-plugin-polyfill-regenerator/@babel/helper-define-polyfill-provider": ["@babel/helper-define-polyfill-provider@0.6.3", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.22.6", "@babel/helper-plugin-utils": "^7.22.5", "debug": "^4.1.1", "lodash.debounce": "^4.0.8", "resolve": "^1.14.2" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-HK7Bi+Hj6H+VTHA3ZvBis7V/6hu9QuTrnMXNybfUf2iiuU/N97I8VjB+KbhFF8Rld/Lx5MzoCwPCpPjfK+n8Cg=="],
"@bknd/plasmic/@types/bun/bun-types": ["bun-types@1.3.8", "", { "dependencies": { "@types/node": "*" } }, "sha512-fL99nxdOWvV4LqjmC+8Q9kW3M4QTtTR1eePs94v5ctGqU8OeceWrSUaRw3JYb7tU3FkMIAjkueehrHPPPGKi5Q=="],
"@bknd/plasmic/@types/bun/bun-types": ["bun-types@1.3.10", "", { "dependencies": { "@types/node": "*" } }, "sha512-tcpfCCl6XWo6nCVnpcVrxQ+9AYN1iqMIzgrSKYMB/fjLtV2eyAVEg7AxQJuCq/26R6HpKWykQXuSOq/21RYcbg=="],
"@bundled-es-modules/tough-cookie/tough-cookie/universalify": ["universalify@0.2.0", "", {}, "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg=="],
@@ -170,7 +170,7 @@ export const prerender = false;
theme: "dark",
logo_return_path: "/../"
}}
client:only
client:only="react"
/>
</body>
</html>
@@ -30,6 +30,25 @@ With a focus on flexibility and ease of integration, bknd's authentication syste
---
## Server Module: `module.auth`
### `auth.changePassword([userId], [password])`
To change a user's password, use the `changePassword` method:
```ts
await app.module.auth.changePassword(user.id, password);
```
This method updates the password for the specified user.
- `userId`: The ID of the user whose password should be changed.
- `password`: The new password value.
This method throws an error if:
- The user with the given `userId` is not found.
- The user's authentication strategy is not `"password"` (e.g. the user registered via an OAuth provider).
## Securing Your Admin Portal
<Callout type="warn" title="Do not enable the Guard without an admin user">
@@ -95,27 +95,12 @@ export default {
The `app` function is useful if you need a cross-platform way to access the environment variables. For example, on Cloudflare Workers, you can only access environment variables inside a request handler. If you're exclusively using a node-like environment, it's safe to access the environment variables directly from `process.env`.
If you're using `npx bknd run`, make sure to create a file in a file format that `node` can load, otherwise you may run into an error that the file couldn't be found:
TypeScript configuration files (`.ts`) are fully supported and work out of the box with `npx bknd run` — no additional flags or tools required.
```
[INF] 2025-03-28 18:02:21 Using config from bknd.config.ts
[ERR] 2025-03-28 18:02:21 Failed to load config: Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'bknd.config.ts' imported from [...]
at packageResolve (node:internal/modules/esm/resolve:857:9)
at [...] {
code: 'ERR_MODULE_NOT_FOUND'
}
```
If you still want to use a `.ts` extension, you can start the CLI e.g. using `node` (>=v22.6.0):
If your config imports from a runtime-specific adapter (e.g. `bknd/adapter/bun`), you need to run the CLI with that runtime:
```sh
node --experimental-strip-types node_modules/.bin/bknd run
```
Or with `tsx`:
```sh
npx tsx node_modules/.bin/bknd run
bun node_modules/.bin/bknd run
```
### Turso/LibSQL database
+5 -1
View File
@@ -8,6 +8,7 @@
"node": ">=22.13"
},
"scripts": {
"build": "bun i && bun run --filter bknd build:all",
"updater": "bun x npm-check-updates -ui",
"ci": "find . -name 'node_modules' -type d -exec rm -rf {} + && bun install",
"npm:local": "verdaccio --config verdaccio.yml",
@@ -24,5 +25,8 @@
"typescript": "^5.9.3",
"verdaccio": "^6.2.1"
},
"workspaces": ["app", "packages/*"]
"workspaces": [
"app",
"packages/*"
]
}