@cfast/db 0.5.0 → 0.7.0
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/dist/index.d.ts +5 -305
- package/dist/index.js +79 -33
- package/dist/seed.d.ts +328 -0
- package/dist/seed.js +399 -0
- package/dist/types-FUFR36h1.d.ts +221 -0
- package/llms.txt +163 -16
- package/package.json +15 -5
package/dist/seed.d.ts
ADDED
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
import { DrizzleTable } from '@cfast/permissions';
|
|
2
|
+
import { a as Db, i as InferRow } from './types-FUFR36h1.js';
|
|
3
|
+
import { Column } from 'drizzle-orm';
|
|
4
|
+
import { SQLiteColumnBuilderBase, SQLiteTableExtraConfigValue } from 'drizzle-orm/sqlite-core';
|
|
5
|
+
import { BuildColumns } from 'drizzle-orm/column-builder';
|
|
6
|
+
import { SQLiteTableWithColumns } from 'drizzle-orm/sqlite-core/table';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Schema-driven seed generator for `@cfast/db`.
|
|
10
|
+
*
|
|
11
|
+
* Introspects Drizzle schema metadata (column types, foreign keys, primary keys)
|
|
12
|
+
* to auto-generate realistic test data using the bundled `@faker-js/faker`.
|
|
13
|
+
* Supports:
|
|
14
|
+
*
|
|
15
|
+
* - Column-level `.seed()` overrides via `seedConfig()` wrapper
|
|
16
|
+
* - Table-level `.seed()` overrides via `tableSeed()` wrapper (count, per)
|
|
17
|
+
* - Automatic FK resolution from generated parent rows
|
|
18
|
+
* - `per` relational generation (N children per parent row)
|
|
19
|
+
* - `ctx` API for parent access, ref, index, and all
|
|
20
|
+
* - Topological sort for correct insert order
|
|
21
|
+
* - Many-to-many deduplication
|
|
22
|
+
* - Auth table detection for realistic emails
|
|
23
|
+
* - SQL transcript generation
|
|
24
|
+
*
|
|
25
|
+
* @module seed-generator
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
type AnyColumn = Column<any, any, any>;
|
|
29
|
+
/** Faker instance type -- matches `@faker-js/faker`'s default export. */
|
|
30
|
+
type Faker = any;
|
|
31
|
+
/**
|
|
32
|
+
* Context passed to column-level seed functions as the second argument.
|
|
33
|
+
*/
|
|
34
|
+
type SeedContext = {
|
|
35
|
+
/** The parent row when `per` is used on the table. `undefined` for root tables. */
|
|
36
|
+
parent: Record<string, unknown> | undefined;
|
|
37
|
+
/** Pick a random existing row from any table already seeded. */
|
|
38
|
+
ref: (table: DrizzleTable) => Record<string, unknown>;
|
|
39
|
+
/** Zero-based position within the current batch (per-parent or global). */
|
|
40
|
+
index: number;
|
|
41
|
+
/** All generated rows for the given table (available after that table is seeded). */
|
|
42
|
+
all: (table: DrizzleTable) => Record<string, unknown>[];
|
|
43
|
+
};
|
|
44
|
+
/** Column-level seed function. Receives faker instance and optional context. */
|
|
45
|
+
type ColumnSeedFn = (faker: Faker, ctx: SeedContext) => unknown;
|
|
46
|
+
/** Table-level seed config attached via `tableSeed()`. */
|
|
47
|
+
type TableSeedConfig = {
|
|
48
|
+
/** Total count (or per-parent count when `per` is set). */
|
|
49
|
+
count: number;
|
|
50
|
+
/** Generate `count` rows per row in this parent table. */
|
|
51
|
+
per?: DrizzleTable;
|
|
52
|
+
};
|
|
53
|
+
/** Options for `db.seed().run()`. */
|
|
54
|
+
type SeedRunOptions = {
|
|
55
|
+
/** If provided, write the equivalent SQL INSERT statements to this path. */
|
|
56
|
+
transcript?: string;
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Stores column-level seed functions keyed by the column builder's `config`
|
|
60
|
+
* object. Drizzle shares the same `config` reference between the builder
|
|
61
|
+
* (what the user passes to `sqliteTable(...)`) and the built column (what
|
|
62
|
+
* `getTableColumns()` returns), so we can look up the seed fn from either
|
|
63
|
+
* side. This avoids the builder-vs-column identity mismatch.
|
|
64
|
+
*
|
|
65
|
+
* Exported so the `.seed()` prototype patches in `seed.ts` can write to
|
|
66
|
+
* the same registries. Application code should use `.seed()` or
|
|
67
|
+
* `seedConfig()`/`tableSeed()` rather than accessing these directly.
|
|
68
|
+
*/
|
|
69
|
+
declare const columnSeedMap: WeakMap<object, ColumnSeedFn>;
|
|
70
|
+
declare const tableSeedMap: WeakMap<object, TableSeedConfig>;
|
|
71
|
+
/**
|
|
72
|
+
* Attaches a seed generator function to a Drizzle column.
|
|
73
|
+
*
|
|
74
|
+
* The column object is returned unmodified so this can be used inline in
|
|
75
|
+
* schema definitions without breaking Drizzle types.
|
|
76
|
+
*
|
|
77
|
+
* @deprecated Use the `.seed()` method on column builders instead:
|
|
78
|
+
* ```ts
|
|
79
|
+
* text("title").seed(f => f.lorem.sentence())
|
|
80
|
+
* ```
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* ```ts
|
|
84
|
+
* const posts = sqliteTable("posts", {
|
|
85
|
+
* title: seedConfig(text("title"), f => f.lorem.sentence()),
|
|
86
|
+
* });
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
89
|
+
declare function seedConfig<T>(column: T, fn: ColumnSeedFn): T;
|
|
90
|
+
/**
|
|
91
|
+
* Attaches table-level seed config (count, per) to a Drizzle table.
|
|
92
|
+
*
|
|
93
|
+
* @deprecated Use `table()` from `@cfast/db/seed` with `.seed()` instead:
|
|
94
|
+
* ```ts
|
|
95
|
+
* import { table } from "@cfast/db/seed";
|
|
96
|
+
* const posts = table("posts", { ... }).seed({ count: 5, per: users });
|
|
97
|
+
* ```
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* ```ts
|
|
101
|
+
* const posts = tableSeed(
|
|
102
|
+
* sqliteTable("posts", { ... }),
|
|
103
|
+
* { count: 5, per: users },
|
|
104
|
+
* );
|
|
105
|
+
* ```
|
|
106
|
+
*/
|
|
107
|
+
declare function tableSeed<T extends DrizzleTable>(table: T, config: TableSeedConfig): T;
|
|
108
|
+
/** FK info extracted from Drizzle's internal symbol. */
|
|
109
|
+
type FkInfo = {
|
|
110
|
+
/** Column name in the current table (SQL name). */
|
|
111
|
+
columnName: string;
|
|
112
|
+
/** JS key of the column in the current table. */
|
|
113
|
+
columnKey: string;
|
|
114
|
+
/** Referenced table (Drizzle object). */
|
|
115
|
+
foreignTable: DrizzleTable;
|
|
116
|
+
/** Referenced column name (SQL name). */
|
|
117
|
+
foreignColumnName: string;
|
|
118
|
+
};
|
|
119
|
+
/** Extracts all inline foreign key definitions from a Drizzle table. */
|
|
120
|
+
declare function extractForeignKeys(table: DrizzleTable): FkInfo[];
|
|
121
|
+
/** Returns the JS key for the primary key column(s). Only supports single PK. */
|
|
122
|
+
declare function findPrimaryKeyColumn(table: DrizzleTable): {
|
|
123
|
+
key: string;
|
|
124
|
+
column: AnyColumn;
|
|
125
|
+
} | undefined;
|
|
126
|
+
/**
|
|
127
|
+
* Topologically sorts tables so parents are seeded before children.
|
|
128
|
+
* Uses Kahn's algorithm. Respects both FK and `per` dependencies.
|
|
129
|
+
*/
|
|
130
|
+
declare function topologicalSort(tables: DrizzleTable[], fkMap: Map<DrizzleTable, FkInfo[]>): DrizzleTable[];
|
|
131
|
+
/**
|
|
132
|
+
* Generates seed data for all tables in a schema using Drizzle column metadata
|
|
133
|
+
* and the bundled `@faker-js/faker` instance.
|
|
134
|
+
*
|
|
135
|
+
* @param schema - The full Drizzle schema (`import * as schema from "./schema"`).
|
|
136
|
+
* @returns An engine with `generate()` and `run(db)` methods.
|
|
137
|
+
*/
|
|
138
|
+
declare function createSeedEngine(schema: Record<string, unknown>): {
|
|
139
|
+
tables: object[];
|
|
140
|
+
fkMap: Map<object, FkInfo[]>;
|
|
141
|
+
/**
|
|
142
|
+
* Generate rows for all tables (or a subset).
|
|
143
|
+
* Returns a map of table -> rows[].
|
|
144
|
+
*/
|
|
145
|
+
generate(tableOverrides?: Map<DrizzleTable, {
|
|
146
|
+
count: number;
|
|
147
|
+
}>): Map<DrizzleTable, Record<string, unknown>[]>;
|
|
148
|
+
/**
|
|
149
|
+
* Generate and insert seed data into the database.
|
|
150
|
+
*/
|
|
151
|
+
run(db: Db, options?: SeedRunOptions & {
|
|
152
|
+
tableOverrides?: Map<DrizzleTable, {
|
|
153
|
+
count: number;
|
|
154
|
+
}>;
|
|
155
|
+
}): Promise<Map<DrizzleTable, Record<string, unknown>[]>>;
|
|
156
|
+
};
|
|
157
|
+
/**
|
|
158
|
+
* Creates a single-table seed generator for use with `db.query(table).seed(n)`.
|
|
159
|
+
*/
|
|
160
|
+
declare function createSingleTableSeed(schema: Record<string, unknown>, table: DrizzleTable, count: number): {
|
|
161
|
+
generate: () => Map<object, Record<string, unknown>[]>;
|
|
162
|
+
run: (db: Db, options?: SeedRunOptions) => Promise<Map<object, Record<string, unknown>[]>>;
|
|
163
|
+
};
|
|
164
|
+
/**
|
|
165
|
+
* Checks if a value is a Drizzle table by looking for the IsDrizzleTable symbol.
|
|
166
|
+
*/
|
|
167
|
+
declare function isTable(value: unknown): boolean;
|
|
168
|
+
|
|
169
|
+
declare module "drizzle-orm/sqlite-core" {
|
|
170
|
+
interface SQLiteColumnBuilder<T, TRuntimeConfig, TTypeConfig, TExtraConfig> {
|
|
171
|
+
/**
|
|
172
|
+
* Attaches a seed generator function to this column.
|
|
173
|
+
*
|
|
174
|
+
* @example
|
|
175
|
+
* ```ts
|
|
176
|
+
* title: text("title").seed(f => f.lorem.sentence()),
|
|
177
|
+
* ```
|
|
178
|
+
*/
|
|
179
|
+
seed(fn: ColumnSeedFn): this;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Type representing a Drizzle table with an added `.seed()` method.
|
|
185
|
+
* The `.seed()` method returns the same table reference for chaining.
|
|
186
|
+
*/
|
|
187
|
+
type SeedableTable<T> = T & {
|
|
188
|
+
seed(config: TableSeedConfig): SeedableTable<T>;
|
|
189
|
+
};
|
|
190
|
+
/**
|
|
191
|
+
* Creates a SQLite table with a `.seed()` method for configuring
|
|
192
|
+
* seed generation. Drop-in replacement for `sqliteTable()` from
|
|
193
|
+
* `drizzle-orm/sqlite-core`.
|
|
194
|
+
*
|
|
195
|
+
* @example
|
|
196
|
+
* ```ts
|
|
197
|
+
* import { table } from "@cfast/db/seed";
|
|
198
|
+
*
|
|
199
|
+
* const posts = table("posts", {
|
|
200
|
+
* title: text("title").seed(f => f.lorem.sentence()),
|
|
201
|
+
* authorId: text("author_id").references(() => users.id),
|
|
202
|
+
* }).seed({ count: 5, per: users });
|
|
203
|
+
* ```
|
|
204
|
+
*/
|
|
205
|
+
declare function table<TTableName extends string, TColumnsMap extends Record<string, SQLiteColumnBuilderBase>>(name: TTableName, columns: TColumnsMap, extraConfig?: (self: BuildColumns<TTableName, TColumnsMap, "sqlite">) => SQLiteTableExtraConfigValue[]): SeedableTable<SQLiteTableWithColumns<{
|
|
206
|
+
name: TTableName;
|
|
207
|
+
schema: undefined;
|
|
208
|
+
columns: BuildColumns<TTableName, TColumnsMap, "sqlite">;
|
|
209
|
+
dialect: "sqlite";
|
|
210
|
+
}>>;
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* One-liner seed: introspects the schema from the `db` instance, generates
|
|
214
|
+
* realistic data via the bundled `@faker-js/faker`, and inserts it.
|
|
215
|
+
*
|
|
216
|
+
* @example
|
|
217
|
+
* ```ts
|
|
218
|
+
* import { seed } from "@cfast/db/seed";
|
|
219
|
+
* await seed(db);
|
|
220
|
+
* ```
|
|
221
|
+
*
|
|
222
|
+
* @param db - A {@link Db} instance created via `createDb()`.
|
|
223
|
+
* @param options - Optional {@link SeedRunOptions} (e.g. `{ transcript: "./seed.sql" }`).
|
|
224
|
+
*/
|
|
225
|
+
declare function seed(db: Db, options?: SeedRunOptions): Promise<void>;
|
|
226
|
+
/**
|
|
227
|
+
* A single seed entry -- every row in `rows` is inserted into `table` at seed
|
|
228
|
+
* time. Row shape is inferred from the Drizzle table so typos in column names
|
|
229
|
+
* are caught by `tsc` instead of failing at runtime when `INSERT` rejects
|
|
230
|
+
* the statement.
|
|
231
|
+
*
|
|
232
|
+
* @typeParam TTable - The Drizzle table reference (e.g. `typeof usersTable`).
|
|
233
|
+
*/
|
|
234
|
+
type SeedEntry<TTable extends DrizzleTable = DrizzleTable> = {
|
|
235
|
+
/**
|
|
236
|
+
* The Drizzle table to insert into. Must be imported from your schema
|
|
237
|
+
* (`import { users } from "~/db/schema"`) rather than passed as a string
|
|
238
|
+
* so the helper can infer row types and forward the reference to
|
|
239
|
+
* `db.insert()`.
|
|
240
|
+
*/
|
|
241
|
+
table: TTable;
|
|
242
|
+
/**
|
|
243
|
+
* Rows to insert. The row shape is inferred from the table's
|
|
244
|
+
* `$inferSelect` -- making a typo in a column name is a compile-time error.
|
|
245
|
+
*
|
|
246
|
+
* Entries are inserted in the order they appear, which lets you control
|
|
247
|
+
* foreign-key ordering just by ordering your `entries` array
|
|
248
|
+
* (`{ users }` before `{ posts }`, etc.).
|
|
249
|
+
*/
|
|
250
|
+
rows: readonly InferRow<TTable>[];
|
|
251
|
+
};
|
|
252
|
+
/**
|
|
253
|
+
* Configuration passed to {@link defineSeed}.
|
|
254
|
+
*/
|
|
255
|
+
type SeedConfig = {
|
|
256
|
+
/**
|
|
257
|
+
* Ordered list of seed entries. Each entry is flushed as a batched insert
|
|
258
|
+
* in list order, so place parent tables (users, orgs) before child tables
|
|
259
|
+
* (posts, memberships) that reference them via foreign keys.
|
|
260
|
+
*/
|
|
261
|
+
entries: readonly SeedEntry[];
|
|
262
|
+
};
|
|
263
|
+
/**
|
|
264
|
+
* The compiled seed returned by {@link defineSeed}.
|
|
265
|
+
*
|
|
266
|
+
* Holds a frozen copy of the entry list so runner callers can introspect
|
|
267
|
+
* what would be seeded, plus a `run(db)` method that actually executes the
|
|
268
|
+
* inserts against a real {@link Db} instance.
|
|
269
|
+
*/
|
|
270
|
+
type Seed = {
|
|
271
|
+
/** The frozen list of entries this seed will insert, in order. */
|
|
272
|
+
readonly entries: readonly SeedEntry[];
|
|
273
|
+
/**
|
|
274
|
+
* Executes every entry against the given {@link Db} in the order they were
|
|
275
|
+
* declared. Uses `db.unsafe()` internally so seed scripts don't need
|
|
276
|
+
* their own grants plumbing -- seeding is a system task by definition.
|
|
277
|
+
*
|
|
278
|
+
* Entries with an empty `rows` array are skipped so callers can leave
|
|
279
|
+
* placeholder entries in the config without crashing the seed.
|
|
280
|
+
*
|
|
281
|
+
* @param db - A {@link Db} instance, typically created once at the top
|
|
282
|
+
* of a `scripts/seed.ts` file via `createDb({...})`.
|
|
283
|
+
*/
|
|
284
|
+
run: (db: Db) => Promise<void>;
|
|
285
|
+
};
|
|
286
|
+
/**
|
|
287
|
+
* Declares a database seed in a portable, type-safe way.
|
|
288
|
+
*
|
|
289
|
+
* The canonical replacement for hand-rolled `scripts/seed.ts` files that
|
|
290
|
+
* called `db.mutate("tablename")` (a method that never existed) or reached
|
|
291
|
+
* straight into raw Drizzle. Use the scaffolded `scripts/seed.ts` in a
|
|
292
|
+
* `create-cfast` project for a ready-made example.
|
|
293
|
+
*
|
|
294
|
+
* @example
|
|
295
|
+
* ```ts
|
|
296
|
+
* // scripts/seed.ts
|
|
297
|
+
* import { defineSeed, createDb } from "@cfast/db";
|
|
298
|
+
* import * as schema from "~/db/schema";
|
|
299
|
+
*
|
|
300
|
+
* const seed = defineSeed({
|
|
301
|
+
* entries: [
|
|
302
|
+
* {
|
|
303
|
+
* table: schema.users,
|
|
304
|
+
* rows: [
|
|
305
|
+
* { id: "u-1", email: "ada@example.com", name: "Ada" },
|
|
306
|
+
* { id: "u-2", email: "grace@example.com", name: "Grace" },
|
|
307
|
+
* ],
|
|
308
|
+
* },
|
|
309
|
+
* {
|
|
310
|
+
* table: schema.posts,
|
|
311
|
+
* rows: [
|
|
312
|
+
* { id: "p-1", authorId: "u-1", title: "Hello" },
|
|
313
|
+
* ],
|
|
314
|
+
* },
|
|
315
|
+
* ],
|
|
316
|
+
* });
|
|
317
|
+
*
|
|
318
|
+
* // In a worker/runner that already has a real D1 binding:
|
|
319
|
+
* const db = createDb({ d1, schema, grants: [], user: null });
|
|
320
|
+
* await seed.run(db);
|
|
321
|
+
* ```
|
|
322
|
+
*
|
|
323
|
+
* @param config - The {@link SeedConfig} with the ordered list of entries.
|
|
324
|
+
* @returns A {@link Seed} with a `.run(db)` executor.
|
|
325
|
+
*/
|
|
326
|
+
declare function defineSeed(config: SeedConfig): Seed;
|
|
327
|
+
|
|
328
|
+
export { type ColumnSeedFn, type Faker, type Seed, type SeedConfig, type SeedContext, type SeedEntry, type SeedRunOptions, type SeedableTable, type TableSeedConfig, columnSeedMap, createSeedEngine, createSingleTableSeed, defineSeed, extractForeignKeys, findPrimaryKeyColumn, isTable, seed, seedConfig, table, tableSeed, tableSeedMap, topologicalSort };
|