bigal 16.0.0-beta.2 → 16.0.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.
@@ -0,0 +1,3 @@
1
+ # Marks docs/ as a standalone pnpm project, separate from the library at the repo root.
2
+ allowBuilds:
3
+ esbuild: true
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: Complete API reference for BigAl - initialize(), table(), column builders, relationships, repository methods, query builder, and types.
2
+ description: Complete API reference for BigAl initialize(), Repository, ReadonlyRepository, query builder methods, subquery(), decorators, and types.
3
3
  ---
4
4
 
5
5
  # API Reference
@@ -8,279 +8,35 @@ All public exports from `bigal`.
8
8
 
9
9
  ## initialize()
10
10
 
11
- Creates a BigAl instance with typed repositories for all provided models.
12
-
13
- ### Object-style models (recommended)
14
-
15
- Returns typed repositories directly via destructuring:
11
+ Creates repositories for all provided models.
16
12
 
17
13
  ```ts
18
14
  import { initialize } from 'bigal';
19
15
 
20
- const { Product, Store } = initialize({
16
+ const repos = initialize({
17
+ models: [Product, Store],
21
18
  pool,
22
19
  readonlyPool,
23
- models: { Product, Store, Category, ProductCategory },
24
- connections: {
25
- audit: { pool: auditPool },
26
- },
27
- onQuery({ sql, params, duration, error, model, operation }) {
28
- logger.debug({ sql, params, duration, model, operation });
29
- },
30
- });
31
- ```
32
-
33
- ### Array-style models
34
-
35
- Returns an object with `getRepository()` and `getReadonlyRepository()` methods:
36
-
37
- ```ts
38
- const bigal = initialize({
39
- pool,
40
- models: [Product, Store, Category, ProductCategory],
20
+ connections,
21
+ expose,
41
22
  });
42
-
43
- const Product = bigal.getRepository(Product);
44
- ```
45
-
46
- **Parameters:**
47
-
48
- | Option | Type | Required | Description |
49
- | -------------- | ------------------------------------------ | -------- | --------------------------------------------- |
50
- | `pool` | `PoolLike` | Yes | Primary connection pool |
51
- | `readonlyPool` | `PoolLike` | No | Pool for read operations (defaults to `pool`) |
52
- | `models` | `Record<string, TableDefinition>` or array | Yes | All model definitions |
53
- | `connections` | `Record<string, IConnection>` | No | Named connections for multi-database setups |
54
- | `onQuery` | `OnQueryCallback` | No | Query observability callback |
55
-
56
- All relationships are validated at startup. If a `belongsTo` or `hasMany` references a model not
57
- included in `models`, `initialize()` throws immediately.
58
-
59
- ## getRepository()
60
-
61
- Returns a typed read-write repository for a model.
62
-
63
- ```ts
64
- const Product = bigal.getRepository(Product);
65
- // Type: IRepository<InferSelect<typeof Product.schema>>
66
- ```
67
-
68
- Throws if the model was not included in `models`.
69
-
70
- ## getReadonlyRepository()
71
-
72
- Returns a typed read-only repository for a model.
73
-
74
- ```ts
75
- const ViewRepo = bigal.getReadonlyRepository(StoreSummary);
76
- // Type: IReadonlyRepository<InferSelect<typeof StoreSummary.schema>>
77
23
  ```
78
24
 
79
- ## table()
80
-
81
- Creates a model with column metadata and inferred types. Exported as `defineTable` from the main
82
- `'bigal'` package to avoid naming conflicts.
83
-
84
- ```ts
85
- import { defineTable as table, serial, text } from 'bigal';
86
-
87
- export const Product = table(
88
- 'products',
89
- {
90
- id: serial().primaryKey(),
91
- name: text().notNull(),
92
- },
93
- {
94
- schema: 'public',
95
- readonly: false,
96
- hooks: {
97
- beforeCreate(v) {
98
- return v;
99
- },
100
- },
101
- filters: {
102
- active: { isActive: true },
103
- },
104
- },
105
- );
106
- ```
107
-
108
- **Parameters:**
109
-
110
- | Parameter | Type | Description |
111
- | ----------- | ------------------ | ----------------------------------- |
112
- | `tableName` | `string` | Database table or view name |
113
- | `schema` | `SchemaDefinition` | Column and relationship definitions |
114
- | `options` | `TableOptions` | Optional table-level configuration |
115
-
116
- **Returns:** `TableDefinition<TName, TSchema>` (frozen object)
117
-
118
- ### TableOptions
119
-
120
- | Option | Type | Description |
121
- | ------------ | ---------------------------------- | -------------------------------------------------- |
122
- | `schema` | `string` | PostgreSQL schema (default: `public`) |
123
- | `readonly` | `boolean` | If `true`, returns read-only repository |
124
- | `connection` | `string` | Named connection key |
125
- | `modelName` | `string` | Override auto-derived model name |
126
- | `hooks` | `ModelHooks` | Lifecycle hooks |
127
- | `filters` | `Record<string, FilterDefinition>` | Named filters auto-applied to find/findOne queries |
128
-
129
- ### TableDefinition
130
-
131
- The returned object exposes:
132
-
133
- | Property | Type | Description |
134
- | ----------------------- | ---------------------------------- | --------------------------------- |
135
- | `tableName` | `string` | Database table name |
136
- | `modelName` | `string` | Auto-derived or overridden name |
137
- | `dbSchema` | `string \| undefined` | PostgreSQL schema |
138
- | `isReadonly` | `boolean` | Whether this is a read-only model |
139
- | `connection` | `string \| undefined` | Named connection key |
140
- | `schema` | `SchemaDefinition` | The column definitions |
141
- | `hooks` | `ModelHooks \| undefined` | Lifecycle hooks |
142
- | `filters` | `Record<string, ...> \| undefined` | Global filters |
143
- | `columns` | `ColumnMetadata[]` | All column metadata |
144
- | `primaryKeyColumn` | `ColumnMetadata` | Primary key column metadata |
145
- | `columnsByPropertyName` | `Record<string, ColumnMetadata>` | Lookup by property name |
146
- | `columnsByColumnName` | `Record<string, ColumnMetadata>` | Lookup by database column name |
147
-
148
- ## view()
149
-
150
- Defines a read-only model backed by a PostgreSQL view. Equivalent to
151
- `table(name, schema, { readonly: true, ...options })`.
152
-
153
- ```ts
154
- import { view, serial, text, integer } from 'bigal';
155
-
156
- export const ProductSummary = view('product_summaries', {
157
- id: serial().primaryKey(),
158
- name: text().notNull(),
159
- storeName: text().notNull(),
160
- categoryCount: integer().notNull(),
161
- });
162
- ```
163
-
164
- ## Column builders
165
-
166
- All column builders accept an optional `{ name: 'column_name' }` options object. When omitted, the
167
- database column name is auto-derived from the property key using snakeCase conversion.
168
-
169
- ### Chain methods
170
-
171
- | Method | Description |
172
- | ----------------- | ------------------------------------------------------------------- |
173
- | `.notNull()` | Removes `null` from the type |
174
- | `.default(value)` | Makes column optional on insert |
175
- | `.primaryKey()` | Implies `.notNull()`, optional on insert |
176
- | `.unique()` | UNIQUE constraint (no type-level effect) |
177
- | `.version()` | Optimistic locking; implies `.notNull()`, auto-increments on update |
178
-
179
- ### Builder functions
180
-
181
- | Function | PostgreSQL type | TypeScript type |
182
- | ------------------------ | ---------------- | ------------------- |
183
- | `serial()` | SERIAL | `number` |
184
- | `bigserial()` | BIGSERIAL | `number` |
185
- | `text<T>()` | TEXT | `T \| null` |
186
- | `varchar<T>(options?)` | VARCHAR(n) | `T \| null` |
187
- | `integer()` | INTEGER | `number \| null` |
188
- | `bigint()` | BIGINT | `number \| null` |
189
- | `smallint()` | SMALLINT | `number \| null` |
190
- | `float()` / `real()` | REAL | `number \| null` |
191
- | `double()` | DOUBLE PRECISION | `number \| null` |
192
- | `boolean()` | BOOLEAN | `boolean \| null` |
193
- | `timestamp()` | TIMESTAMP | `Date \| null` |
194
- | `timestamptz()` | TIMESTAMPTZ | `Date \| null` |
195
- | `date()` | DATE | `Date \| null` |
196
- | `json<T>()` | JSON | `T \| null` |
197
- | `jsonb<T>()` | JSONB | `T \| null` |
198
- | `uuid()` | UUID | `string \| null` |
199
- | `bytea()` | BYTEA | `Buffer \| null` |
200
- | `textArray()` | TEXT[] | `string[] \| null` |
201
- | `integerArray()` | INTEGER[] | `number[] \| null` |
202
- | `booleanArray()` | BOOLEAN[] | `boolean[] \| null` |
203
- | `vector({ dimensions })` | VECTOR(n) | `number[] \| null` |
204
- | `createdAt()` | TIMESTAMPTZ | `Date` |
205
- | `updatedAt()` | TIMESTAMPTZ | `Date` |
206
-
207
- `serial()` and `bigserial()` imply `.notNull()` and `.default()`.
208
-
209
- `createdAt()` defaults to column name `created_at`. `updatedAt()` defaults to `updated_at`.
210
-
211
- `vector()` requires the `dimensions` option. Requires the pgvector PostgreSQL extension.
212
-
213
- ### VarcharOptions
214
-
215
- ```ts
216
- interface VarcharOptions {
217
- name?: string;
218
- length?: number;
219
- }
220
- ```
221
-
222
- ### VectorOptions
223
-
224
- ```ts
225
- interface VectorOptions {
226
- name?: string;
227
- dimensions: number;
228
- }
229
- ```
230
-
231
- ## Relationship builders
232
-
233
- ### belongsTo()
234
-
235
- Defines a many-to-one relationship where this table holds the foreign key.
236
-
237
- ```ts
238
- import { belongsTo } from 'bigal';
239
-
240
- store: belongsTo('Store'),
241
- store: belongsTo('Store', { name: 'shop_id' }),
242
- ```
243
-
244
- **Parameters:**
245
-
246
- | Parameter | Type | Description |
247
- | ----------- | ------------------------------ | ---------------------------------------------------- |
248
- | `modelName` | `string` | Model name string (e.g., `'Store'`) |
249
- | `options` | `string` or `{ name: string }` | FK column name (auto-derived as `snakeCase(key)_id`) |
250
-
251
- **Select type:** the FK type (typically `number`).
25
+ **Parameters:** `InitializeOptions`
252
26
 
253
- **Insert type:** the FK type (required by default).
27
+ | Option | Type | Required | Description |
28
+ | -------------- | ----------------------------- | -------- | --------------------------------------------- |
29
+ | `models` | `EntityStatic<Entity>[]` | Yes | Model classes decorated with `@table()` |
30
+ | `pool` | `PoolLike` | Yes | Primary connection pool |
31
+ | `readonlyPool` | `PoolLike` | No | Pool for read operations (defaults to `pool`) |
32
+ | `connections` | `Record<string, IConnection>` | No | Named connections for multi-database setups |
33
+ | `expose` | `(repo, metadata) => void` | No | Callback invoked for each created repository |
254
34
 
255
- ### hasMany()
256
-
257
- Defines a one-to-many or many-to-many relationship.
258
-
259
- ```ts
260
- import { hasMany } from 'bigal';
261
-
262
- // One-to-many
263
- products: hasMany('Product').via('store'),
264
-
265
- // Many-to-many
266
- categories: hasMany('Category')
267
- .through('ProductCategory')
268
- .via('product'),
269
- ```
270
-
271
- **Chain methods:**
272
-
273
- | Method | Description |
274
- | -------------------- | ----------------------------------------- |
275
- | `.via(propertyName)` | Property on the related table with the FK |
276
- | `.through(modelRef)` | Junction table for many-to-many |
277
-
278
- `hasMany` columns appear in `InferSelect` as optional `Record<string, unknown>[]` to support
279
- populate. `QueryResult` strips them from query results, so they only appear after `.populate()`.
35
+ **Returns:** `Record<string, IReadonlyRepository<Entity> | IRepository<Entity>>`
280
36
 
281
37
  ## Repository
282
38
 
283
- Full CRUD repository returned by `getRepository()` or object-style destructuring.
39
+ Full CRUD repository returned by `initialize()` for non-readonly models.
284
40
 
285
41
  ### find()
286
42
 
@@ -304,7 +60,7 @@ Returns a query builder for a single record or `null`. Options: `{ select?, pool
304
60
  repository.count(options?): CountQuery<T>
305
61
  ```
306
62
 
307
- Returns a query builder that resolves to a number. Options: `{ pool? }`.
63
+ Returns a query builder that resolves to a number. Options: `{ pool? }`. Prefer this over `findOne()` for existence checks — it performs better since it doesn't select or hydrate a row.
308
64
 
309
65
  ### create()
310
66
 
@@ -315,6 +71,8 @@ repository.create(values[], options?): Promise<QueryResult<T>[]>
315
71
 
316
72
  Insert one or multiple records. Options: `{ returnRecords?, returnSelect?, onConflict? }`.
317
73
 
74
+ An array inserts in a single statement. Prefer this over calling `create()` in a loop, which costs one round trip per record.
75
+
318
76
  ### update()
319
77
 
320
78
  ```ts
@@ -333,48 +91,25 @@ Delete matching records. Options: `{ returnRecords?, returnSelect? }`.
333
91
 
334
92
  ## ReadonlyRepository
335
93
 
336
- Read-only repository returned by `getReadonlyRepository()` or for models with `readonly: true`.
337
- Exposes `find()`, `findOne()`, and `count()` only.
94
+ Read-only repository returned for models with `readonly: true`. Exposes `find()`, `findOne()`, and `count()` only.
338
95
 
339
96
  ## Query builder methods
340
97
 
341
98
  All query types support fluent chaining. Each method returns a new immutable instance.
342
99
 
343
- | Method | Available on | Description |
344
- | ---------------------------------- | -------------------- | --------------------------------- |
345
- | `.where(query)` | find, findOne, count | Filter records |
346
- | `.sort(value)` | find, findOne | Order results |
347
- | `.limit(n)` | find | Limit rows returned |
348
- | `.skip(n)` | find | Skip rows |
349
- | `.paginate(page, pageSize)` | find | Shorthand for skip + limit |
350
- | `.withCount()` | find | Return `{ results, totalCount }` |
351
- | `.populate(relation, options?)` | find, findOne | Load related entities |
352
- | `.join(relation, alias?, on?)` | find, findOne | INNER JOIN |
353
- | `.leftJoin(relation, alias?, on?)` | find, findOne | LEFT JOIN |
354
- | `.distinctOn(columns)` | find | PostgreSQL DISTINCT ON |
355
- | `.filters(value)` | find, findOne | Override global filters |
356
- | `.toSQL()` | all operations | Get generated SQL without running |
357
-
358
- ### toSQL()
359
-
360
- Returns the generated SQL and parameters without executing the query. Available on `find`, `findOne`,
361
- `create`, `update`, and `destroy`:
362
-
363
- ```ts
364
- const { sql, params } = Product.find().where({ name: 'Widget' }).toSQL();
365
- ```
366
-
367
- ### filters()
368
-
369
- Override global filters defined on the table:
370
-
371
- ```ts
372
- // Disable all filters
373
- Product.find().where({}).filters(false);
374
-
375
- // Disable a specific filter
376
- Product.find().where({}).filters({ active: false });
377
- ```
100
+ | Method | Available on | Description |
101
+ | ---------------------------------- | -------------------- | -------------------------------- |
102
+ | `.where(query)` | find, findOne, count | Filter records |
103
+ | `.sort(value)` | find, findOne | Order results |
104
+ | `.limit(n)` | find | Limit rows returned |
105
+ | `.skip(n)` | find | Skip rows |
106
+ | `.paginate(page, pageSize)` | find | Shorthand for skip + limit |
107
+ | `.withCount()` | find | Return `{ results, totalCount }` |
108
+ | `.populate(relation, options?)` | find, findOne | Load related entities |
109
+ | `.join(relation, alias?, on?)` | find, findOne | INNER JOIN |
110
+ | `.leftJoin(relation, alias?, on?)` | find, findOne | LEFT JOIN |
111
+ | `.distinctOn(columns)` | find | PostgreSQL DISTINCT ON |
112
+ | `.toJSON()` | find, findOne | Return plain objects |
378
113
 
379
114
  ## subquery()
380
115
 
@@ -384,93 +119,71 @@ import { subquery } from 'bigal';
384
119
  const sub = subquery(repository);
385
120
  ```
386
121
 
387
- Returns a `SubqueryBuilder` with methods: `select()`, `where()`, `sort()`, `limit()`, `groupBy()`,
388
- `having()`, `distinctOn()`.
122
+ Returns a `SubqueryBuilder` with methods: `select()`, `where()`, `sort()`, `limit()`, `groupBy()`, `having()`, `distinctOn()`.
389
123
 
390
- Scalar aggregate shortcuts: `sub.count()`, `sub.sum(col)`, `sub.avg(col)`, `sub.max(col)`,
391
- `sub.min(col)`.
124
+ Scalar aggregate shortcuts: `sub.count()`, `sub.sum(col)`, `sub.avg(col)`, `sub.max(col)`, `sub.min(col)`.
392
125
 
393
- ## Types
126
+ ## Decorators
394
127
 
395
- ### Repository\<T\>
128
+ ### @table(options)
396
129
 
397
- Type alias for a typed read-write repository. Accepts `typeof YourTableDef`:
130
+ Binds a class to a database table or view.
398
131
 
399
- ```ts
400
- import type { Repository } from 'bigal';
132
+ | Option | Type | Description |
133
+ | ------------ | --------- | -------------------------------------- |
134
+ | `name` | `string` | Table or view name |
135
+ | `schema` | `string` | PostgreSQL schema (default: `public`) |
136
+ | `readonly` | `boolean` | Returns `ReadonlyRepository` if `true` |
137
+ | `connection` | `string` | Named connection key |
401
138
 
402
- function process(repo: Repository<typeof Product>) {
403
- /* ... */
404
- }
405
- ```
139
+ ### @primaryColumn(options)
406
140
 
407
- ### ReadonlyRepository\<T\>
141
+ Marks the primary key. Options: `{ type }`.
408
142
 
409
- Type alias for a typed read-only repository:
143
+ ### @column(options)
410
144
 
411
- ```ts
412
- import type { ReadonlyRepository } from 'bigal';
145
+ Defines a column. See [Models > Column options](/guide/models#column-options) for all options.
146
+ Vector columns are declared with `{ type: 'vector', dimensions: n }` (`dimensions` is informational —
147
+ BigAl does not issue DDL).
413
148
 
414
- function read(repo: ReadonlyRepository<typeof ProductSummary>) {
415
- /* ... */
416
- }
417
- ```
149
+ ### @createDateColumn()
418
150
 
419
- ### InferSelect\<TSchema\>
151
+ Auto-set on insert.
420
152
 
421
- Mapped type that extracts the row type from a schema definition:
153
+ ### @updateDateColumn()
422
154
 
423
- ```ts
424
- type ProductRow = InferSelect<(typeof Product)['schema']>;
425
- ```
155
+ Auto-set on update.
426
156
 
427
- ### InferInsert\<TSchema\>
157
+ ### @versionColumn()
428
158
 
429
- Mapped type that extracts the insert parameter type from a schema definition:
159
+ Auto-incrementing version for optimistic locking.
430
160
 
431
- ```ts
432
- type ProductInsert = InferInsert<(typeof Product)['schema']>;
433
- ```
161
+ ## Types
434
162
 
435
- ### ModelHooks\<TInsert, TSelect\>
163
+ ### Entity
436
164
 
437
- ```ts
438
- interface ModelHooks<TInsert, TSelect = TInsert> {
439
- beforeCreate?: (values: TInsert) => Promise<TInsert> | TInsert;
440
- afterCreate?: (result: TSelect) => Promise<void> | void;
441
- beforeUpdate?: (values: Partial<TInsert>) => Partial<TInsert> | Promise<Partial<TInsert>>;
442
- afterUpdate?: (result: TSelect) => Promise<void> | void;
443
- beforeDestroy?: (where: Record<string, unknown>) => Promise<Record<string, unknown>> | Record<string, unknown>;
444
- afterDestroy?: (result: { rowCount: number }) => Promise<void> | void;
445
- afterFind?: (results: TSelect[]) => Promise<TSelect[]> | TSelect[];
446
- }
447
- ```
165
+ Base class for all models.
448
166
 
449
- ### FilterDefinition
167
+ ### NotEntity\<T\>
450
168
 
451
- ```ts
452
- type FilterDefinition = (() => Record<string, unknown>) | Record<string, unknown>;
453
- ```
169
+ Wrapper type for JSON column objects that have an `id` field. Prevents BigAl's type system from treating them as entities.
454
170
 
455
- A static where clause object or a function that returns one dynamically.
171
+ ### QueryResult\<T\>
456
172
 
457
- ### OnQueryCallback
173
+ Narrows relationship fields from union types to foreign key types. See [Relationships > QueryResult](/guide/relationships#queryresult-type-narrowing).
458
174
 
459
- ```ts
460
- type OnQueryCallback = (event: OnQueryEvent) => void;
461
- ```
175
+ ### QueryResultPopulated\<T, K\>
462
176
 
463
- ### OnQueryEvent
177
+ Type for entities with specific relationships populated.
178
+
179
+ ### TypedAggregateExpression\<Alias\>
180
+
181
+ Return type annotation for aggregate callbacks that enables type-safe sorting on subquery join columns.
182
+
183
+ ### VectorDistanceMetric
464
184
 
465
185
  ```ts
466
- interface OnQueryEvent {
467
- sql: string;
468
- params: readonly unknown[];
469
- duration: number;
470
- error?: Error;
471
- model: string;
472
- operation: 'count' | 'create' | 'destroy' | 'find' | 'findOne' | 'update';
473
- }
186
+ type VectorDistanceMetric = 'cosine' | 'innerProduct' | 'l1' | 'l2';
474
187
  ```
475
188
 
476
189
  ### VectorDistanceSort
@@ -478,42 +191,31 @@ interface OnQueryEvent {
478
191
  ```ts
479
192
  interface VectorDistanceSort {
480
193
  nearestTo: number[];
481
- metric?: 'cosine' | 'innerProduct' | 'l1' | 'l2';
194
+ metric?: VectorDistanceMetric;
482
195
  }
483
196
  ```
484
197
 
485
- Used in `.sort()` for nearest-neighbor queries on vector columns.
198
+ Used in `.sort()` for nearest-neighbor queries on vector columns. See
199
+ [Querying > Vector distance queries](/guide/querying#vector-distance-queries).
486
200
 
487
- ### VectorDistanceMetric
488
-
489
- ```ts
490
- type VectorDistanceMetric = 'cosine' | 'innerProduct' | 'l1' | 'l2';
491
- ```
492
-
493
- ### QueryResult\<T\>
494
-
495
- Produces the row type for query results. Accepts a `TableDefinition` directly or a row type.
496
- Strips hasMany collections (which appear in `InferSelect` as optional arrays) and narrows
497
- belongsTo FK types:
201
+ ### VectorDistanceConstraint
498
202
 
499
203
  ```ts
500
- // From a TableDefinition - strips hasMany, narrows FKs
501
- type ProductRow = QueryResult<typeof Product>;
502
-
503
- // From a row type - passthrough
504
- type Row = QueryResult<{ id: number; name: string }>;
204
+ interface VectorDistanceConstraint {
205
+ nearestTo: number[];
206
+ metric?: VectorDistanceMetric;
207
+ distance: Partial<Record<'<' | '<=' | '>' | '>=', number>>;
208
+ }
505
209
  ```
506
210
 
507
- See [Relationships > QueryResult](/guide/relationships#queryresult-type-narrowing).
508
-
509
- ### TypedAggregateExpression\<Alias\>
510
-
511
- Return type annotation for aggregate callbacks that enables type-safe sorting on subquery join columns.
211
+ Used in where clauses to filter vector columns by distance threshold. At least one `distance` bound is
212
+ required (pgvector distance operators return a number, so a bare distance expression is not a valid
213
+ where clause); multiple bounds are combined with `AND`. To order by distance without filtering, use
214
+ `sort()` with `nearestTo` instead.
512
215
 
513
216
  ### PoolLike
514
217
 
515
- Interface for compatible connection pools. Supported: `postgres-pool`, `pg`,
516
- `@neondatabase/serverless`.
218
+ Interface for compatible connection pools. Supported: `postgres-pool`, `pg`, `@neondatabase/serverless`.
517
219
 
518
220
  ### IConnection
519
221