Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: enable to get bindings env from vars #31

Merged
merged 4 commits into from
Dec 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions .changeset/clean-seals-flash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
"hono-do": minor
---

feat: enable to get bindings env from vars

```ts
import { generateHonoObject } from "hono-do";

type Env = {
Bindings: {
KV: KVNamespace
};
}

export const Batcher = generateHonoObject<Env>("/", async (app, state, vars) => {
vars.env.KV // KVNamespace
})
```
21 changes: 20 additions & 1 deletion examples/batcher/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,25 @@ The code is based on [Cloudflare's Alarm example](https://developers.cloudflare.

```
pnpm install
pnpm dev
```

Setup the database:

```
pnpm create-db
```

Copy the outputted database configuration.
Add the database configuration to the `wrangler.toml` file.

Add the following to the `wrangler.toml` file's `[[d1_databases]]` section:

```
preview_database_id = "DB"
```

Run the following to start the batcher:

```
pnpm dev
```
3 changes: 2 additions & 1 deletion examples/batcher/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
"format": "prettier --write \"src/**/*.{ts,tsx}\"",
"format:check": "prettier --check \"src/**/*.{ts,tsx}\"",
"dev": "wrangler dev src/index.ts",
"deploy": "wrangler deploy --minify src/index.ts"
"deploy": "wrangler deploy --minify src/index.ts",
"create-db": "wrangler d1 create batch-db"
},
"dependencies": {
"hono": "^3.6.0",
Expand Down
59 changes: 37 additions & 22 deletions examples/batcher/src/batcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import { generateHonoObject } from "hono-do";

import { Env } from ".";

const SECONDS = 1000;

declare module "hono-do" {
Expand All @@ -10,29 +12,42 @@ declare module "hono-do" {
}
}

export const Batcher = generateHonoObject(
"/batcher",
async (app, state, vars) => {
const { storage } = state;
const vals = await storage.list({ reverse: true, limit: 1 });
vars.count = vals.size === 0 ? 0 : parseInt(vals.keys().next().value);

app.post("/", async (c) => {
vars.count++;

const currentAlarm = await storage.getAlarm();
if (currentAlarm == null) {
await storage.setAlarm(Date.now() + 10 * SECONDS);
}

await storage.put(vars.count.toString(), await c.req.text());
return c.json({ queued: vars.count });
});
},
).alarm(async (state, vars) => {
export const Batcher = generateHonoObject<
Env,
Record<string, never>,
"/batcher"
>("/batcher", async (app, state, vars) => {
await vars.env.DB.prepare(
"CREATE TABLE IF NOT EXISTS batcher (id INTEGER PRIMARY KEY AUTOINCREMENT, data TEXT)",
).run();

const { storage } = state;
const vals = await storage.list();
console.log(Array.from(vals.values())); // eg: POST other upstream service
const vals = await storage.list<string>({ reverse: true, limit: 1 });
vars.count = vals.size === 0 ? 0 : parseInt(vals.keys().next().value);

app.post("/", async (c) => {
vars.count++;

const currentAlarm = await storage.getAlarm();
if (currentAlarm == null) {
await storage.setAlarm(Date.now() + 10 * SECONDS);
}

await storage.put(vars.count.toString(), await c.req.text());
return c.json({ queued: vars.count });
});
}).alarm(async ({ storage }, vars) => {
const vals = await storage.list<string>();
let query = "INSERT INTO batcher (data) VALUES ";
const params: string[] = [];
for (const value of vals.values()) {
query += "(?),";
params.push(value);
}
query = query.slice(0, -1);
await vars.env.DB.prepare(query)
.bind(...params)
.run();
await storage.deleteAll();
vars.count = 0;
});
7 changes: 5 additions & 2 deletions examples/batcher/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { Hono } from "hono";

const app = new Hono<{
export interface Env {
Bindings: {
BATCHER: DurableObjectNamespace;
DB: D1Database;
};
}>();
}

const app = new Hono<Env>();

app.all("/batcher/*", (c) => {
const id = c.env.BATCHER.idFromName("Batcher");
Expand Down
6 changes: 6 additions & 0 deletions examples/batcher/wrangler.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,9 @@ bindings = [{ name = "BATCHER", class_name = "Batcher" }]
[[migrations]]
tag = "v1"
new_classes = ["Batcher"]

[[d1_databases]]
binding = "DB"
database_name = "batch-db"
database_id = "ae9d3bb9-d843-4fab-9387-7aed0ad5b92b"
preview_database_id = "DB"
8 changes: 5 additions & 3 deletions packages/hono-do/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,21 @@ export function generateHonoObject<
cb: (
app: Hono<E, S, BasePath>,
state: DurableObjectState,
vars: HonoObjectVars,
vars: HonoObjectVars & { env: E["Bindings"] },
) => void | Promise<void>,
handlers: HonoObjectHandlers = {},
) {
const _handlers: HonoObjectHandlers = {
...handlers,
};

const honoObject = function (this, state) {
const honoObject = function (this, state, env) {
const app = new Hono<E, S, BasePath>().basePath(basePath);
this.app = app;
this.state = state;
this.vars = {};
this.vars = {
env,
};
state.blockConcurrencyWhile(async () => {
await cb(app, state, this.vars);
});
Expand Down
34 changes: 20 additions & 14 deletions packages/hono-do/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,52 +11,58 @@ interface HonoObjectState<
> {
app: Hono<E, S, BasePath>;
state: DurableObjectState;
vars: HonoObjectVars;
vars: HonoObjectVars & {
env: E["Bindings"];
};
}

export interface HonoObject<
E extends Env = Env,
S extends Schema = Record<string, never>,
BasePath extends string = "/",
> extends HonoObjectState<E, S, BasePath> {
(this: HonoObject<E, S, BasePath>, state: DurableObjectState): void;
alarm: (handler: AlarmHandler) => HonoObject<E, S, BasePath>;
(
this: HonoObject<E, S, BasePath>,
state: DurableObjectState,
env: E["Bindings"],
): void;
alarm: (handler: AlarmHandler<E>) => HonoObject<E, S, BasePath>;
webSocketMessage: (
handler: WebSocketMessageHandler,
handler: WebSocketMessageHandler<E>,
) => HonoObject<E, S, BasePath>;
webSocketClose: (
handler: WebSocketCloseHandler,
handler: WebSocketCloseHandler<E>,
) => HonoObject<E, S, BasePath>;
webSocketError: (
handler: WebSocketErrorHandler,
handler: WebSocketErrorHandler<E>,
) => HonoObject<E, S, BasePath>;
}

export type AlarmHandler = (
export type AlarmHandler<E extends Env = Env> = (
...args: MergeArray<
Parameters<NonNullable<DurableObject["alarm"]>>,
[state: DurableObjectState, vars: HonoObjectVars]
[state: DurableObjectState, vars: HonoObjectVars & { env: E["Bindings"] }]
>
) => ReturnType<NonNullable<DurableObject["alarm"]>>;

export type WebSocketMessageHandler = (
export type WebSocketMessageHandler<E extends Env = Env> = (
...args: MergeArray<
Parameters<NonNullable<DurableObject["webSocketMessage"]>>,
[state: DurableObjectState, vars: HonoObjectVars]
[state: DurableObjectState, vars: HonoObjectVars & { env: E["Bindings"] }]
>
) => ReturnType<NonNullable<DurableObject["webSocketMessage"]>>;

export type WebSocketCloseHandler = (
export type WebSocketCloseHandler<E extends Env = Env> = (
...args: MergeArray<
Parameters<NonNullable<DurableObject["webSocketClose"]>>,
[state: DurableObjectState, vars: HonoObjectVars]
[state: DurableObjectState, vars: HonoObjectVars & { env: E["Bindings"] }]
>
) => ReturnType<NonNullable<DurableObject["webSocketClose"]>>;

export type WebSocketErrorHandler = (
export type WebSocketErrorHandler<E extends Env = Env> = (
...args: MergeArray<
Parameters<NonNullable<DurableObject["webSocketError"]>>,
[state: DurableObjectState, vars: HonoObjectVars]
[state: DurableObjectState, vars: HonoObjectVars & { env: E["Bindings"] }]
>
) => ReturnType<NonNullable<DurableObject["webSocketError"]>>;

Expand Down
3 changes: 2 additions & 1 deletion packages/hono-do/tests/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ describe("generateHonoObject", () => {
Bindings: {
HOGE_DB: D1Database;
};
}>("/", (app) => {
}>("/", (app, _, vars) => {
expectTypeOf(vars.env.HOGE_DB).toEqualTypeOf<D1Database>();
app.get("/hoge", async (c) => {
expectTypeOf(c.var.HOGE_VAR).toEqualTypeOf<string>();
expectTypeOf(c.env.HOGE_DB).toEqualTypeOf<D1Database>();
Expand Down