@beignet/provider-db-drizzle 0.0.25 → 0.0.27
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/CHANGELOG.md +12 -0
- package/README.md +21 -11
- package/package.json +4 -2
- package/skills/database-provider/SKILL.md +190 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @beignet/provider-db-drizzle
|
|
2
2
|
|
|
3
|
+
## 0.0.27
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- f2461a9: Add lazy Next route server loaders and update generated route files and package docs to avoid booting providers during production build imports.
|
|
8
|
+
|
|
9
|
+
## 0.0.26
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 104302c: Refresh package-shipped TanStack Intent skills and add package skills for Drizzle database providers, Better Auth, React Hook Form, and React uploads.
|
|
14
|
+
|
|
3
15
|
## 0.0.25
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -72,6 +72,14 @@ bun add @beignet/provider-db-drizzle @beignet/core drizzle-orm mysql2
|
|
|
72
72
|
Doctor also checks standard Drizzle config, schema exports, database scripts,
|
|
73
73
|
seed/reset entrypoints, and provider registration drift in generated apps.
|
|
74
74
|
|
|
75
|
+
## Agent skills
|
|
76
|
+
|
|
77
|
+
This package ships a TanStack Intent skill for coding agents:
|
|
78
|
+
`@beignet/provider-db-drizzle#database-provider`. Load it when wiring Drizzle
|
|
79
|
+
providers, schema placement, Drizzle config, `DbPort` typing, repository
|
|
80
|
+
adapters, Unit of Work, audit, idempotency, outbox tables, migrations,
|
|
81
|
+
seed/reset helpers, devtools query instrumentation, or database doctor drift.
|
|
82
|
+
|
|
75
83
|
## Setup
|
|
76
84
|
|
|
77
85
|
### 1. Define your schema
|
|
@@ -166,16 +174,19 @@ export type AppPorts = {
|
|
|
166
174
|
|
|
167
175
|
```ts
|
|
168
176
|
// server/index.ts
|
|
169
|
-
import { createNextServer } from "@beignet/next";
|
|
177
|
+
import { createNextServer, createNextServerLoader } from "@beignet/next";
|
|
170
178
|
import { appPorts } from "@/infra/app-ports";
|
|
171
179
|
import { routes } from "@/server/routes";
|
|
172
|
-
import { providers } from "./providers";
|
|
173
180
|
|
|
174
|
-
export const
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
181
|
+
export const getServer = createNextServerLoader(async () => {
|
|
182
|
+
const { providers } = await import("./providers");
|
|
183
|
+
|
|
184
|
+
return createNextServer({
|
|
185
|
+
ports: appPorts,
|
|
186
|
+
providers,
|
|
187
|
+
context: ({ ports }) => ({ ports }),
|
|
188
|
+
routes,
|
|
189
|
+
});
|
|
179
190
|
});
|
|
180
191
|
```
|
|
181
192
|
|
|
@@ -954,15 +965,14 @@ These choices are deliberate and shared across the three backends:
|
|
|
954
965
|
idempotency timestamps, plus audit `occurred_at`, as ISO-8601 UTC strings in
|
|
955
966
|
text columns. This keeps retry timing, lease expiry, replay semantics, and
|
|
956
967
|
audit ordering identical across backends, and lexicographic ordering matches
|
|
957
|
-
chronological ordering.
|
|
958
|
-
`timestamptz`.
|
|
968
|
+
chronological ordering.
|
|
959
969
|
- **MySQL key length limits.** SQLite and Postgres use unbounded text columns
|
|
960
970
|
for idempotency keys; MySQL needs bounded `varchar` columns for its unique
|
|
961
971
|
index, so over-length keys fail loudly rather than truncating silently.
|
|
962
972
|
- **Shared conformance suite.** One conformance suite runs the same Unit of
|
|
963
973
|
Work, outbox, and idempotency behavior tests against SQLite (libSQL),
|
|
964
|
-
Postgres
|
|
965
|
-
|
|
974
|
+
Postgres, and MySQL, so durable workflow semantics do not drift between
|
|
975
|
+
backends.
|
|
966
976
|
|
|
967
977
|
## Advanced usage
|
|
968
978
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@beignet/provider-db-drizzle",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.27",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Drizzle ORM database providers for Beignet — typed DbPort, unit of work, audit logs, outbox, and idempotency for SQLite (libSQL/Turso), Postgres, and MySQL.",
|
|
6
6
|
"exports": {
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
"!src/**/*.test.ts",
|
|
36
36
|
"!src/**/*.test.tsx",
|
|
37
37
|
"!src/**/*.test-d.ts",
|
|
38
|
+
"skills",
|
|
38
39
|
"README.md",
|
|
39
40
|
"CHANGELOG.md"
|
|
40
41
|
],
|
|
@@ -60,7 +61,8 @@
|
|
|
60
61
|
"database",
|
|
61
62
|
"ports",
|
|
62
63
|
"hex",
|
|
63
|
-
"hexagonal"
|
|
64
|
+
"hexagonal",
|
|
65
|
+
"tanstack-intent"
|
|
64
66
|
],
|
|
65
67
|
"license": "MIT",
|
|
66
68
|
"repository": {
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: database-provider
|
|
3
|
+
description: "Wire Beignet Drizzle database providers with @beignet/provider-db-drizzle: SQLite/libSQL, Postgres, MySQL, schema placement, Drizzle config, provider registration, DbPort typing, repository adapters, Unit of Work, audit, idempotency, outbox tables, migrations, seed/reset helpers, devtools query instrumentation, and doctor drift. Use when adding, fixing, or reviewing Beignet database provider setup."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Beignet Drizzle Database Provider
|
|
7
|
+
|
|
8
|
+
Use this skill when working in an app that depends on
|
|
9
|
+
`@beignet/provider-db-drizzle`. Read local `AGENTS.md`, `beignet.config.*`,
|
|
10
|
+
`drizzle.config.*`, `infra/db/`, `server/providers.ts`, and the nearest
|
|
11
|
+
repository adapter before changing database code.
|
|
12
|
+
|
|
13
|
+
## Backend Choice
|
|
14
|
+
|
|
15
|
+
Use the provider subpath that matches the selected database:
|
|
16
|
+
|
|
17
|
+
- SQLite/libSQL/Turso: `@beignet/provider-db-drizzle/sqlite`
|
|
18
|
+
- Postgres/node-postgres: `@beignet/provider-db-drizzle/postgres`
|
|
19
|
+
- MySQL/mysql2: `@beignet/provider-db-drizzle/mysql`
|
|
20
|
+
|
|
21
|
+
Switching backends should change driver install, provider subpath, Drizzle
|
|
22
|
+
schema dialect, connection env var, and infra adapters. Contracts, use cases,
|
|
23
|
+
policies, routes, and UI should keep depending on app-owned ports.
|
|
24
|
+
|
|
25
|
+
## App-Owned Schema
|
|
26
|
+
|
|
27
|
+
Keep Drizzle schema and migrations in the app, normally:
|
|
28
|
+
|
|
29
|
+
```txt
|
|
30
|
+
infra/db/schema/
|
|
31
|
+
drizzle/
|
|
32
|
+
drizzle.config.ts
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Feature use cases must not import Drizzle schema or provider packages. Put SQL
|
|
36
|
+
mapping in `infra/<feature>/` or `infra/db/` adapters, then expose app-owned
|
|
37
|
+
repository interfaces from `features/<feature>/ports.ts`.
|
|
38
|
+
|
|
39
|
+
When audit, idempotency, or outbox need provider-owned SQL tables, generate the
|
|
40
|
+
Beignet table exports into the app schema, then run the normal migration flow:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
beignet db schema generate
|
|
44
|
+
beignet db generate
|
|
45
|
+
beignet db migrate
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Use `--tables audit,idempotency,outbox` when adopting a subset.
|
|
49
|
+
|
|
50
|
+
## Provider Registration
|
|
51
|
+
|
|
52
|
+
Create the backend provider in `server/providers.ts` or a nearby infra module
|
|
53
|
+
and register it before app-local providers that require `ctx.ports.db`.
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
import { createDrizzleSqliteProvider } from "@beignet/provider-db-drizzle/sqlite";
|
|
57
|
+
import * as schema from "@/infra/db/schema";
|
|
58
|
+
|
|
59
|
+
const drizzleSqliteProvider = createDrizzleSqliteProvider({ schema });
|
|
60
|
+
|
|
61
|
+
export const providers = [drizzleSqliteProvider] as const;
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Provider env vars:
|
|
65
|
+
|
|
66
|
+
- SQLite: `SQLITE_DB_URL`, optional `SQLITE_DB_AUTH_TOKEN`
|
|
67
|
+
- Postgres: `POSTGRES_DB_URL`
|
|
68
|
+
- MySQL: `MYSQL_DB_URL`
|
|
69
|
+
|
|
70
|
+
`beignet doctor --strict` checks required env, provider registration,
|
|
71
|
+
Drizzle config, schema exports, scripts, seed/reset entrypoints, and
|
|
72
|
+
registration drift in generated apps.
|
|
73
|
+
|
|
74
|
+
## Port Typing
|
|
75
|
+
|
|
76
|
+
Type the provider-contributed `db` port in app ports, then infer runtime ports
|
|
77
|
+
from the registered providers.
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
import type { DbPort } from "@beignet/provider-db-drizzle/sqlite";
|
|
81
|
+
import type { InferProviderPorts } from "@beignet/core/providers";
|
|
82
|
+
import type * as schema from "@/infra/db/schema";
|
|
83
|
+
import type { providers } from "@/server/providers";
|
|
84
|
+
|
|
85
|
+
export type AppPorts = {
|
|
86
|
+
db: DbPort<typeof schema>;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
export type AppRuntimePorts = AppPorts & InferProviderPorts<typeof providers>;
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Use `ctx.ports.db.drizzle` inside infra and provider wiring. Keep ordinary app
|
|
93
|
+
behavior behind repository, audit, outbox, idempotency, and Unit of Work ports.
|
|
94
|
+
|
|
95
|
+
## Repositories
|
|
96
|
+
|
|
97
|
+
Repository factories should accept a root database or transaction client from
|
|
98
|
+
the matching provider subpath:
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
import { eq } from "drizzle-orm";
|
|
102
|
+
import type { DrizzleSqliteDatabase } from "@beignet/provider-db-drizzle/sqlite";
|
|
103
|
+
import type { TodoRepository } from "@/features/todos/ports";
|
|
104
|
+
import * as schema from "@/infra/db/schema";
|
|
105
|
+
|
|
106
|
+
export function createTodoRepository(
|
|
107
|
+
db: DrizzleSqliteDatabase<typeof schema>,
|
|
108
|
+
): TodoRepository {
|
|
109
|
+
return {
|
|
110
|
+
async findById(id) {
|
|
111
|
+
const [row] = await db
|
|
112
|
+
.select()
|
|
113
|
+
.from(schema.todos)
|
|
114
|
+
.where(eq(schema.todos.id, id))
|
|
115
|
+
.limit(1);
|
|
116
|
+
|
|
117
|
+
return row ?? null;
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Collect repositories in an infra factory and contribute them through an
|
|
124
|
+
app-local provider when they depend on `ctx.ports.db`.
|
|
125
|
+
|
|
126
|
+
## Unit Of Work
|
|
127
|
+
|
|
128
|
+
Use the backend `createDrizzleXUnitOfWork(...)` helper when a use case needs
|
|
129
|
+
business writes, audit rows, idempotency records, outbox records, or domain
|
|
130
|
+
events to commit together.
|
|
131
|
+
|
|
132
|
+
Build every transaction-scoped port from the transaction client:
|
|
133
|
+
|
|
134
|
+
```ts
|
|
135
|
+
uow: createDrizzleSqliteUnitOfWork({
|
|
136
|
+
db: ports.db.drizzle,
|
|
137
|
+
eventBus: ports.eventBus,
|
|
138
|
+
createTransactionPorts: (tx, events) => ({
|
|
139
|
+
...createRepositories(tx),
|
|
140
|
+
events,
|
|
141
|
+
audit: createDrizzleSqliteAuditLogPort(tx),
|
|
142
|
+
idempotency: createDrizzleSqliteIdempotencyPort(tx),
|
|
143
|
+
outbox: createDrizzleSqliteOutboxPort(tx),
|
|
144
|
+
}),
|
|
145
|
+
});
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Root ports are useful for reads and background work. They do not participate in
|
|
149
|
+
the current transaction unless rebuilt from `tx`.
|
|
150
|
+
|
|
151
|
+
## Gotchas
|
|
152
|
+
|
|
153
|
+
- The provider installs `ctx.ports.db`; the app still owns schema,
|
|
154
|
+
migrations, repository interfaces, seed/reset scripts, and Drizzle config.
|
|
155
|
+
- `ctx.ports.db.drizzle` is an infra escape hatch. Do not make it the normal
|
|
156
|
+
dependency for contracts, use cases, policies, routes, or UI.
|
|
157
|
+
- Register Drizzle before app-local providers that call `ports.db.drizzle` in
|
|
158
|
+
`setup(...)`.
|
|
159
|
+
- The legacy `ctx.ports.db.db` alias may exist, but new app code should use
|
|
160
|
+
`drizzle`.
|
|
161
|
+
- Operational ports must be rebuilt from `tx` when they need to commit with
|
|
162
|
+
the business write.
|
|
163
|
+
|
|
164
|
+
## Operational Ports
|
|
165
|
+
|
|
166
|
+
- Audit: use `createDrizzleXAuditLogPort(...)`; wrap with
|
|
167
|
+
`createInstrumentedAuditLog(...)` or ambient audit helpers when the app needs
|
|
168
|
+
actor/request defaults and devtools events.
|
|
169
|
+
- Idempotency: use `createDrizzleXIdempotencyPort(...)` with
|
|
170
|
+
`runIdempotently(...)` when replay safety must survive process restarts.
|
|
171
|
+
- Outbox: use `createDrizzleXOutboxPort(...)`, record inside the transaction,
|
|
172
|
+
and drain from a worker, cron route, or explicit outbox drain route.
|
|
173
|
+
|
|
174
|
+
Generate and migrate the backing tables before booting code that depends on
|
|
175
|
+
these ports.
|
|
176
|
+
|
|
177
|
+
## Testing
|
|
178
|
+
|
|
179
|
+
Use repository fakes for pure use-case tests. Use isolated Drizzle databases
|
|
180
|
+
for SQL mapping, transactions, migration assumptions, durable idempotency,
|
|
181
|
+
audit rows, and outbox rows.
|
|
182
|
+
|
|
183
|
+
After database provider changes, run from the app root:
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
beignet lint
|
|
187
|
+
beignet doctor --strict
|
|
188
|
+
bun run test
|
|
189
|
+
bun run typecheck
|
|
190
|
+
```
|