mirror of
https://github.com/bknd-io/bknd/
synced 2026-08-02 08:06:00 +00:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ee15150c78 | |||
| f8f5ef9c98 | |||
| ec015b7849 | |||
| 67e0374c04 | |||
| 9380091ba9 | |||
| aa66a81b27 | |||
| 0c15ec1434 | |||
| 7b8c7f1ae4 |
+1
-1
@@ -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": {
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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];
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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
@@ -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`:
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user