first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,98 @@
# adapter
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
The package's flagship export. `createBetterAuthSurrealAdapter(options)`
returns a Better-Auth `Adapter` whose 8 contract methods plus
`consumeOne` and `id` are wired against a SurrealDB v3 connection. The
adapter validates options through Zod at the boundary, dispatches every
CRUD method through OTel-instrumented spans, applies link-field
prefix/strip on writes/reads, and compiles `Where[]` filters into
parameterised SurrealQL — no string interpolation, no mocks, no
`vi.mock`.
## Features
- Stable `id` constant `"@suaveplan/better-auth-surrealdb"`.
- All 8 Better-Auth methods: `create`, `findOne`, `findMany`, `count`,
`update`, `updateMany`, `delete`, `deleteMany`, plus the `consumeOne`
primitive Better-Auth v1.6+ adds atop the older 8 methods.
- Link-field-aware writes and reads — `userId: "u1"` becomes
`userId: "user:u1"` at rest for `record<user>` columns.
- Three id-strategies (`record-id` default, `plain`, `ulid`).
- OTel counter + histogram via `instrument()`.
- Zod-validated options at the boundary; failures raise
`BetterAuthSurrealError(INVALID_INPUT)` with the `ZodError` as `cause`.
- Connection-readiness check: every method validates `client.isConnected`
and raises `CONNECTION_NOT_READY` when the underlying SurrealAdapter
has not connected.
- Underlying `SurrealError` failures wrap in `QUERY_FAILED` preserving the
`cause` chain.
## Usage: Basic
```ts
import { SurrealAdapter } from "@suaveplan/surrealdb";
import { applyBetterAuthSchema } from "@suaveplan/better-auth-surrealdb/schema";
import { createBetterAuthSurrealAdapter } from "@suaveplan/better-auth-surrealdb/adapter";
const client = new SurrealAdapter({ url: "ws://localhost:8000" });
await client.connect();
await applyBetterAuthSchema(client);
const adapter = createBetterAuthSurrealAdapter({ client });
await adapter.create({
model: "session",
data: { id: "s1", userId: "u1", token: "t" },
});
```
## Usage: Advanced
```ts
import { createBetterAuthSurrealAdapter } from "@suaveplan/better-auth-surrealdb/adapter";
const adapter = createBetterAuthSurrealAdapter({
client: surrealAdapter,
idStrategy: "ulid",
modelToTable: (m) => `bauth_${m}`,
linkFields: {
// override default — match a flat-string schema:
session: {},
member: { organizationId: "organization" },
},
});
const user = await adapter.findOne({
model: "user",
where: [{ field: "email", value: "x@y" }],
select: ["id", "email"],
});
```
## API Reference
- `createBetterAuthSurrealAdapter(options): BetterAuthSurrealAdapter`
- `BETTER_AUTH_SURREAL_ADAPTER_ID = "@suaveplan/better-auth-surrealdb"`
- `BetterAuthSurrealAdapter` — structural type satisfying Better-Auth's
`DBAdapter` minus `transaction` / `createSchema` (the host's own
transaction primitive remains reachable via `SurrealAdapter.transaction`).
## Implementation Notes
The adapter's `client.query()` calls pass the binding record as
`[vars]` so both `SurrealAdapter` (which flattens a trailing record) and
hand-rolled `SurrealClientView` implementations receive the same shape.
`update` is implemented atop `updateMany`'s `RETURN AFTER` row stream and
returns the first row — `count() FROM <table> WHERE … GROUP ALL` returns
a `{ c: <num> }` row by aliasing; bare `count()` returns the count
unnamed which trips SurrealDB v3's stricter row shape. `consumeOne`
fetches the row first, then deletes by the same predicate; this is the
fallback path Better-Auth's factory installs when an adapter does not
expose a native consume primitive, and gives the same race semantics
under SurrealDB's read-then-delete pipeline.