Skip to content

Commit 087b9ff

Browse files
committed
feat(experimental): database schema var
1 parent 6c3f3d6 commit 087b9ff

2 files changed

Lines changed: 59 additions & 3 deletions

File tree

packages/experimental/src/plugins/db.test.ts

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import { describe, expect, it } from "vitest";
2-
import { v } from "../index";
1+
import { describe, expect, expectTypeOf, it } from "vitest";
2+
import { memoryAdapter, v } from "../index";
33
import { attrsOf, validate } from "../schema";
4-
import { db, generateId, id } from "./db";
4+
import type { ValueOfVar } from "../var";
5+
import { db, generateId, id, schema } from "./db";
56

67
const ALPHANUMERIC = /^[a-zA-Z0-9]+$/;
78

@@ -64,3 +65,51 @@ describe("db.id", () => {
6465
});
6566
});
6667
});
68+
69+
describe("schema", () => {
70+
it("is a named export and the same helper as db.schema", () => {
71+
expect(schema).toBe(db.schema);
72+
});
73+
74+
it("wraps v.var with default null and an object schema", () => {
75+
const user = schema("dbt_user", {
76+
id: db.id(v.string({})),
77+
email: db.unique(v.string()),
78+
});
79+
expect(user.$var).toBe(true);
80+
expect(user.name).toBe("dbt_user");
81+
expect(user.default).toBeNull();
82+
expect(user.schema?.name).toBe("object");
83+
expect(attrsOf(user.schema?.shape?.email, "db")).toEqual({ unique: true });
84+
});
85+
86+
it("types the var as the row or null, and works as a storage model", async () => {
87+
const item = schema("dbt_item", {
88+
id: db.id(v.string({})),
89+
tag: v.string(),
90+
});
91+
expectTypeOf<ValueOfVar<typeof item>>().toEqualTypeOf<{
92+
id: string;
93+
tag: string;
94+
} | null>();
95+
96+
const store = v.storage(memoryAdapter(), { item });
97+
type CreateArg = Parameters<typeof store.item.create>[0];
98+
expectTypeOf<CreateArg>().toEqualTypeOf<{ tag: string; id?: string }>();
99+
100+
const created = await store.item.create({ tag: "a" });
101+
expect(created.tag).toBe("a");
102+
expect(created.id).toHaveLength(32);
103+
expect(await store.item.findOne({ id: created.id })).toEqual(created);
104+
105+
const read = v.fn({ use: [{ item }] }, (c) => {
106+
expectTypeOf(c.dbt_item).toEqualTypeOf<{
107+
id: string;
108+
tag: string;
109+
} | null>();
110+
return c.dbt_item;
111+
});
112+
expect(read()).toBeNull();
113+
expect(read.with({ dbt_item: created })()).toEqual(created);
114+
});
115+
});

packages/experimental/src/plugins/db.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { v } from "..";
22
import { createRandomStringGenerator } from "../helpers/random";
33
import { type TypeDefination, withAttrs } from "../schema";
4+
import type { LiteralString } from "../types";
45

56
export const generateId = v.fn(
67
"db.generate_id",
@@ -46,9 +47,15 @@ export const id = <T, O>(
4647
{ id: true },
4748
) as TypeDefination<T, O, string>;
4849

50+
/** A model var: `v.var(name, { default: null, schema: v.object(shape) })`.
51+
* Import it from the db plugin: `import { schema } from "better-call/plugins/db"`. */
52+
export const schema = <N extends LiteralString, S>(name: N, shape: S) =>
53+
v.var(name, { default: null, schema: v.object(shape) });
54+
4955
export const db = {
5056
unique,
5157
indexed,
5258
references,
5359
id,
60+
schema,
5461
};

0 commit comments

Comments
 (0)