bkper 4.25.0 → 4.26.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/lib/agent/extensions/handoff.d.ts.map +1 -1
- package/lib/agent/extensions/handoff.js +2 -0
- package/lib/agent/extensions/handoff.js.map +1 -1
- package/lib/agent/startup-maintenance.d.ts +1 -0
- package/lib/agent/startup-maintenance.d.ts.map +1 -1
- package/lib/agent/startup-maintenance.js +7 -1
- package/lib/agent/startup-maintenance.js.map +1 -1
- package/lib/agent/system-prompt.js +1 -1
- package/lib/commands/apps/git/clone.js +1 -1
- package/lib/commands/apps/git/clone.js.map +1 -1
- package/lib/commands/apps/init.d.ts +3 -1
- package/lib/commands/apps/init.d.ts.map +1 -1
- package/lib/commands/apps/init.js +105 -58
- package/lib/commands/apps/init.js.map +1 -1
- package/lib/commands/apps/register.js +2 -2
- package/lib/commands/apps/register.js.map +1 -1
- package/lib/dev/local-outbound.d.ts.map +1 -1
- package/lib/dev/local-outbound.js +17 -3
- package/lib/dev/local-outbound.js.map +1 -1
- package/lib/dev/miniflare.js +1 -1
- package/lib/dev/miniflare.js.map +1 -1
- package/lib/dev/preflight.js +1 -1
- package/lib/dev/preflight.js.map +1 -1
- package/lib/dev/shared.js +1 -1
- package/lib/dev/shared.js.map +1 -1
- package/lib/docs/apps/ai.md +270 -0
- package/lib/docs/apps/app-listing.md +86 -0
- package/lib/docs/apps/architecture.md +188 -0
- package/lib/docs/apps/configuration.md +171 -0
- package/lib/docs/apps/context-menu.md +69 -0
- package/lib/docs/apps/deploying.md +181 -0
- package/lib/docs/apps/development.md +122 -0
- package/lib/docs/apps/event-handlers.md +248 -0
- package/lib/docs/apps/first-app.md +76 -0
- package/lib/docs/apps/overview.md +106 -0
- package/lib/docs/apps/self-hosted.md +63 -0
- package/lib/docs/apps/shared-app-source.md +85 -0
- package/lib/docs/cli/app-management.md +16 -4
- package/lib/docs/index.md +12 -1
- package/lib/docs/sdk/bkper-js.md +5 -0
- package/lib/upgrade/index.d.ts +1 -1
- package/lib/upgrade/index.d.ts.map +1 -1
- package/lib/upgrade/index.js +1 -1
- package/lib/upgrade/index.js.map +1 -1
- package/lib/upgrade/installation.d.ts +4 -0
- package/lib/upgrade/installation.d.ts.map +1 -1
- package/lib/upgrade/installation.js +23 -0
- package/lib/upgrade/installation.js.map +1 -1
- package/package.json +3 -3
- package/lib/docs/apps/app-building.md +0 -1429
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# Development Experience
|
|
2
|
+
|
|
3
|
+
Local development uses two composable processes — the worker runtime and the client dev server — that run concurrently.
|
|
4
|
+
|
|
5
|
+
## What runs
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm run dev
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
The project template runs both processes via `concurrently`:
|
|
12
|
+
|
|
13
|
+
1. **`vite dev`** — Client dev server with HMR. Changes to Lit components reflect instantly in the browser. Configured in `client/vite.config.ts`.
|
|
14
|
+
2. **`bkper app dev`** — The worker runtime:
|
|
15
|
+
- **Miniflare** — Simulates the single Cloudflare Worker locally.
|
|
16
|
+
- **Cloudflare tunnel** — Exposes `/events` via a public URL so Bkper can route webhook events to your machine.
|
|
17
|
+
- **File watching** — Server changes trigger automatic rebuilds via esbuild.
|
|
18
|
+
|
|
19
|
+
You can also run them independently: `npm run dev:client` for just the UI, or `npm run dev:server` for the local Worker.
|
|
20
|
+
|
|
21
|
+
## URLs
|
|
22
|
+
|
|
23
|
+
| Endpoint | URL |
|
|
24
|
+
| -------------------------------------- | ------------------------------------------- |
|
|
25
|
+
| Client (Vite dev server) | `http://localhost:5173` |
|
|
26
|
+
| Server Worker (Miniflare) | `http://localhost:8787` |
|
|
27
|
+
| App API routes | `http://localhost:8787/api/*` |
|
|
28
|
+
| App OpenAPI spec | `http://localhost:8787/openapi.json` |
|
|
29
|
+
| Events (via tunnel to the same Worker) | `https://<random>.trycloudflare.com/events` |
|
|
30
|
+
|
|
31
|
+
The Vite dev server proxies `/api` requests to `http://localhost:8787` through `client/vite.config.ts`. The app OpenAPI spec is served by the Worker at `http://localhost:8787/openapi.json`. The tunnel URL is automatically registered as `webhookUrlDev`, so development-mode events are routed to your local machine.
|
|
32
|
+
|
|
33
|
+
## Configuration flags
|
|
34
|
+
|
|
35
|
+
There is one local Worker. Override its port when needed:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
bkper app dev --sp 8787
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Client configuration
|
|
42
|
+
|
|
43
|
+
The client dev server is configured in `client/vite.config.ts`. This standard Vite configuration registers local auth middleware and proxies `/api` requests to the Worker.
|
|
44
|
+
|
|
45
|
+
### Local development authentication
|
|
46
|
+
|
|
47
|
+
During local development, the Vite dev server runs `createBkperAuthMiddleware()` from `bkper/dev`. It serves the local `/auth/refresh` endpoint used by `@bkper/web-auth`, obtaining OAuth tokens from your CLI credentials.
|
|
48
|
+
|
|
49
|
+
The separate Vite proxy configuration forwards `/api` requests to the Miniflare Worker.
|
|
50
|
+
|
|
51
|
+
Before starting development, run:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
bkper auth login # one-time setup
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Then `npm run dev` handles local authentication. Direct `bkper-js` calls use `auth.getAccessToken()`, while the typed app API client uses `auth.authenticatedFetch()` to attach and refresh bearer authentication.
|
|
58
|
+
|
|
59
|
+
Local outbound uses your CLI credentials when the app server or event handler calls Bkper.
|
|
60
|
+
|
|
61
|
+
If you see authentication errors in the browser, verify you're logged in:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
bkper auth token # should print a token
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
This is the canonical pattern for local development. Do not manually pass tokens or implement custom auth flows.
|
|
68
|
+
|
|
69
|
+
## Local secrets
|
|
70
|
+
|
|
71
|
+
Environment variables for local development live in a `.dev.vars` file at the project root:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
# .dev.vars (gitignored)
|
|
75
|
+
EXTERNAL_SERVICE_TOKEN=your-token-here
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Copy from the provided template:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
cp .dev.vars.example .dev.vars
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
These variables are available as `c.env.SECRET_NAME` in your Hono handlers during development.
|
|
85
|
+
|
|
86
|
+
## KV storage
|
|
87
|
+
|
|
88
|
+
KV data persists locally in the `.mf/kv/` directory during development. This means your data survives restarts — useful for testing caching and state patterns.
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
// Read
|
|
92
|
+
const value = await c.env.KV.get('my-key');
|
|
93
|
+
|
|
94
|
+
// Write with TTL
|
|
95
|
+
await c.env.KV.put('my-key', 'value', { expirationTtl: 3600 });
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
See the [Cloudflare KV documentation](https://developers.cloudflare.com/kv/) for more usage patterns.
|
|
99
|
+
|
|
100
|
+
## Type generation
|
|
101
|
+
|
|
102
|
+
The `env.d.ts` file provides TypeScript types for the Worker environment — KV bindings, secrets, and other platform services. It's auto-generated based on your `bkper.yaml` configuration and checked into version control.
|
|
103
|
+
|
|
104
|
+
Rebuild it after changing services or secrets in `bkper.yaml`:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
bkper app build
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## The development loop
|
|
111
|
+
|
|
112
|
+
1. Run `npm run dev`.
|
|
113
|
+
2. Edit client code and use Vite HMR.
|
|
114
|
+
3. Edit server code and let the Worker reload.
|
|
115
|
+
4. Trigger events in Bkper and inspect handler responses in the activity stream.
|
|
116
|
+
5. Run `npm run check` before considering the change complete.
|
|
117
|
+
|
|
118
|
+
## Debugging
|
|
119
|
+
|
|
120
|
+
- **Server errors** — Check the terminal output from `bkper app dev`. Worker runtime errors appear here.
|
|
121
|
+
- **Event handler errors** — Check the Bkper activity stream. Click on an event handler response to see the result or error, and replay failed events.
|
|
122
|
+
- **Client errors** — Use browser DevTools. The Vite dev server provides source maps.
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
# Event Handlers
|
|
2
|
+
|
|
3
|
+
Event handlers are the code that reacts to events in your Bkper Books. When a transaction is checked, an account is created, or any other event occurs, your handler receives it and can take action — calculate taxes, sync data between books, post to external services, and more.
|
|
4
|
+
|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
## How it works
|
|
8
|
+
|
|
9
|
+
1. You declare which events your app handles in [`bkper.yaml`](https://bkper.com/docs/build/apps/configuration.md)
|
|
10
|
+
2. Bkper sends an HTTP POST to your webhook URL when those events fire
|
|
11
|
+
3. Your handler processes the event and returns a response
|
|
12
|
+
|
|
13
|
+
On the [Bkper Platform](https://bkper.com/docs/build/apps/overview.md), events are routed to `/events` on your app's single Worker — including local development via tunnels. For [self-hosted](https://bkper.com/docs/build/apps/self-hosted.md) setups, you configure the webhook URL directly.
|
|
14
|
+
|
|
15
|
+
## Agent identity
|
|
16
|
+
|
|
17
|
+
Event handlers **run on behalf of the user who installed the app**. Their transactions and activities are identified in the UI by the app's logo and name:
|
|
18
|
+
|
|
19
|
+

|
|
20
|
+
|
|
21
|
+
## Responses
|
|
22
|
+
|
|
23
|
+
Handler responses are recorded in the activity that triggered the event. You can view and replay them by clicking the response at the bottom of the activity:
|
|
24
|
+
|
|
25
|
+

|
|
26
|
+
|
|
27
|
+
### Response format
|
|
28
|
+
|
|
29
|
+
Your handler must return a response in this format:
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
{ result?: string | string[] | boolean; error?: string; warning?: string }
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
- The `result` is recorded as the handler response in the book activity
|
|
36
|
+
- If you return `{ result: false }`, the response is suppressed and not recorded
|
|
37
|
+
- Errors like `{ error: "This is an error" }` show up as error responses
|
|
38
|
+
|
|
39
|
+
To show the full error stack trace for debugging:
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
try {
|
|
43
|
+
// handler logic
|
|
44
|
+
} catch (err) {
|
|
45
|
+
return { error: err instanceof Error ? err.message : String(err) };
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### HTML in responses
|
|
50
|
+
|
|
51
|
+
If you return an **HTML snippet** (e.g., a link) in the result, it will be rendered in the response popup.
|
|
52
|
+
|
|
53
|
+
## Development mode
|
|
54
|
+
|
|
55
|
+
Event handlers run in _Development Mode_ when executed by the **developer or owner** of the App.
|
|
56
|
+
|
|
57
|
+
In development mode, both successful results and errors are shown as responses:
|
|
58
|
+
|
|
59
|
+

|
|
60
|
+
|
|
61
|
+
You can click a response to **replay** failed executions — useful for debugging without recreating the triggering event.
|
|
62
|
+
|
|
63
|
+
To find transactions with bot errors in a book, run the query:
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
error:true
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Preventing loops
|
|
70
|
+
|
|
71
|
+
When your event handler creates or modifies transactions, those changes fire new events. To prevent infinite loops, check the `event.agent.id` field:
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
function handleEvent(event: bkper.Event) {
|
|
75
|
+
// Skip events triggered by this app
|
|
76
|
+
if (event.agent?.id === 'your-app-id') {
|
|
77
|
+
return { result: false };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Process the event
|
|
81
|
+
// ...
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
This pattern is essential for any handler that writes back to the same book.
|
|
86
|
+
|
|
87
|
+
## Authentication
|
|
88
|
+
|
|
89
|
+
Platform-hosted event handlers use the same server-side Bkper API pattern as `/api/*` routes:
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
const bkper = new Bkper();
|
|
93
|
+
const book = new Book(event.book, bkper.getConfig());
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Dispatch consumes the event delivery token, strips platform headers before your Worker runs, and platform outbound auth injects the OAuth token and app agent identity on Bkper API calls.
|
|
97
|
+
|
|
98
|
+
Do not read `bkper-oauth-token`, `bkper-agent-id`, or `Authorization` headers in platform app code.
|
|
99
|
+
|
|
100
|
+
> **Note**
|
|
101
|
+
> During local development, events are routed through the Cloudflare tunnel started by `bkper app dev`. Local outbound uses your CLI credentials when the handler calls Bkper.
|
|
102
|
+
For [self-hosted](https://bkper.com/docs/build/apps/self-hosted.md) setups, the event auth headers are sent to both `webhookUrl` and `webhookUrlDev` and must be handled directly by your infrastructure.
|
|
103
|
+
|
|
104
|
+
## Event routing pattern
|
|
105
|
+
|
|
106
|
+
On the Bkper Platform, your server Worker uses [Hono](https://hono.dev) to receive webhook calls at `/events`. A typical pattern routes events by type:
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
import { Bkper, Book } from 'bkper-js';
|
|
110
|
+
|
|
111
|
+
app.post('/events', async c => {
|
|
112
|
+
const event: bkper.Event = await c.req.json();
|
|
113
|
+
|
|
114
|
+
if (!event.book) {
|
|
115
|
+
return c.json({ error: 'Missing book in event payload' }, 400);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const bkper = new Bkper();
|
|
119
|
+
const book = new Book(event.book, bkper.getConfig());
|
|
120
|
+
|
|
121
|
+
switch (event.type) {
|
|
122
|
+
case 'TRANSACTION_CHECKED':
|
|
123
|
+
return c.json(await handleTransactionChecked(book, event));
|
|
124
|
+
default:
|
|
125
|
+
return c.json({ result: false });
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## The Event object
|
|
131
|
+
|
|
132
|
+
The event payload has the following structure:
|
|
133
|
+
|
|
134
|
+
```ts
|
|
135
|
+
{
|
|
136
|
+
/** The id of the Book associated to the Event */
|
|
137
|
+
bookId?: string;
|
|
138
|
+
|
|
139
|
+
/** The Book object associated with the Event */
|
|
140
|
+
book?: {
|
|
141
|
+
agentId?: string;
|
|
142
|
+
collection?: Collection;
|
|
143
|
+
createdAt?: string;
|
|
144
|
+
datePattern?: string;
|
|
145
|
+
decimalSeparator?: "DOT" | "COMMA";
|
|
146
|
+
fractionDigits?: number;
|
|
147
|
+
id?: string;
|
|
148
|
+
lastUpdateMs?: string;
|
|
149
|
+
lockDate?: string;
|
|
150
|
+
name?: string;
|
|
151
|
+
ownerName?: string;
|
|
152
|
+
pageSize?: number;
|
|
153
|
+
period?: "MONTH" | "QUARTER" | "YEAR";
|
|
154
|
+
periodStartMonth?: "JANUARY" | "FEBRUARY" | "MARCH" | "APRIL"
|
|
155
|
+
| "MAY" | "JUNE" | "JULY" | "AUGUST" | "SEPTEMBER"
|
|
156
|
+
| "OCTOBER" | "NOVEMBER" | "DECEMBER";
|
|
157
|
+
permission?: "OWNER" | "EDITOR" | "POSTER" | "RECORDER"
|
|
158
|
+
| "VIEWER" | "NONE";
|
|
159
|
+
properties?: { [name: string]: string };
|
|
160
|
+
timeZone?: string;
|
|
161
|
+
timeZoneOffset?: number;
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
/** The user in charge of the Event */
|
|
165
|
+
user?: {
|
|
166
|
+
avatarUrl?: string;
|
|
167
|
+
name?: string;
|
|
168
|
+
username?: string;
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
/** The Event agent, such as the App, Bot or Bank institution */
|
|
172
|
+
agent?: {
|
|
173
|
+
id?: string;
|
|
174
|
+
logo?: string;
|
|
175
|
+
name?: string;
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
/** The creation timestamp, in milliseconds */
|
|
179
|
+
createdAt?: string;
|
|
180
|
+
|
|
181
|
+
/** The event data */
|
|
182
|
+
data?: {
|
|
183
|
+
/** The object payload. Depends on the event type. */
|
|
184
|
+
object?: any;
|
|
185
|
+
/** The object previous attributes when updated */
|
|
186
|
+
previousAttributes?: { [name: string]: string };
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
/** The unique id that identifies the Event */
|
|
190
|
+
id?: string;
|
|
191
|
+
|
|
192
|
+
/** The resource associated to the Event */
|
|
193
|
+
resource?: string;
|
|
194
|
+
|
|
195
|
+
/** The type of the Event */
|
|
196
|
+
type?: EventType;
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
The event payload is the same structure exposed by the [REST API](https://bkper.com/docs/build/scripts/rest-api.md). If you use TypeScript, add the [`@bkper/bkper-api-types`](https://www.npmjs.com/package/@bkper/bkper-api-types) package to your project for full type definitions.
|
|
201
|
+
|
|
202
|
+
For update events, `data.previousAttributes` contains the fields that changed and their previous values — useful for computing diffs or reacting only to specific field changes.
|
|
203
|
+
|
|
204
|
+
## Event types
|
|
205
|
+
|
|
206
|
+
Declare which events your app handles in `bkper.yaml`:
|
|
207
|
+
|
|
208
|
+
```yaml
|
|
209
|
+
events:
|
|
210
|
+
- TRANSACTION_CHECKED
|
|
211
|
+
- TRANSACTION_POSTED
|
|
212
|
+
- ACCOUNT_CREATED
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
The complete current set of event types:
|
|
216
|
+
|
|
217
|
+
| Event | Description |
|
|
218
|
+
| --- | --- |
|
|
219
|
+
| `FILE_CREATED` | A file was attached to the book. |
|
|
220
|
+
| `FILE_UPDATED` | An attached file was updated. |
|
|
221
|
+
| `TRANSACTION_CREATED` | A draft transaction was created. |
|
|
222
|
+
| `TRANSACTION_UPDATED` | A transaction was updated. |
|
|
223
|
+
| `TRANSACTION_DELETED` | A transaction was deleted. |
|
|
224
|
+
| `TRANSACTION_POSTED` | A draft transaction was posted and now affects balances. |
|
|
225
|
+
| `TRANSACTION_CHECKED` | A posted transaction was checked (reviewed and locked). |
|
|
226
|
+
| `TRANSACTION_UNCHECKED` | A checked transaction was unchecked and becomes editable again. |
|
|
227
|
+
| `TRANSACTION_RESTORED` | A deleted transaction was restored. |
|
|
228
|
+
| `ACCOUNT_CREATED` | An account was created. |
|
|
229
|
+
| `ACCOUNT_UPDATED` | An account was updated. |
|
|
230
|
+
| `ACCOUNT_DELETED` | An account was deleted. |
|
|
231
|
+
| `QUERY_CREATED` | A saved query was created. |
|
|
232
|
+
| `QUERY_UPDATED` | A saved query was updated. |
|
|
233
|
+
| `QUERY_DELETED` | A saved query was deleted. |
|
|
234
|
+
| `GROUP_CREATED` | A group was created. |
|
|
235
|
+
| `GROUP_UPDATED` | A group was updated. |
|
|
236
|
+
| `GROUP_DELETED` | A group was deleted. |
|
|
237
|
+
| `COMMENT_CREATED` | A comment was added. |
|
|
238
|
+
| `COMMENT_DELETED` | A comment was deleted. |
|
|
239
|
+
| `COLLABORATOR_ADDED` | A collaborator was added to the book. |
|
|
240
|
+
| `COLLABORATOR_UPDATED` | A collaborator's permissions were updated. |
|
|
241
|
+
| `COLLABORATOR_REMOVED` | A collaborator was removed from the book. |
|
|
242
|
+
| `INTEGRATION_CREATED` | An integration was created in the book. |
|
|
243
|
+
| `INTEGRATION_UPDATED` | An integration was updated. |
|
|
244
|
+
| `INTEGRATION_DELETED` | An integration was deleted. |
|
|
245
|
+
| `BOOK_CREATED` | A book was created. |
|
|
246
|
+
| `BOOK_AUDITED` | A balances audit completed for the book. |
|
|
247
|
+
| `BOOK_UPDATED` | Book settings were updated. |
|
|
248
|
+
| `BOOK_DELETED` | The book was deleted. |
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Your First App
|
|
2
|
+
|
|
3
|
+
This tutorial walks you through building and deploying a Bkper app from scratch. For the deep reference on any topic — architecture, configuration, development, events, or deployment — follow the links in each step.
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
[Development Setup](https://bkper.com/docs/build/getting-started/setup.md) — the CLI installed and authenticated.
|
|
8
|
+
|
|
9
|
+
## Walkthrough
|
|
10
|
+
|
|
11
|
+
1. **Scaffold from the template**
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
bkper app init my-app
|
|
15
|
+
cd my-app
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
`bkper app init my-app` creates `./my-app` and uses `my-app` as the app id. The CLI sets your package name, URLs, and event-handler loop guards automatically. See [App Configuration](https://bkper.com/docs/build/apps/configuration.md) for the full `bkper.yaml` reference.
|
|
19
|
+
|
|
20
|
+
2. **Install and start developing**
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install
|
|
24
|
+
npm run dev
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
This runs the Vite client dev server and the local Worker runtime with automatic event tunneling. See [Development Experience](https://bkper.com/docs/build/apps/development.md) for details.
|
|
28
|
+
|
|
29
|
+
3. **Open the app**
|
|
30
|
+
|
|
31
|
+
Visit [http://localhost:5173](http://localhost:5173). Select a book to see account balances. No OAuth setup required — the platform handles authentication.
|
|
32
|
+
|
|
33
|
+
4. **Trigger an event**
|
|
34
|
+
|
|
35
|
+
Go to any Bkper book and check a transaction. The event handler creates a 20% draft using the original from and to Accounts. It does not affect balances unless posted. See [Event Handlers](https://bkper.com/docs/build/apps/event-handlers.md) for the full event model.
|
|
36
|
+
|
|
37
|
+
5. **Make a change**
|
|
38
|
+
|
|
39
|
+
Edit `server/src/events/handlers/transaction-checked.ts` and save. The Worker reloads automatically. Check another transaction to see your change.
|
|
40
|
+
|
|
41
|
+
6. **Customize your listing**
|
|
42
|
+
|
|
43
|
+
Update `bkper.yaml` with your app's description and owner details. Replace the placeholder logos in `client/public/images/`. See [App Listing](https://bkper.com/docs/build/apps/app-listing.md) for publishing details.
|
|
44
|
+
|
|
45
|
+
7. **Update the README**
|
|
46
|
+
|
|
47
|
+
Edit `README.md` for end users — what the app does and how to use it. If your app exposes `/api/*` routes for users or integrators, include the app API base URL, `/openapi.json` URL, and one minimal authenticated example. Keep deeper developer docs in `AGENTS.md`.
|
|
48
|
+
|
|
49
|
+
8. **Establish shared source**
|
|
50
|
+
|
|
51
|
+
Review the app, create its first commit, and sync it:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
git add .
|
|
55
|
+
git commit -m "Initial app"
|
|
56
|
+
bkper app sync
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
For an eligible standalone app without an external remote, sync creates private Bkper-managed source and configures it as `origin`. Authorized teammates and coding agents can then clone the same codebase with `bkper app clone my-app`. See [Shared App Source](https://bkper.com/docs/build/apps/shared-app-source.md) for access rules and other source workflows.
|
|
60
|
+
|
|
61
|
+
9. **Check and deploy**
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
npm run check
|
|
65
|
+
npm run deploy
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Deployment is explicit: syncing or pushing source does not deploy the app. Your app is live at `https://my-app.bkper.app`. See [Building & Deploying](https://bkper.com/docs/build/apps/deploying.md) for preview environments, secrets, and KV.
|
|
69
|
+
|
|
70
|
+
## Next steps
|
|
71
|
+
|
|
72
|
+
- [Shared App Source](https://bkper.com/docs/build/apps/shared-app-source.md) — Clone and improve one private app codebase together
|
|
73
|
+
- [App Architecture](https://bkper.com/docs/build/apps/architecture.md) — Understand the single Worker client/server structure
|
|
74
|
+
- [App Configuration](https://bkper.com/docs/build/apps/configuration.md) — Full `bkper.yaml` reference
|
|
75
|
+
- [Event Handlers](https://bkper.com/docs/build/apps/event-handlers.md) — All event types and patterns
|
|
76
|
+
- [Building & Deploying](https://bkper.com/docs/build/apps/deploying.md) — Preview environments and secrets
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# The Bkper Platform
|
|
2
|
+
|
|
3
|
+
The Bkper Platform is a complete managed environment for building, deploying, and hosting apps on Bkper. It removes infrastructure complexity so you can focus on business logic.
|
|
4
|
+
|
|
5
|
+
### Hosting
|
|
6
|
+
|
|
7
|
+
Apps are deployed to `{appId}.bkper.app` on a global edge network powered by [Cloudflare Workers for Platforms](https://developers.cloudflare.com/cloudflare-for-platforms/workers-for-platforms/). Your app runs close to your users, with zero infrastructure to manage.
|
|
8
|
+
|
|
9
|
+
Preview environments are built in — deploy to a preview URL to test before going to production.
|
|
10
|
+
|
|
11
|
+
### App APIs
|
|
12
|
+
|
|
13
|
+
The same Worker can expose app-defined `/api/*` routes. Treat those routes as the reusable contract for your app behavior:
|
|
14
|
+
|
|
15
|
+
- The bundled web client can call them.
|
|
16
|
+
- Scripts, external clients, and agents can call them too.
|
|
17
|
+
- The default template documents them with an app OpenAPI spec at `/openapi.json`.
|
|
18
|
+
|
|
19
|
+
### AI inference
|
|
20
|
+
|
|
21
|
+
When an app needs model inference, use Bkper AI by default. An authenticated app API route or event establishes the user and app identity, then platform outbound supplies authorization and usage attribution for the Worker's Bkper AI requests. The app does not need provider credentials.
|
|
22
|
+
|
|
23
|
+
See [Add Bkper AI to an App](https://bkper.com/docs/build/apps/ai.md) for live model discovery, strict structured output, validation, and the client-to-Worker authentication flow.
|
|
24
|
+
|
|
25
|
+
### Authentication
|
|
26
|
+
|
|
27
|
+
OAuth is pre-configured. No client IDs, no redirect URIs, no consent screens to build.
|
|
28
|
+
|
|
29
|
+
- **Web client** — Use `@bkper/web-auth`: `auth.getAccessToken()`. See [App Architecture → Client authentication](https://bkper.com/docs/build/apps/architecture.md#client-authentication).
|
|
30
|
+
- **Server API routes** — Send `Authorization: Bearer <token>` to `/api/*`; dispatch validates it and platform outbound injects auth for server-side Bkper API calls. See [App Architecture → Server API authentication](https://bkper.com/docs/build/apps/architecture.md#server-api-authentication).
|
|
31
|
+
- **Event handlers** — Handle `/events` in the same Worker and call Bkper with server-side `new Bkper()`; dispatch/outbound handle auth and agent identity. See [Event Handlers → Authentication](https://bkper.com/docs/build/apps/event-handlers.md#authentication).
|
|
32
|
+
- **Local development** — The Vite auth middleware uses your CLI credentials. See [Development Experience → Local development authentication](https://bkper.com/docs/build/apps/development.md#local-development-authentication).
|
|
33
|
+
|
|
34
|
+
### Services
|
|
35
|
+
|
|
36
|
+
Declare the services you need in [`bkper.yaml`](https://bkper.com/docs/build/apps/configuration.md) and the platform provisions them:
|
|
37
|
+
|
|
38
|
+
- **KV storage** — Key-value storage for caching and state. Access via `c.env.KV` in your handlers.
|
|
39
|
+
- **Secrets** — Securely stored environment variables. Set via `bkper app secrets put`, access via `c.env.SECRET_NAME`.
|
|
40
|
+
|
|
41
|
+
### Developer experience
|
|
42
|
+
|
|
43
|
+
The project template composes the full development environment:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
npm run dev
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
This runs two processes concurrently: `vite dev` for the client UI (HMR), and `bkper app dev` for the Worker runtime (Miniflare for `/api/*` and `/events`, plus a Cloudflare tunnel so Bkper can route webhook events to your laptop). Your entire development environment, running locally.
|
|
50
|
+
|
|
51
|
+
### Shared app source
|
|
52
|
+
|
|
53
|
+
Bkper can host one private codebase for your app. Authorized teammates and coding agents can clone it, improve it locally, and continue building from the same shared history.
|
|
54
|
+
|
|
55
|
+
Source synchronization remains separate from deployment. A Git push stores source but never builds or deploys the app.
|
|
56
|
+
|
|
57
|
+
See [Shared App Source](https://bkper.com/docs/build/apps/shared-app-source.md) for the collaboration workflow, access rules, and external Git options.
|
|
58
|
+
|
|
59
|
+
### Deployment
|
|
60
|
+
|
|
61
|
+
Check and deploy the app template:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
npm run check
|
|
65
|
+
npm run deploy
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Your app is live at `{appId}.bkper.app`. The platform handles routing, SSL, and edge distribution.
|
|
69
|
+
|
|
70
|
+
## What you'd build yourself without it
|
|
71
|
+
|
|
72
|
+
Without the platform, creating a Bkper app with a UI, event handling, and authentication requires:
|
|
73
|
+
|
|
74
|
+
| Concern | Without the platform | With the platform |
|
|
75
|
+
| ------------------------ | --------------------------------------------------------------------------------------- | ----------------------------------------------- |
|
|
76
|
+
| **Hosting** | Provision servers, configure domains, SSL, CDN | `bkper app deploy` |
|
|
77
|
+
| **Authentication** | Register OAuth client, build consent screen, handle token refresh, manage redirect URIs | `auth.getAccessToken()` |
|
|
78
|
+
| **Event webhooks** | Set up a public endpoint, configure DNS, handle JWT verification | Declare in `bkper.yaml`, platform routes events |
|
|
79
|
+
| **Local dev webhooks** | Install ngrok or similar, manually configure tunnel URL | `bkper app dev` starts tunnel automatically |
|
|
80
|
+
| **Secrets** | Set up a secrets manager, configure access | `bkper app secrets put` |
|
|
81
|
+
| **KV storage** | Deploy Redis/Memcached, manage connections | Declare `KV` in `bkper.yaml` |
|
|
82
|
+
| **Preview environments** | Build a staging pipeline | `bkper app deploy --preview` |
|
|
83
|
+
| **Shared app source** | Operate a separate private Git host | Managed source for app developers and agents |
|
|
84
|
+
| **Type safety** | Manually create type definitions | `env.d.ts` auto-generated |
|
|
85
|
+
|
|
86
|
+
The platform eliminates all of this. You write business logic, the platform handles infrastructure.
|
|
87
|
+
|
|
88
|
+
## Getting started
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
# Create a new app from the template
|
|
92
|
+
bkper app init my-app
|
|
93
|
+
cd my-app
|
|
94
|
+
|
|
95
|
+
# Install dependencies and start developing
|
|
96
|
+
npm install
|
|
97
|
+
npm run dev
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
This gives you a working app with a client UI, server API routes, and `/events` handling in one Worker — all running locally with full HMR and webhook tunneling.
|
|
101
|
+
|
|
102
|
+
## Next steps
|
|
103
|
+
|
|
104
|
+
- [Your First App](https://bkper.com/docs/build/apps/first-app.md) — Build and deploy a complete platform app
|
|
105
|
+
- [Shared App Source](https://bkper.com/docs/build/apps/shared-app-source.md) — Collaborate from one private codebase
|
|
106
|
+
- [App Architecture](https://bkper.com/docs/build/apps/architecture.md) — Understand how platform apps are structured
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Self-Hosted Alternative
|
|
2
|
+
|
|
3
|
+
The [Bkper Platform](https://bkper.com/docs/build/apps/overview.md) handles hosting, authentication, and deployment for you. However, you can host event handlers on your own infrastructure if you have specific requirements — existing cloud setup, compliance constraints, or legacy apps.
|
|
4
|
+
|
|
5
|
+
> **Tip**
|
|
6
|
+
> Use the Bkper Platform unless you have a specific reason to self-host. It eliminates the need to manage authentication, secrets, hosting, and deployment yourself.
|
|
7
|
+
## Cloud Functions
|
|
8
|
+
|
|
9
|
+
A Bkper event handler running on [Google Cloud Functions](https://cloud.google.com/functions/) receives authenticated calls from the `bkper-hrd@appspot.gserviceaccount.com` service account. You need to grant this service account the [Cloud Functions Invoker IAM role](https://cloud.google.com/functions/docs/securing/managing-access-iam) (`roles/cloudfunctions.invoker`).
|
|
10
|
+
|
|
11
|
+
Set the production endpoint in [`bkper.yaml`](https://bkper.com/docs/build/apps/configuration.md):
|
|
12
|
+
|
|
13
|
+
```yaml
|
|
14
|
+
webhookUrl: https://us-central1-my-project.cloudfunctions.net/events
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### Authentication
|
|
18
|
+
|
|
19
|
+
An OAuth Access Token **of the user who installed the app** is sent to the production `webhookUrl` endpoint in the `bkper-oauth-token` HTTP header, along with the agent identifier in `bkper-agent-id`, on each event. Your handler uses this token to call the API back on behalf of the user.
|
|
20
|
+
|
|
21
|
+
Both production (`webhookUrl`) and development (`webhookUrlDev`) endpoints receive OAuth tokens in the `bkper-oauth-token` header.
|
|
22
|
+
|
|
23
|
+
### Throughput and scaling
|
|
24
|
+
|
|
25
|
+
Event throughput can be high, especially when processing large batches. Set the [max instance limit](https://cloud.google.com/functions/docs/max-instances#setting_max_instances_limits) — usually **1-2 is enough**. When the function returns `429 Too Many Requests`, the event is automatically retried with incremental backoff until it receives an HTTP `200`.
|
|
26
|
+
|
|
27
|
+
### Response format
|
|
28
|
+
|
|
29
|
+
The function response must follow the standard format:
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
{ result?: any, error?: any }
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
See [Event Handlers](https://bkper.com/docs/build/apps/event-handlers.md#response-format) for details on response handling.
|
|
36
|
+
|
|
37
|
+
### Considerations
|
|
38
|
+
|
|
39
|
+
- Execution environment is subject to [Cloud Function Quotas](https://cloud.google.com/functions/quotas) — quota counts against the developer account, not the end user
|
|
40
|
+
- Recommended for scenarios where event throughput exceeds **1 event/second/user** and processing can be handled asynchronously
|
|
41
|
+
- Can be combined with context menus built with [Apps Script HTML Service](https://developers.google.com/apps-script/guides/html) or any other UI infrastructure
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## Generic Webhooks
|
|
46
|
+
|
|
47
|
+
You can host event handlers on any infrastructure — other cloud providers, containers, on-premise servers.
|
|
48
|
+
|
|
49
|
+
Configure the same `webhookUrl` property in [`bkper.yaml`](https://bkper.com/docs/build/apps/configuration.md):
|
|
50
|
+
|
|
51
|
+
```yaml
|
|
52
|
+
webhookUrl: https://my-server.example.com/bkper/events
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Authentication
|
|
56
|
+
|
|
57
|
+
Calls to the production webhook URL are signed with a JWT token using the [Service to Function](https://cloud.google.com/functions/docs/securing/authenticating#service-to-function) method. You can verify this token to assert the identity of the Bkper service.
|
|
58
|
+
|
|
59
|
+
> **Note**
|
|
60
|
+
> Cloud Functions handles JWT verification automatically. For other infrastructure, you need to implement verification yourself. We strongly recommend Cloud Functions for this reason.
|
|
61
|
+
### Retry behavior
|
|
62
|
+
|
|
63
|
+
If your infrastructure returns an HTTP `429` status, the event is automatically retried with incremental backoff until it receives an HTTP `200`. Use this to handle temporary overload gracefully.
|