Compare commits

...

8 Commits

Author SHA1 Message Date
dswbx ee15150c78 Merge pull request #109 from bknd-io/release/0.10
Release 0.10
2025-03-25 13:04:25 +01:00
dswbx f8f5ef9c98 bump version to 0.10 2025-03-25 13:03:15 +01:00
dswbx ec015b7849 docs: add postgres and sqlocal instructions 2025-03-25 13:02:09 +01:00
dswbx 67e0374c04 add change set to mutator insert/update after event 2025-03-21 19:32:24 +01:00
dswbx 9380091ba9 Merge pull request #116 from bknd-io/fix/s3-upload-in-node
fix s3 media upload in node environments by adding content length to request
2025-03-21 18:05:04 +01:00
dswbx aa66a81b27 Merge pull request #114 from bknd-io/feat/admin-colors-and-resize
admin ui: started color centralization + made sidebar resizable
2025-03-21 18:04:26 +01:00
dswbx 0c15ec1434 hide oauth client details with type password 2025-03-21 18:03:45 +01:00
dswbx 7b8c7f1ae4 fix s3 media upload in node environments by adding content length to request 2025-03-21 18:01:44 +01:00
7 changed files with 93 additions and 25 deletions
+1 -1
View File
@@ -3,7 +3,7 @@
"type": "module", "type": "module",
"sideEffects": false, "sideEffects": false,
"bin": "./dist/cli/index.js", "bin": "./dist/cli/index.js",
"version": "0.10.0-rc.5", "version": "0.10.0",
"description": "Lightweight Firebase/Supabase alternative built to run anywhere — incl. Next.js, React Router, Astro, Cloudflare, Bun, Node, AWS Lambda & more.", "description": "Lightweight Firebase/Supabase alternative built to run anywhere — incl. Next.js, React Router, Astro, Cloudflare, Bun, Node, AWS Lambda & more.",
"homepage": "https://bknd.io", "homepage": "https://bknd.io",
"repository": { "repository": {
+9 -2
View File
@@ -167,7 +167,9 @@ export class Mutator<
const res = await this.single(query); const res = await this.single(query);
await this.emgr.emit(new Mutator.Events.MutatorInsertAfter({ entity, data: res.data })); await this.emgr.emit(
new Mutator.Events.MutatorInsertAfter({ entity, data: res.data, changed: validatedData }),
);
return res as any; return res as any;
} }
@@ -198,7 +200,12 @@ export class Mutator<
const res = await this.single(query); const res = await this.single(query);
await this.emgr.emit( await this.emgr.emit(
new Mutator.Events.MutatorUpdateAfter({ entity, entityId: id, data: res.data }), new Mutator.Events.MutatorUpdateAfter({
entity,
entityId: id,
data: res.data,
changed: validatedData,
}),
); );
return res as any; return res as any;
+6 -1
View File
@@ -18,7 +18,11 @@ export class MutatorInsertBefore extends Event<{ entity: Entity; data: EntityDat
}); });
} }
} }
export class MutatorInsertAfter extends Event<{ entity: Entity; data: EntityData }> { export class MutatorInsertAfter extends Event<{
entity: Entity;
data: EntityData;
changed: EntityData;
}> {
static override slug = "mutator-insert-after"; static override slug = "mutator-insert-after";
} }
export class MutatorUpdateBefore extends Event< export class MutatorUpdateBefore extends Event<
@@ -48,6 +52,7 @@ export class MutatorUpdateAfter extends Event<{
entity: Entity; entity: Entity;
entityId: PrimaryFieldType; entityId: PrimaryFieldType;
data: EntityData; data: EntityData;
changed: EntityData;
}> { }> {
static override slug = "mutator-update-after"; static override slug = "mutator-update-after";
} }
@@ -118,14 +118,20 @@ export class StorageS3Adapter extends AwsClient implements StorageAdapter {
const res = await this.fetch(url, { const res = await this.fetch(url, {
method: "PUT", method: "PUT",
body, body,
headers: isFile(body)
? {
// required for node environments
"Content-Length": String(body.size),
}
: {},
}); });
if (res.ok) { if (!res.ok) {
// "df20fcb574dba1446cf5ec997940492b" throw new Error(`Failed to upload object: ${res.status} ${res.statusText}`);
return String(res.headers.get("etag"));
} }
return undefined; // "df20fcb574dba1446cf5ec997940492b"
return String(res.headers.get("etag"));
} }
private async headObject( private async headObject(
+7 -10
View File
@@ -4,15 +4,12 @@
// there is no lifecycle or Hook in React that we can use to switch // there is no lifecycle or Hook in React that we can use to switch
// .current at the right timing." // .current at the right timing."
// So we will have to make do with this "close enough" approach for now. // So we will have to make do with this "close enough" approach for now.
import { useEffect, useRef } from "react"; import { useLayoutEffect, useRef } from "react";
import { isDebug } from "core";
export const useEvent = <Fn>(fn: Fn | ((...args: any[]) => any) | undefined): Fn => { export const useEvent = <Fn>(fn: Fn): Fn => {
const ref = useRef([fn, (...args) => ref[0](...args)]).current; if (isDebug()) {
// Per Dan Abramov: useInsertionEffect executes marginally closer to the console.warn("useEvent() is deprecated");
// correct timing for ref synchronization than useLayoutEffect on React 18. }
// See: https://github.com/facebook/react/pull/25881#issuecomment-1356244360 return fn;
useEffect(() => {
ref[0] = fn;
}, []);
return ref[1];
}; };
+2 -2
View File
@@ -240,8 +240,8 @@ const StrategyPasswordForm = () => {
const StrategyOAuthForm = () => { const StrategyOAuthForm = () => {
return ( return (
<> <>
<Field name="config.client.client_id" required /> <Field name="config.client.client_id" required inputProps={{ type: "password" }} />
<Field name="config.client.client_secret" required /> <Field name="config.client.client_secret" required inputProps={{ type: "password" }} />
</> </>
); );
}; };
+58 -5
View File
@@ -55,12 +55,65 @@ connection object to your new database:
} }
``` ```
### Custom Connection ### Cloudflare D1
<Note> Using the [Cloudflare Adapter](/integration/cloudflare), you can choose to use a D1 database binding. To do so, you only need to add a D1 database to your `wrangler.toml` and it'll pick up automatically. To manually specify which D1 database to take, you can specify it manually:
Follow the progress of custom connections on its [Github Issue](https://github.com/bknd-io/bknd/issues/24).
If you're interested, make sure to upvote so it can be prioritized.
</Note>
```ts
import { serve, d1 } from "bknd/adapter/cloudflare";
export default serve<Env>({
app: ({ env }) => d1({ binding: env.D1_BINDING })
});
```
### PostgreSQL
To use bknd with Postgres, you need to install the `@bknd/postgres` package. You can do so by running the following command:
```bash
npm install @bknd/postgres
```
This package uses `pg` under the hood. If you'd like to see `postgres` or any other flavor, please create an [issue on Github](https://github.com/bknd-io/bknd/issues/new).
To establish a connection to your database, you can use any connection options available on the [`pg`](https://node-postgres.com/apis/client) package. Here is a quick example using the [Node.js Adapter](http://localhost:3000/integration/node):
```js
import { serve } from "bknd/adapter/node";
import { PostgresConnection } from "@bknd/postgres";
/** @type {import("bknd/adapter/node").NodeBkndConfig} */
const config = {
connection: new PostgresConnection({
connectionString:
"postgresql://user:password@localhost:5432/database",
}),
};
serve(config);
```
### SQLocal
To use bknd with `sqlocal` for a offline expierence, you need to install the `@bknd/sqlocal` package. You can do so by running the following command:
```bash
npm install @bknd/sqlocal
```
This package uses `sqlocal` under the hood. Consult the [sqlocal documentation](https://sqlocal.dallashoffman.com/guide/setup) for connection options:
```js
import { createApp } from "bknd";
import { SQLocalConnection } from "@bknd/sqlocal";
const app = createApp({
connection: new SQLocalConnection({
databasePath: ":localStorage:",
verbose: true,
})
});
```
### Custom Connection
Any bknd app instantiation accepts as connection either `undefined`, a connection object like Any bknd app instantiation accepts as connection either `undefined`, a connection object like
described above, or an class instance that extends from `Connection`: described above, or an class instance that extends from `Connection`: