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,77 @@
# id-strategy
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
Encapsulates the three Better-Auth record-id strategies the adapter
supports. SurrealDB's native record-id format is `<table>:<id>`, but
Better-Auth callers may pass either a bare `id` (record-id mode), no
`id` at all (ulid mode auto-generates), or a row that stores `id` as a
regular field (plain mode). This module resolves the strategy choice into
concrete CREATE-target-and-data tuples and normalises the returned id
on read.
## Features
- `prepareCreate(strategy, table, data) → PreparedCreate` — dispatches on
the strategy and returns `{ target, data, assignedId }`.
- `record-id` strategy: requires non-empty `data.id`, rejects colons, emits
`<table>:\`<id>\`` to keep arbitrary identifier characters safe.
- `ulid` strategy: when `data.id` is absent, generates a 26-char Crockford
base32 ULID (10 chars time + 16 chars random) via `crypto.getRandomValues`.
- `plain` strategy: keeps `id` as a row field; SurrealDB assigns the
storage id; row's `assignedId` echoes `data.id` when present.
- `normaliseReturnedId` — strips the `<table>:` prefix from SurrealDB's
return shape (`RecordId` object, `<table>:<id>` string, or bare value).
- Throws `INVALID_MODEL` when the table name fails the identifier rule.
- Throws `INVALID_ID_FORMAT` when an id-strategy contract is violated.
## Usage: Basic
```ts
import { prepareCreate } from "@suaveplan/better-auth-surrealdb/id-strategy";
const p = prepareCreate("record-id", "user", { id: "u1", email: "x@y" });
// p.target === "user:`u1`"
// p.data === { email: "x@y" }
// p.assignedId === "u1"
```
## Usage: Advanced
```ts
import { prepareCreate, generateUlid } from "@suaveplan/better-auth-surrealdb/id-strategy";
// Inject deterministic id for tests by composing your own table+id outside
// of prepareCreate; or use the ulid generator directly:
const id = generateUlid();
const p = prepareCreate("record-id", "session", { id, userId: "user:u1" });
// Plain mode for schemas that store id as a regular column:
const flat = prepareCreate("plain", "session", { id: "s1", token: "t" });
// flat.target === "session"
// flat.data === { id: "s1", token: "t" }
// flat.assignedId === "s1"
```
## API Reference
- `IdStrategy = "record-id" | "plain" | "ulid"`
- `PreparedCreate = { target: string; data: Row; assignedId: string | null }`
- `prepareCreate(strategy, table, data): PreparedCreate`
- `generateUlid(now?): string` — 26-char Crockford base32 ULID.
- `normaliseReturnedId(value, table): string` — strips `<table>:` from SurrealDB
return shapes.
## Implementation Notes
The ULID alphabet matches the published spec (Crockford base32 minus I, L,
O, U). Random bytes come from `crypto.getRandomValues` so the generator is
universal (works in Bun, Node ≥18, and browsers). The record-id
target is wrapped in backticks so SurrealDB accepts ULIDs containing digits
at position 1 (otherwise the parser may treat the value as a number). The
`prepareCreate` switch is exhaustive; TypeScript narrows `IdStrategy` to
the three known cases at compile time.