@biref/scanner 0.0.1

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,1507 @@
1
+ /**
2
+ * Structural type for the database client the Postgres adapter consumes.
3
+ *
4
+ * Declares a single `query` method that takes a SQL string plus
5
+ * optional parameters and returns an object with a `rows` array.
6
+ * `pg.Client`, `pg.Pool`, and any structurally compatible library
7
+ * satisfy the shape without the SDK importing `pg`.
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * import pg from 'pg';
12
+ * const client = new pg.Client({ ... });
13
+ * await client.connect();
14
+ * postgresAdapter.create(client);
15
+ * ```
16
+ */
17
+ interface PostgresClient {
18
+ query<TRow = unknown>(text: string, params?: readonly unknown[]): Promise<{
19
+ rows: TRow[];
20
+ }>;
21
+ }
22
+
23
+ /**
24
+ * The paradigm of an underlying data store.
25
+ *
26
+ * Used as a discriminator on `DataModel` so consumers can branch on the
27
+ * paradigm without inspecting adapter-specific details. New paradigms can
28
+ * be added without breaking existing consumers; fall back to `'unknown'`
29
+ * if you do not recognize a value.
30
+ */
31
+ type EngineKind = 'relational' | 'document' | 'key-value' | 'graph' | 'wide-column' | 'time-series' | 'unknown';
32
+
33
+ /**
34
+ * Kind of constraint declared on an entity.
35
+ *
36
+ * Categories:
37
+ * 'unique': enforces uniqueness on a set of fields
38
+ * 'check': evaluates an expression that must be true for every row
39
+ * 'exclusion': pg-style exclusion constraint
40
+ * 'custom': paradigm-specific constraint that does not fit the above
41
+ */
42
+ type ConstraintKind = 'unique' | 'check' | 'exclusion' | 'custom';
43
+ /**
44
+ * A constraint declared on an entity.
45
+ *
46
+ * Excludes the primary key, which is exposed through the entity's
47
+ * `identifier` instead. Excludes foreign keys, which are exposed through
48
+ * `relationships`.
49
+ *
50
+ * Fields:
51
+ * name: constraint name as reported by the data store
52
+ * kind: category of constraint
53
+ * fields: field names the constraint applies to
54
+ * expression: textual definition (e.g. CHECK clause body) or null
55
+ */
56
+ interface Constraint {
57
+ readonly name: string;
58
+ readonly kind: ConstraintKind;
59
+ readonly fields: readonly string[];
60
+ readonly expression: string | null;
61
+ }
62
+
63
+ /**
64
+ * Normalized category of a field's type, abstracted from any specific
65
+ * data store's native type system.
66
+ *
67
+ * Adapters are responsible for mapping their native types onto these
68
+ * categories. Consumers should branch on `category` rather than
69
+ * `nativeType` for portable, paradigm-neutral logic (e.g. "render a
70
+ * date picker for `date` fields").
71
+ */
72
+ type FieldTypeCategory = 'string' | 'integer' | 'decimal' | 'boolean' | 'date' | 'timestamp' | 'time' | 'json' | 'uuid' | 'binary' | 'enum' | 'array' | 'reference' | 'unknown';
73
+ /**
74
+ * A field's type, normalized across data store paradigms.
75
+ *
76
+ * `nativeType` is the raw string the adapter reported, kept for
77
+ * transparency and edge cases. Optional properties carry additional
78
+ * structure when meaningful for the category.
79
+ */
80
+ interface FieldType {
81
+ readonly category: FieldTypeCategory;
82
+ readonly nativeType: string;
83
+ /** Maximum length, when applicable (e.g. varchar(255)). */
84
+ readonly length?: number;
85
+ /** Numeric precision, when applicable. */
86
+ readonly precision?: number;
87
+ /** Numeric scale, when applicable. */
88
+ readonly scale?: number;
89
+ /** Allowed values, for enum-typed fields. */
90
+ readonly enumValues?: readonly string[];
91
+ /** Element type, for array/collection fields. */
92
+ readonly elementType?: FieldType;
93
+ }
94
+
95
+ /**
96
+ * A single field within an entity.
97
+ *
98
+ * Maps to:
99
+ * - relational: column
100
+ * - document: document field
101
+ * - graph: node/edge property
102
+ *
103
+ * `nullable` and `defaultValue` may be best-effort approximations in
104
+ * paradigms without strict schemas. Adapters document how they determine
105
+ * each value.
106
+ */
107
+ interface Field {
108
+ readonly name: string;
109
+ readonly type: FieldType;
110
+ readonly nullable: boolean;
111
+ /**
112
+ * True if this field participates in the entity's identifier.
113
+ * Paradigm-neutral replacement for "primary key".
114
+ */
115
+ readonly isIdentifier: boolean;
116
+ readonly defaultValue: string | null;
117
+ readonly description: string | null;
118
+ }
119
+
120
+ /**
121
+ * Kind of index physically created on an entity.
122
+ *
123
+ * Includes the common Postgres families. Adapters that target other
124
+ * stores map their native index types onto these or report 'unknown'.
125
+ */
126
+ type IndexKind = 'btree' | 'hash' | 'gin' | 'gist' | 'brin' | 'spgist' | 'unknown';
127
+ /**
128
+ * An index defined on an entity.
129
+ *
130
+ * Excludes the index that backs the primary key, since that information
131
+ * is already available through the entity's `identifier`.
132
+ *
133
+ * Fields:
134
+ * name: index name as reported by the data store
135
+ * fields: field names the index covers, in order
136
+ * unique: true if the index enforces uniqueness
137
+ * kind: physical index type
138
+ * partial: true if the index has a WHERE clause
139
+ * definition: textual definition (e.g. CREATE INDEX statement) or null
140
+ */
141
+ interface Index {
142
+ readonly name: string;
143
+ readonly fields: readonly string[];
144
+ readonly unique: boolean;
145
+ readonly kind: IndexKind;
146
+ readonly partial: boolean;
147
+ readonly definition: string | null;
148
+ }
149
+
150
+ /**
151
+ * Action a data store takes when a referenced row/document is updated or
152
+ * deleted. `null` indicates the paradigm does not enforce referential
153
+ * actions (e.g. document stores).
154
+ */
155
+ type ReferentialAction = 'no-action' | 'restrict' | 'cascade' | 'set-null' | 'set-default';
156
+ /**
157
+ * A qualified pointer to an entity, used to identify the source and
158
+ * target of a `Reference` without embedding the full entity.
159
+ */
160
+ interface EntityRef {
161
+ readonly namespace: string;
162
+ readonly name: string;
163
+ }
164
+ /**
165
+ * A pointer from one set of fields in one entity to another set of
166
+ * fields in another entity.
167
+ *
168
+ * Paradigm-neutral abstraction:
169
+ * - relational: built from a declared FOREIGN KEY constraint
170
+ * - document: inferred from naming conventions and value sampling
171
+ * - graph: built from declared edge definitions
172
+ *
173
+ * `confidence` is `1` for declared references (e.g. SQL foreign keys)
174
+ * and less than `1` for inferred references. Adapters that only ever
175
+ * return declared references should always set this to `1`.
176
+ */
177
+ interface Reference {
178
+ readonly name: string;
179
+ readonly fromEntity: EntityRef;
180
+ readonly fromFields: readonly string[];
181
+ readonly toEntity: EntityRef;
182
+ readonly toFields: readonly string[];
183
+ readonly confidence: number;
184
+ readonly onUpdate: ReferentialAction | null;
185
+ readonly onDelete: ReferentialAction | null;
186
+ }
187
+
188
+ /**
189
+ * Direction of a relationship from the perspective of the entity that
190
+ * owns it.
191
+ *
192
+ * - 'outbound': this entity holds the reference and points outward.
193
+ * - 'inbound': another entity holds a reference that points to this one.
194
+ *
195
+ * Storing both directions on each entity is the central feature of the
196
+ * SDK. Most introspection tools only expose outbound relationships,
197
+ * forcing consumers to know which other entities reference theirs ahead
198
+ * of time.
199
+ */
200
+ type RelationshipDirection = 'outbound' | 'inbound';
201
+ /**
202
+ * A relationship as seen from a specific entity, with a direction.
203
+ *
204
+ * The same underlying `Reference` appears as 'outbound' on the source
205
+ * entity and 'inbound' on the target entity.
206
+ */
207
+ interface Relationship {
208
+ readonly direction: RelationshipDirection;
209
+ readonly reference: Reference;
210
+ }
211
+
212
+ /**
213
+ * A unit of data with named fields, identifier, relationships,
214
+ * constraints, and indexes.
215
+ *
216
+ * Maps to:
217
+ * relational: table or view
218
+ * document: collection
219
+ * graph: node label
220
+ * key-value: bucket
221
+ *
222
+ * Fields:
223
+ * namespace: schema or database the entity lives in
224
+ * name: entity name
225
+ * fields: the entity's fields, in declaration order
226
+ * identifier: field names that uniquely identify a row of this entity;
227
+ * contains all fields involved in a composite identifier;
228
+ * empty when the entity has no identifier
229
+ * relationships: all relationships involving this entity, in BOTH
230
+ * directions; filter by `direction` to get outbound or
231
+ * inbound separately
232
+ * constraints: constraints declared on the entity (unique, check,
233
+ * exclusion, custom); excludes the primary key
234
+ * indexes: indexes defined on the entity; excludes the index
235
+ * that backs the primary key
236
+ * description: human-readable comment, when available
237
+ */
238
+ interface Entity {
239
+ readonly namespace: string;
240
+ readonly name: string;
241
+ readonly fields: readonly Field[];
242
+ readonly identifier: readonly string[];
243
+ readonly relationships: readonly Relationship[];
244
+ readonly constraints: readonly Constraint[];
245
+ readonly indexes: readonly Index[];
246
+ readonly description: string | null;
247
+ }
248
+
249
+ /**
250
+ * The aggregate root for an introspected data store.
251
+ *
252
+ * Holds all entities discovered by an `Introspector`, indexed for
253
+ * efficient lookup, and exposes navigation helpers around relationships.
254
+ *
255
+ * Paradigm-neutral: the same shape is returned for relational, document,
256
+ * graph, and other data stores. Use `kind` to branch when needed.
257
+ */
258
+ declare class DataModel {
259
+ readonly kind: EngineKind;
260
+ readonly entities: readonly Entity[];
261
+ private readonly index;
262
+ constructor(kind: EngineKind, entities: readonly Entity[]);
263
+ /**
264
+ * Build the lookup key for an entity. Exposed as a static helper so
265
+ * consumers can compute keys consistently when building their own
266
+ * indexes on top of a `DataModel`.
267
+ */
268
+ static qualifiedName(namespace: string, name: string): string;
269
+ getEntity(namespace: string, name: string): Entity | undefined;
270
+ hasEntity(namespace: string, name: string): boolean;
271
+ /**
272
+ * All relationships an entity participates in, in both directions.
273
+ * Returns an empty array if the entity is unknown.
274
+ */
275
+ relationshipsOf(namespace: string, name: string): readonly Relationship[];
276
+ /**
277
+ * Outbound relationships: this entity holds the reference.
278
+ * In a relational store these correspond to FOREIGN KEY constraints
279
+ * declared by the entity itself.
280
+ */
281
+ outboundRelationshipsOf(namespace: string, name: string): readonly Relationship[];
282
+ /**
283
+ * Inbound relationships: other entities hold references pointing here.
284
+ *
285
+ * This is the differentiating feature of the SDK. Most introspection
286
+ * tools only surface outbound relationships, requiring consumers to
287
+ * know which other entities reference theirs ahead of time.
288
+ */
289
+ inboundRelationshipsOf(namespace: string, name: string): readonly Relationship[];
290
+ }
291
+
292
+ /**
293
+ * Port: a paradigm-specific introspector capable of producing a
294
+ * `DataModel` from an underlying data store.
295
+ *
296
+ * Implementations are provided by adapters, registered with an
297
+ * `AdapterRegistry`, and resolved through the public `Scanner` facade.
298
+ *
299
+ * Adapters are responsible for accepting their own native client/driver
300
+ * in their constructor. The SDK does not impose a unified client
301
+ * abstraction because the shape of "a client" varies wildly across
302
+ * paradigms.
303
+ */
304
+ interface Introspector {
305
+ /** Stable adapter identifier (e.g. 'postgres', 'mongo'). */
306
+ readonly name: string;
307
+ /** Paradigm of the underlying data store. */
308
+ readonly kind: EngineKind;
309
+ /** Read the data store and produce a paradigm-neutral `DataModel`. */
310
+ introspect(options?: IntrospectOptions): Promise<DataModel>;
311
+ }
312
+ interface IntrospectOptions {
313
+ /**
314
+ * Namespaces to include. The adapter chooses a default if omitted
315
+ * (Postgres picks `['public']`). Pass the literal string `'all'`
316
+ * to scan every non-system namespace the adapter can discover -
317
+ * useful when bootstrapping codegen against an unfamiliar
318
+ * database.
319
+ */
320
+ readonly namespaces?: readonly string[] | 'all';
321
+ /** Optional entity allowlist (matched after namespace filtering). */
322
+ readonly includeEntities?: readonly string[];
323
+ /** Optional entity denylist (matched after namespace filtering). */
324
+ readonly excludeEntities?: readonly string[];
325
+ }
326
+
327
+ /**
328
+ * Postgres implementation of the `Introspector` port.
329
+ *
330
+ * Runs six queries against `pg_catalog` in parallel (tables, columns,
331
+ * primary keys, foreign keys, indexes, constraints) and hands the
332
+ * result to `EntityAssembler`, which stitches them into a paradigm-
333
+ * neutral `DataModel`.
334
+ *
335
+ * What it returns for each entity:
336
+ * - fields with normalized type categories
337
+ * - identifier (primary key columns)
338
+ * - relationships in BOTH directions
339
+ * - constraints (unique, check, exclusion)
340
+ * - indexes (excluding the primary key index)
341
+ *
342
+ * Accepts any client that satisfies `PostgresClient`, so it works with
343
+ * `pg.Client`, `pg.Pool`, or any compatible library without the SDK
344
+ * importing `pg`.
345
+ */
346
+ declare class PostgresIntrospector implements Introspector {
347
+ private readonly client;
348
+ readonly name = "postgres";
349
+ readonly kind: "relational";
350
+ constructor(client: PostgresClient);
351
+ introspect(options?: IntrospectOptions): Promise<DataModel>;
352
+ private resolveNamespaces;
353
+ private static readonly ALL_NAMESPACES_SQL;
354
+ private static applyEntityFilters;
355
+ }
356
+
357
+ /**
358
+ * Adapter metadata constants for Postgres.
359
+ *
360
+ * Centralized so the rest of the adapter never references the literal
361
+ * string 'postgres' or the URL schemes by hand. Anywhere those values
362
+ * are needed, import the constant.
363
+ */
364
+ declare const POSTGRES_ADAPTER_NAME = "postgres";
365
+ /** Literal type for the Postgres adapter name, used for discrimination. */
366
+ type PostgresAdapterName = typeof POSTGRES_ADAPTER_NAME;
367
+ declare const POSTGRES_URL_SCHEMES: readonly string[];
368
+
369
+ /**
370
+ * Output of `QueryEngine.build`.
371
+ *
372
+ * Holds:
373
+ * command: the engine-specific instruction to execute
374
+ * params: the bound parameter values
375
+ * metadata: information about the engine and parameter count
376
+ *
377
+ * The SDK never executes a `BuiltQuery`. The consumer passes `command`
378
+ * and `params` to their own driver.
379
+ *
380
+ * `TCommand` is the shape of the command for a given engine:
381
+ * relational engines -> `BuiltQuery<string>` (SQL string)
382
+ * document engines -> `BuiltQuery<DocFilter>` (filter object)
383
+ */
384
+ interface BuiltQuery<TCommand = unknown> {
385
+ readonly command: TCommand;
386
+ readonly params: readonly unknown[];
387
+ readonly metadata: BuiltQueryMetadata;
388
+ }
389
+ interface BuiltQueryMetadata {
390
+ /** Engine that produced this query (e.g. 'postgres', 'mongo'). */
391
+ readonly engine: string;
392
+ /** Number of parameters bound. */
393
+ readonly paramCount: number;
394
+ }
395
+
396
+ /**
397
+ * Declarative description of a query against a `DataModel`.
398
+ *
399
+ * Paradigm-neutral: the same `QuerySpec` can be handed to a relational
400
+ * `QueryEngine` (which produces a SQL string) or a document
401
+ * `QueryEngine` (which produces a Mongo filter, etc).
402
+ *
403
+ * Engines are responsible for raising clear errors when a spec contains
404
+ * something they cannot represent in their target language.
405
+ */
406
+ interface QuerySpec {
407
+ readonly namespace: string;
408
+ readonly entity: string;
409
+ /** Field names to project. `undefined` means "all fields". */
410
+ readonly select?: readonly string[];
411
+ readonly filters?: readonly Filter[];
412
+ readonly orderBy?: readonly OrderBy[];
413
+ readonly limit?: number;
414
+ readonly offset?: number;
415
+ }
416
+ interface Filter {
417
+ readonly field: string;
418
+ readonly operator: FilterOperator;
419
+ readonly value: unknown;
420
+ }
421
+ type FilterOperator = 'eq' | 'neq' | 'lt' | 'lte' | 'gt' | 'gte' | 'like' | 'ilike' | 'in' | 'not-in' | 'is-null' | 'is-not-null' | 'between';
422
+ interface OrderBy {
423
+ readonly field: string;
424
+ readonly direction: 'asc' | 'desc';
425
+ }
426
+
427
+ /**
428
+ * Port: a paradigm-specific engine that turns a `QuerySpec` into a
429
+ * ready-to-execute `BuiltQuery`.
430
+ *
431
+ * `TCommand` lets each engine pick the right command shape for its
432
+ * paradigm: a SQL string, a Mongo filter, a Cypher query, etc. The
433
+ * default of `unknown` is intentional. Code that holds an
434
+ * engine-agnostic reference cannot accidentally assume a command shape.
435
+ */
436
+ interface QueryEngine<TCommand = unknown> {
437
+ /** Stable engine identifier (e.g. 'postgres', 'mongo'). */
438
+ readonly name: string;
439
+ /** Paradigm this engine targets. */
440
+ readonly kind: EngineKind;
441
+ /**
442
+ * Build a query from a declarative spec, validating it against the
443
+ * provided `DataModel`. Engines must throw on unknown entities or
444
+ * fields rather than silently producing broken queries.
445
+ */
446
+ build(spec: QuerySpec, model: DataModel): BuiltQuery<TCommand>;
447
+ }
448
+
449
+ /**
450
+ * Postgres implementation of the `QueryEngine` port.
451
+ *
452
+ * Produces parameterized SQL using `$1`, `$2`, ... placeholders and
453
+ * double-quoted identifiers. Validates the spec against the
454
+ * `DataModel` before emitting any SQL: unknown entities or fields
455
+ * throw rather than yielding a broken query.
456
+ *
457
+ * Scope:
458
+ * - SELECT against a single entity
459
+ * - WHERE filters (every `FilterOperator`)
460
+ * - ORDER BY
461
+ * - LIMIT and OFFSET
462
+ * - No joins, group by, or aggregates yet
463
+ */
464
+ declare class PostgresQueryEngine implements QueryEngine<string> {
465
+ readonly name = "postgres";
466
+ readonly kind: "relational";
467
+ build(spec: QuerySpec, model: DataModel): BuiltQuery<string>;
468
+ }
469
+
470
+ /**
471
+ * The set of JS values a `RecordParser` produces after coercing raw
472
+ * driver output. Covers the common cases without pulling in
473
+ * framework-specific types.
474
+ *
475
+ * `null` represents both SQL NULL and a missing document field.
476
+ */
477
+ type ParsedValue = string | number | bigint | boolean | Date | null | ParsedValue[] | {
478
+ readonly [key: string]: ParsedValue;
479
+ };
480
+ /**
481
+ * A single record after parsing. A map of field name to coerced value.
482
+ *
483
+ * Returned by `RecordParser.parse`. Consumed by `Formatter.serialize`.
484
+ */
485
+ interface ParsedRecord {
486
+ readonly [field: string]: ParsedValue;
487
+ }
488
+
489
+ /**
490
+ * Port: turns raw rows from a database driver into typed `ParsedRecord`s
491
+ * using an `Entity`'s field metadata to coerce values.
492
+ *
493
+ * Implementations live in the SDK (default) or can be provided by users
494
+ * who need custom coercion (e.g. mapping `decimal` columns to a Big.js
495
+ * type instead of the default string).
496
+ */
497
+ interface RecordParser {
498
+ /** Parse a single raw row using the entity's field definitions. */
499
+ parse(entity: Entity, row: Readonly<Record<string, unknown>>): ParsedRecord;
500
+ /** Parse many rows. Convenience wrapper around `parse`. */
501
+ parseMany(entity: Entity, rows: readonly Readonly<Record<string, unknown>>[]): readonly ParsedRecord[];
502
+ }
503
+
504
+ /**
505
+ * Default `RecordParser` used when no adapter-specific parser is needed.
506
+ *
507
+ * Coerces raw driver output to JS values using the field type
508
+ * categories from the `DataModel`:
509
+ * string | uuid | enum -> JS string
510
+ * integer -> JS number (or bigint if raw is bigint)
511
+ * decimal -> JS string (preserves precision)
512
+ * boolean -> JS boolean
513
+ * date | timestamp | time -> JS Date
514
+ * json -> pass-through
515
+ * binary -> pass-through
516
+ * array -> pass-through if already an array, else []
517
+ * reference -> pass-through
518
+ * unknown -> pass-through
519
+ *
520
+ * Null and undefined raw values become `null`. Fields on the entity
521
+ * that are absent from the row are omitted from the result, so a
522
+ * partial projection only emits the keys that were selected.
523
+ *
524
+ * The `coerce` method is `protected` so adapter-specific subclasses
525
+ * (for example `PostgresRecordParser`) can override or extend it.
526
+ */
527
+ declare class DefaultRecordParser implements RecordParser {
528
+ parse(entity: Entity, row: Readonly<Record<string, unknown>>): ParsedRecord;
529
+ parseMany(entity: Entity, rows: readonly Readonly<Record<string, unknown>>[]): readonly ParsedRecord[];
530
+ protected coerce(field: Field, raw: unknown): ParsedValue;
531
+ }
532
+
533
+ /**
534
+ * Postgres-aware `RecordParser`.
535
+ *
536
+ * Extends `DefaultRecordParser` with behavior that matches how the
537
+ * `pg` driver returns Postgres values:
538
+ *
539
+ * - `int8` / `bigint` columns are returned by `pg` as strings by
540
+ * default (to preserve precision). This parser converts them to
541
+ * JS `bigint` so downstream code does not silently lose digits.
542
+ *
543
+ * - `json` / `jsonb` columns are usually already parsed by `pg`,
544
+ * but when they arrive as strings (for example from drivers that
545
+ * do not auto-parse) this parser parses them.
546
+ *
547
+ * - All other categories fall through to `DefaultRecordParser`.
548
+ *
549
+ * Use this parser when consuming rows that come from a `pg.Client` or
550
+ * `pg.Pool`. For adapters that handle these concerns themselves, the
551
+ * default parser is still appropriate.
552
+ */
553
+ declare class PostgresRecordParser extends DefaultRecordParser {
554
+ protected coerce(field: Field, raw: unknown): ParsedValue;
555
+ }
556
+
557
+ /**
558
+ * Port: executes a `BuiltQuery` against an underlying driver and
559
+ * returns the raw rows.
560
+ *
561
+ * Sits one layer below `RecordParser`: the runner only hands back
562
+ * untyped row objects, and the parser turns them into `ParsedRecord`s
563
+ * using the entity's field metadata.
564
+ *
565
+ * Each adapter implements this against its own client (for example
566
+ * `PostgresRawQueryRunner` wraps a `PostgresClient`). Used by
567
+ * `QueryPlanExecutor` to drive the typed query API.
568
+ *
569
+ * @example
570
+ * ```ts
571
+ * const rows = await runner.run(built);
572
+ * ```
573
+ */
574
+ interface RawQueryRunner {
575
+ run<TRow = unknown>(built: BuiltQuery): Promise<readonly TRow[]>;
576
+ }
577
+
578
+ /**
579
+ * Names of the adapters that ship with the SDK.
580
+ *
581
+ * Grows as new adapters are added. Consumers can still register custom
582
+ * adapters with any name; the type accepts arbitrary strings in
583
+ * addition to the known ones (see `AdapterName`).
584
+ */
585
+ type KnownAdapterName = 'postgres';
586
+ /**
587
+ * Adapter name type.
588
+ *
589
+ * Known adapter names appear in IDE autocomplete when calling
590
+ * `Biref.scan(...)`, `AdapterRegistry.get(...)`, and similar APIs.
591
+ * Any other string is still accepted so consumers can register
592
+ * adapters the SDK does not know about.
593
+ *
594
+ * Pattern: `KnownAdapterName | (string & {})` preserves literal
595
+ * suggestions while also accepting the broader string type.
596
+ */
597
+ type AdapterName = KnownAdapterName | (string & {});
598
+ /**
599
+ * An adapter packaged for registration with `Biref` or `AdapterRegistry`.
600
+ *
601
+ * Generic over `TName` so each adapter declares its name as a literal
602
+ * string type. Consumers narrow by comparing `adapter.name` against an
603
+ * exported constant from the adapter module:
604
+ *
605
+ * import { POSTGRES_ADAPTER_NAME } from '@biref/scanner';
606
+ *
607
+ * if (adapter.name === POSTGRES_ADAPTER_NAME) {
608
+ * // adapter is narrowed to Adapter<'postgres'>
609
+ * }
610
+ *
611
+ * Fields:
612
+ * name: literal name of the adapter (e.g. 'postgres')
613
+ * introspector: produces a `DataModel` from the data store
614
+ * engine: turns a `QuerySpec` into a `BuiltQuery`
615
+ * runner: executes a `BuiltQuery` and returns raw rows
616
+ * parser: coerces raw rows into typed `ParsedRecord`s
617
+ * urlSchemes: URL schemes the adapter handles, for auto-detection
618
+ *
619
+ * `runner` and `parser` are optional on the interface so older adapter
620
+ * modules stay source-compatible. The typed query API requires both.
621
+ */
622
+ interface Adapter<TName extends AdapterName = AdapterName> {
623
+ readonly name: TName;
624
+ readonly introspector: Introspector;
625
+ readonly engine: QueryEngine;
626
+ readonly runner?: RawQueryRunner;
627
+ readonly parser?: RecordParser;
628
+ readonly urlSchemes?: readonly string[];
629
+ }
630
+ /**
631
+ * Registry of `Adapter` instances keyed by name.
632
+ *
633
+ * Looked up by `Biref` and the lower-level facades. Registration is
634
+ * explicit: the SDK has zero runtime dependencies and cannot know
635
+ * which adapters are installed.
636
+ */
637
+ declare class AdapterRegistry {
638
+ private readonly adapters;
639
+ register(adapter: Adapter): void;
640
+ unregister(name: AdapterName): boolean;
641
+ get(name: AdapterName): Adapter;
642
+ has(name: AdapterName): boolean;
643
+ list(): readonly string[];
644
+ /**
645
+ * Find an adapter that claims the given URL scheme. Case-insensitive.
646
+ * Returns `undefined` if no adapter handles it.
647
+ */
648
+ findByUrlScheme(scheme: string): Adapter | undefined;
649
+ /**
650
+ * Same as `findByUrlScheme` but throws a helpful error listing the
651
+ * known schemes when no match is found.
652
+ */
653
+ getByUrlScheme(scheme: string): Adapter;
654
+ }
655
+
656
+ /**
657
+ * Common interface every adapter module exports.
658
+ *
659
+ * Describes how to construct an `Adapter` for a specific data store.
660
+ *
661
+ * Type parameters:
662
+ * TClient: shape of the driver client the factory expects (for
663
+ * example `PostgresClient` for the Postgres adapter)
664
+ * TName: literal name type, exported alongside the factory for
665
+ * type-safe discrimination at call sites
666
+ *
667
+ * Fields:
668
+ * name: literal adapter name (e.g. 'postgres')
669
+ * urlSchemes: URL schemes the adapter handles
670
+ * create: builds an `Adapter<TName>` bound to the given client
671
+ *
672
+ * @example
673
+ * ```ts
674
+ * import { Biref, postgresAdapter } from '@biref/scanner';
675
+ *
676
+ * const biref = Biref.builder()
677
+ * .withAdapter(postgresAdapter.create(client))
678
+ * .build();
679
+ * ```
680
+ */
681
+ interface AdapterFactory<TClient = unknown, TName extends AdapterName = AdapterName> {
682
+ readonly name: TName;
683
+ readonly urlSchemes: readonly string[];
684
+ create(client: TClient): Adapter<TName>;
685
+ }
686
+
687
+ /**
688
+ * Postgres adapter factory.
689
+ *
690
+ * Implements `AdapterFactory<PostgresClient, PostgresAdapterName>`.
691
+ * `create` builds an `Adapter` bound to the user-provided client.
692
+ *
693
+ * @example
694
+ * ```ts
695
+ * import pg from 'pg';
696
+ * import { Biref, postgresAdapter } from '@biref/scanner';
697
+ *
698
+ * const client = new pg.Client({ ... });
699
+ * await client.connect();
700
+ *
701
+ * const biref = Biref.builder()
702
+ * .withAdapter(postgresAdapter.create(client))
703
+ * .build();
704
+ *
705
+ * const model = await biref.scan();
706
+ * ```
707
+ */
708
+ declare const postgresAdapter: AdapterFactory<PostgresClient, PostgresAdapterName>;
709
+ /**
710
+ * Type guard: narrows a generic `Adapter` to an `Adapter<PostgresAdapterName>`.
711
+ *
712
+ * @example
713
+ * ```ts
714
+ * if (isPostgresAdapter(adapter)) {
715
+ * // adapter.name is now typed as 'postgres'
716
+ * }
717
+ * ```
718
+ */
719
+ declare function isPostgresAdapter(adapter: Adapter): adapter is Adapter<PostgresAdapterName>;
720
+
721
+ /**
722
+ * Content of the first-run overrides file scaffold.
723
+ *
724
+ * The CLI only writes this if the target path does not already
725
+ * exist - once the user has started editing it, regenerating the
726
+ * schema never overwrites their work.
727
+ */
728
+ declare function overridesScaffold(): string;
729
+
730
+ /**
731
+ * A single file produced by split-mode codegen.
732
+ *
733
+ * Paths are always relative to the output folder the user passed to
734
+ * the CLI (or to `writeSchemaFiles`) and use forward slashes. The
735
+ * caller is responsible for joining them onto an absolute path and
736
+ * creating any intermediate directories.
737
+ */
738
+ interface SchemaFile {
739
+ readonly path: string;
740
+ readonly content: string;
741
+ }
742
+ /**
743
+ * Emit a TypeScript schema declaration from a scanned `DataModel` as
744
+ * a single self-contained `.ts` file.
745
+ *
746
+ * The output exports:
747
+ *
748
+ * - `RawBirefSchema`: the literal schema shape the model discovered
749
+ * - `BirefSchema`: `RawBirefSchema` with user overrides applied
750
+ *
751
+ * Deterministic: the same `DataModel` input always produces the same
752
+ * string, suitable for snapshot tests and stable git diffs.
753
+ *
754
+ * Use this when you want everything in one file. For larger schemas
755
+ * `generateSchemaFiles` emits one file per entity plus an index and
756
+ * tends to be easier to browse.
757
+ */
758
+ declare function generateSchema(model: DataModel): string;
759
+ /**
760
+ * Emit a folder of split schema files from a scanned `DataModel`.
761
+ *
762
+ * Layout:
763
+ *
764
+ * index.ts Root file re-exporting every per-entity
765
+ * interface and composing `RawBirefSchema`
766
+ * plus `BirefSchema`.
767
+ * <namespace>/<entity>.ts One file per entity with its field
768
+ * descriptor, identifier tuple, and
769
+ * relations map.
770
+ *
771
+ * The overrides file is **not** emitted by this function - it is
772
+ * scaffolded once by the CLI and never touched again.
773
+ *
774
+ * Returns the files in a deterministic order so consumers can write
775
+ * them or snapshot-test them safely.
776
+ */
777
+ declare function generateSchemaFiles(model: DataModel): readonly SchemaFile[];
778
+
779
+ /**
780
+ * Maps a paradigm-neutral `FieldType` to the TypeScript type literal
781
+ * string emitted into the codegen-generated schema.
782
+ *
783
+ * JSON / JSONB categories map to `unknown` (users replace them via
784
+ * the sibling overrides file). Enum categories with known values map
785
+ * to a union of string literals. Array categories recurse on the
786
+ * element type. Everything else follows the fixed table described in
787
+ * the README.
788
+ */
789
+ declare function tsTypeFor(type: FieldType, nullable: boolean): string;
790
+
791
+ /**
792
+ * A node in the typed query builder's plan tree.
793
+ *
794
+ * Each node wraps a single-entity `QuerySpec` (executed against the
795
+ * adapter's engine) plus any child includes to resolve recursively.
796
+ * `QueryPlanExecutor` walks this tree to execute and stitch.
797
+ */
798
+ interface QueryPlan {
799
+ readonly spec: QuerySpec;
800
+ readonly includes: readonly QueryInclude[];
801
+ }
802
+ /**
803
+ * A nested include on a parent plan.
804
+ *
805
+ * `relationName` is the name the user wrote in `.include('orders', ...)`
806
+ * on the builder (either a friendly name computed from the model or,
807
+ * on collisions, the underlying foreign key constraint name).
808
+ *
809
+ * `direction` mirrors `Relationship.direction`. `parentKeys` are the
810
+ * columns on the parent entity used to filter children; `childKeys`
811
+ * are their counterparts on the child entity. For outbound includes
812
+ * (FK → target PK) the parent holds the FK columns; for inbound the
813
+ * parent holds the PK columns.
814
+ *
815
+ * `cardinality` drives how the executor stitches children into the
816
+ * parent record: `'one'` picks the first match, `'many'` returns the
817
+ * full array.
818
+ */
819
+ interface QueryInclude {
820
+ readonly relationName: string;
821
+ readonly plan: QueryPlan;
822
+ readonly direction: 'inbound' | 'outbound';
823
+ readonly parentKeys: readonly string[];
824
+ readonly childKeys: readonly string[];
825
+ readonly cardinality: 'one' | 'many';
826
+ }
827
+
828
+ /**
829
+ * Executes a `QueryPlan` tree against an adapter's engine, runner, and
830
+ * parser, stitching include results into nested shapes on the parent.
831
+ *
832
+ * Runs one query per plan node - root first, then one query per
833
+ * include level, filtered to the parent keys it just collected. This
834
+ * matches Prisma's approach: sequential sub-queries with in-process
835
+ * hydration, rather than SQL JOINs. It keeps the engine layer
836
+ * paradigm-neutral and works without any JOIN support in the
837
+ * adapter's `QueryEngine`.
838
+ *
839
+ * Projection honoring: join-key columns are transparently added to
840
+ * both the parent's and each child's SELECT so the stitcher can read
841
+ * them, then removed from the final hydrated records so the caller
842
+ * sees only the columns they originally asked for.
843
+ */
844
+ declare class QueryPlanExecutor {
845
+ private readonly engine;
846
+ private readonly runner;
847
+ private readonly parser;
848
+ constructor(engine: QueryEngine, runner: RawQueryRunner, parser: RecordParser);
849
+ execute(plan: QueryPlan, model: DataModel): Promise<readonly ParsedRecord[]>;
850
+ private attachIncludes;
851
+ private static prepareChildPlan;
852
+ private static injectFields;
853
+ private static collectParentKeys;
854
+ /**
855
+ * Compute the set of keys a hydrated record should expose to the
856
+ * caller: the user's selected fields (or every entity field when
857
+ * no select was given) plus every nested include's relation name
858
+ * so attached children survive the final filter step.
859
+ */
860
+ private static resolveExposedKeys;
861
+ /**
862
+ * Resolve the exposed-keys set for an include's nested plan.
863
+ *
864
+ * Uses the ORIGINAL include.plan (not the mutated child plan we
865
+ * handed to the executor) so injected join keys do not leak into
866
+ * the final output.
867
+ */
868
+ private static resolveChildExposedKeys;
869
+ private static project;
870
+ private static indexByKey;
871
+ private static buildKey;
872
+ private static filterKeys;
873
+ }
874
+
875
+ /**
876
+ * Runtime context threaded through every `ChainBuilder` instance.
877
+ *
878
+ * Holds the scanned `DataModel` used for validation and the
879
+ * `QueryPlanExecutor` that actually runs terminal methods like
880
+ * `findMany` / `findFirst`.
881
+ */
882
+ interface ChainBuilderContext {
883
+ readonly model: DataModel;
884
+ readonly executor: QueryPlanExecutor;
885
+ }
886
+ /**
887
+ * Immutable fluent builder that accumulates a `QueryPlan`.
888
+ *
889
+ * Each mutating method (`select`, `where`, `orderBy`, `limit`,
890
+ * `offset`, `include`) returns a **new** `ChainBuilder` - chains are
891
+ * safe to fork.
892
+ *
893
+ * Field and relation names are validated against the `DataModel` at
894
+ * call time so mistakes throw immediately with a clear message.
895
+ * Compile-time typing comes from the generic parameters (see
896
+ * `src/query/types/*`); the runtime implementation here is
897
+ * schema-agnostic.
898
+ */
899
+ declare class ChainBuilder {
900
+ private readonly ctx;
901
+ private readonly plan;
902
+ constructor(ctx: ChainBuilderContext, plan: QueryPlan);
903
+ /**
904
+ * Narrow the projection to a specific set of fields.
905
+ *
906
+ * .select('id', 'email') -- only these two columns
907
+ * .select('*') -- every field of the entity
908
+ * .select() -- equivalent to '*', kept for ergonomic parity
909
+ *
910
+ * Unknown field names throw immediately with a clear error; the
911
+ * wildcard sentinel skips validation and leaves the plan's `select`
912
+ * undefined so the engine emits every column the adapter reported.
913
+ */
914
+ select(...fields: readonly string[]): ChainBuilder;
915
+ where(field: string, operator: FilterOperator, value?: unknown): ChainBuilder;
916
+ orderBy(field: string, direction?: 'asc' | 'desc'): ChainBuilder;
917
+ limit(count: number): ChainBuilder;
918
+ offset(count: number): ChainBuilder;
919
+ /**
920
+ * Attach a nested include to the plan.
921
+ *
922
+ * Three call shapes are supported:
923
+ *
924
+ * .include('orders') -- all fields, no nested
925
+ * .include('orders', (q) => q.select('id', 'total')) -- narrow the child
926
+ * .include('*') -- every relation on this entity
927
+ *
928
+ * The callback is optional. When omitted, the child plan uses the
929
+ * default projection (all fields of the related entity) and no
930
+ * further includes. Pass a callback when you want to narrow the
931
+ * projection or chain additional nested includes.
932
+ *
933
+ * The wildcard `'*'` expands to one include per relation discovered
934
+ * on the current entity. Each expanded include uses the default
935
+ * projection. Combining `'*'` with a callback is not supported -
936
+ * the sub-builder's shape differs per relation, so a single
937
+ * callback cannot narrow all of them coherently.
938
+ */
939
+ include(relationName: string, build?: (q: ChainBuilder) => ChainBuilder): ChainBuilder;
940
+ findMany(): Promise<readonly ParsedRecord[]>;
941
+ findFirst(): Promise<ParsedRecord | null>;
942
+ /**
943
+ * Escape hatch for advanced callers (tests, tooling): exposes the
944
+ * accumulated plan without executing it. The typed builder's public
945
+ * surface does not reference this directly.
946
+ */
947
+ toPlan(): QueryPlan;
948
+ private requireEntity;
949
+ }
950
+
951
+ /**
952
+ * Compile-time descriptor for a single field in a codegen schema.
953
+ *
954
+ * `ts` is the TypeScript type the field's values will be hydrated as
955
+ * (e.g. `string`, `number`, `Date`, or a user-provided override type).
956
+ * `nullable` and `isIdentifier` mirror the runtime `Field`. `category`
957
+ * carries the original `FieldTypeCategory` so the typed builder can
958
+ * pick the allowed `where` operator set for this field.
959
+ */
960
+ interface SchemaFieldDescriptor<TS = unknown, Nullable extends boolean = boolean, Identifier extends boolean = boolean, Category extends FieldTypeCategory = FieldTypeCategory> {
961
+ readonly ts: TS;
962
+ readonly nullable: Nullable;
963
+ readonly isIdentifier: Identifier;
964
+ readonly category: Category;
965
+ }
966
+ /**
967
+ * Compile-time descriptor for a single relation in a codegen schema.
968
+ *
969
+ * `target` is a qualified entity name `'namespace.entity'`. The typed
970
+ * builder splits on the dot to resolve the target entity at compile
971
+ * time.
972
+ */
973
+ interface SchemaRelationDescriptor<Direction extends 'inbound' | 'outbound' = 'inbound' | 'outbound', Target extends `${string}.${string}` = `${string}.${string}`, FromFields extends readonly string[] = readonly string[], ToFields extends readonly string[] = readonly string[], Cardinality extends 'one' | 'many' = 'one' | 'many'> {
974
+ readonly direction: Direction;
975
+ readonly target: Target;
976
+ readonly fromFields: FromFields;
977
+ readonly toFields: ToFields;
978
+ readonly cardinality: Cardinality;
979
+ }
980
+ /**
981
+ * Compile-time descriptor for one entity: the fields map, the
982
+ * identifier tuple, and the relations map.
983
+ */
984
+ interface SchemaEntityDescriptor {
985
+ readonly fields: {
986
+ readonly [field: string]: SchemaFieldDescriptor;
987
+ };
988
+ readonly identifier: readonly string[];
989
+ readonly relations: {
990
+ readonly [relation: string]: SchemaRelationDescriptor;
991
+ };
992
+ }
993
+ /**
994
+ * Compile-time descriptor for a namespace.
995
+ */
996
+ type SchemaNamespaceDescriptor = {
997
+ readonly [entity: string]: SchemaEntityDescriptor;
998
+ };
999
+ /**
1000
+ * Loose structural constraint for schema types passed to
1001
+ * `biref.query<Schema>()`.
1002
+ *
1003
+ * A concrete interface with literal namespace / entity / field keys
1004
+ * satisfies `object`, so users can hand a codegen-generated
1005
+ * `BirefSchema` directly without opting into a string index
1006
+ * signature. The actual narrowing (field map, relation target, etc.)
1007
+ * is handled by the type-level helpers in `src/query/types/*`, which
1008
+ * match via conditional types and fall back to `never` when the
1009
+ * schema does not line up.
1010
+ */
1011
+ type BirefSchemaShape = object;
1012
+
1013
+ /**
1014
+ * Split a `'namespace.entity'` string literal on the dot.
1015
+ *
1016
+ * Used to resolve the target entity of a relation at compile time from
1017
+ * its qualified `target` string. The result is a tuple `[Ns, E]`.
1018
+ */
1019
+ type Split<T extends string, Sep extends string> = T extends `${infer L}${Sep}${infer R}` ? [L, R] : never;
1020
+ /**
1021
+ * Relations map of a given entity - indexable by relation name.
1022
+ */
1023
+ type RelationsOfEntity<S extends BirefSchemaShape, Ns extends keyof S, E extends keyof S[Ns]> = S[Ns][E] extends {
1024
+ readonly relations: infer R;
1025
+ } ? R : never;
1026
+ /**
1027
+ * The descriptor for a specific relation on a specific entity.
1028
+ */
1029
+ type RelationDescriptor<S extends BirefSchemaShape, Ns extends keyof S, E extends keyof S[Ns], R extends keyof RelationsOfEntity<S, Ns, E>> = RelationsOfEntity<S, Ns, E>[R];
1030
+ /**
1031
+ * Extract the target namespace literal from a relation descriptor's
1032
+ * `target: 'ns.entity'` field.
1033
+ */
1034
+ type TargetNs<S extends BirefSchemaShape, Ns extends keyof S, E extends keyof S[Ns], R extends keyof RelationsOfEntity<S, Ns, E>> = RelationDescriptor<S, Ns, E, R> extends SchemaRelationDescriptor<infer _Dir, infer Target, infer _From, infer _To, infer _Card> ? Split<Target, '.'>[0] extends keyof S ? Split<Target, '.'>[0] : never : never;
1035
+ /**
1036
+ * Extract the target entity literal from a relation descriptor.
1037
+ */
1038
+ type TargetE<S extends BirefSchemaShape, Ns extends keyof S, E extends keyof S[Ns], R extends keyof RelationsOfEntity<S, Ns, E>> = RelationDescriptor<S, Ns, E, R> extends SchemaRelationDescriptor<infer _Dir, infer Target, infer _From, infer _To, infer _Card> ? TargetNs<S, Ns, E, R> extends keyof S ? Split<Target, '.'>[1] extends keyof S[TargetNs<S, Ns, E, R>] ? Split<Target, '.'>[1] : never : never : never;
1039
+ /**
1040
+ * Extract the cardinality (`'one' | 'many'`) from a relation
1041
+ * descriptor, used to decide whether an include field wraps its
1042
+ * result in an array.
1043
+ */
1044
+ type CardinalityOf<S extends BirefSchemaShape, Ns extends keyof S, E extends keyof S[Ns], R extends keyof RelationsOfEntity<S, Ns, E>> = RelationDescriptor<S, Ns, E, R> extends SchemaRelationDescriptor<infer _Dir, infer _Target, infer _From, infer _To, infer Card> ? Card : never;
1045
+
1046
+ /**
1047
+ * Compile-time helpers that project row shapes out of a codegen schema.
1048
+ *
1049
+ * These types accept an arbitrary `Schema` (which is usually the
1050
+ * `BirefSchema` generated by codegen) plus a namespace / entity / field
1051
+ * path, and produce the shapes the typed builder needs: field maps,
1052
+ * per-field TS types (with nullability applied), categories for
1053
+ * operator selection, default and narrowed row shapes, and the final
1054
+ * hydrated row that merges projected columns with nested include
1055
+ * results.
1056
+ */
1057
+ type FieldsOf<S extends BirefSchemaShape, Ns extends keyof S, E extends keyof S[Ns]> = S[Ns][E] extends {
1058
+ readonly fields: infer F;
1059
+ } ? F : never;
1060
+ type RelationsOf<S extends BirefSchemaShape, Ns extends keyof S, E extends keyof S[Ns]> = S[Ns][E] extends {
1061
+ readonly relations: infer R;
1062
+ } ? R : never;
1063
+ type CategoryOf<S extends BirefSchemaShape, Ns extends keyof S, E extends keyof S[Ns], F extends keyof FieldsOf<S, Ns, E>> = FieldsOf<S, Ns, E>[F] extends SchemaFieldDescriptor<infer _TS, infer _Null, infer _Ident, infer Category> ? Category : never;
1064
+ type TypeOf<S extends BirefSchemaShape, Ns extends keyof S, E extends keyof S[Ns], F extends keyof FieldsOf<S, Ns, E>> = FieldsOf<S, Ns, E>[F] extends SchemaFieldDescriptor<infer TS, infer Nullable, infer _Ident, infer _Category> ? Nullable extends true ? TS | null : TS : never;
1065
+ /**
1066
+ * Selection shape: a map from selected field name to its TS type,
1067
+ * with nullability applied. Produced by `select(...)`.
1068
+ */
1069
+ type Selection = {
1070
+ readonly [field: string]: unknown;
1071
+ };
1072
+ /**
1073
+ * The default selection when `select()` is not called: every field on
1074
+ * the entity, keyed by its declared name.
1075
+ */
1076
+ type DefaultSelect<S extends BirefSchemaShape, Ns extends keyof S, E extends keyof S[Ns]> = {
1077
+ readonly [F in keyof FieldsOf<S, Ns, E>]: TypeOf<S, Ns, E, F>;
1078
+ };
1079
+ /**
1080
+ * Narrowed selection from a `select('id', 'email')` call.
1081
+ */
1082
+ type PickSelect<S extends BirefSchemaShape, Ns extends keyof S, E extends keyof S[Ns], Keys extends readonly (keyof FieldsOf<S, Ns, E>)[]> = {
1083
+ readonly [K in Keys[number]]: TypeOf<S, Ns, E, K>;
1084
+ };
1085
+ /**
1086
+ * An accumulated include map on a chain builder. Each key is a
1087
+ * relation name; each value is the resolved nested row type.
1088
+ */
1089
+ type IncludeMap = {
1090
+ readonly [relation: string]: unknown;
1091
+ };
1092
+ /**
1093
+ * The final hydrated row: a merge of the projected selection plus all
1094
+ * nested include results. Each include key maps to either a single
1095
+ * nested row (`cardinality: 'one'`) or an array (`cardinality:
1096
+ * 'many'`).
1097
+ */
1098
+ type HydratedRow<Sel extends Selection, Inc extends IncludeMap> = {
1099
+ readonly [K in keyof Sel]: Sel[K];
1100
+ } & {
1101
+ readonly [K in keyof Inc]: Inc[K];
1102
+ };
1103
+
1104
+ /**
1105
+ * Operators valid for each `FieldTypeCategory`.
1106
+ *
1107
+ * Equality + nullability operators are always valid. Comparable
1108
+ * categories (integer, decimal, date, timestamp, time) additionally
1109
+ * support ordered comparisons and `between`. String-like categories
1110
+ * support `like` / `ilike`. Arrays of the same element type support
1111
+ * `in` / `not-in`.
1112
+ *
1113
+ * This type is consumed by the typed builder's `where` signature so
1114
+ * the operator argument narrows by the field's category. Accepts an
1115
+ * unconstrained `Category` so callers can pass the output of
1116
+ * `CategoryOfField<...>` without having to widen it manually.
1117
+ */
1118
+ type OpsFor<Category> = Category extends 'string' | 'uuid' | 'enum' ? 'eq' | 'neq' | 'in' | 'not-in' | 'like' | 'ilike' | 'is-null' | 'is-not-null' : Category extends 'integer' | 'decimal' | 'date' | 'timestamp' | 'time' ? 'eq' | 'neq' | 'lt' | 'lte' | 'gt' | 'gte' | 'between' | 'in' | 'not-in' | 'is-null' | 'is-not-null' : Category extends 'boolean' ? 'eq' | 'neq' | 'is-null' | 'is-not-null' : 'eq' | 'neq' | 'is-null' | 'is-not-null';
1119
+ /**
1120
+ * Value shape required for a given operator and a given field TS
1121
+ * type:
1122
+ *
1123
+ * `in` / `not-in` → readonly T[]
1124
+ * `between` → readonly [T, T]
1125
+ * `is-null` / `is-not-null` → value is not supplied (the where
1126
+ * overload drops the argument)
1127
+ * everything else → T
1128
+ */
1129
+ type ValueFor<Op extends string, T> = Op extends 'in' | 'not-in' ? readonly T[] : Op extends 'between' ? readonly [T, T] : T;
1130
+ /**
1131
+ * Operators that take no value argument.
1132
+ */
1133
+ type NullaryOps = 'is-null' | 'is-not-null';
1134
+
1135
+ /**
1136
+ * Type-narrowed fluent interface for the typed query builder.
1137
+ *
1138
+ * Mirrors the runtime `ChainBuilder` class method-for-method but
1139
+ * layers compile-time narrowing on top: `select(...)` narrows the row
1140
+ * shape, `include(...)` appends nested include results, `where(...)`
1141
+ * only accepts operators valid for the field's category. The runtime
1142
+ * `ChainBuilder` instance is cast to this interface by
1143
+ * `NamespaceProxy`.
1144
+ *
1145
+ * Generic parameters:
1146
+ *
1147
+ * S - the user's schema (usually codegen-emitted `BirefSchema`)
1148
+ * Ns - current namespace key
1149
+ * E - current entity key
1150
+ * Sel - accumulated projection (starts as `DefaultSelect`)
1151
+ * Inc - accumulated include map
1152
+ */
1153
+ interface TypedChain<S extends BirefSchemaShape, Ns extends keyof S, E extends keyof S[Ns], Sel extends Selection = DefaultSelect<S, Ns, E>, Inc extends IncludeMap = Record<string, never>> {
1154
+ /**
1155
+ * Narrow the projection to a specific set of field names. Each name
1156
+ * must exist on the entity; unknowns throw at chain time.
1157
+ */
1158
+ select<const K extends readonly (keyof FieldsOf<S, Ns, E>)[]>(...fields: K): TypedChain<S, Ns, E, PickSelect<S, Ns, E, K>, Inc>;
1159
+ /**
1160
+ * Explicit wildcard: every field of the entity, same shape as
1161
+ * calling `findMany()` without `select` at all. Kept as a
1162
+ * discoverable sentinel so `.select('*')` reads clearly in code.
1163
+ */
1164
+ select(wildcard: '*'): TypedChain<S, Ns, E, DefaultSelect<S, Ns, E>, Inc>;
1165
+ /**
1166
+ * Zero-argument shorthand for the wildcard.
1167
+ */
1168
+ select(): TypedChain<S, Ns, E, DefaultSelect<S, Ns, E>, Inc>;
1169
+ where<F extends keyof FieldsOf<S, Ns, E>, Op extends Exclude<OpsFor<CategoryOfField<S, Ns, E, F>>, NullaryOps>>(field: F, operator: Op, value: ValueFor<Op, TypeOf<S, Ns, E, F>>): TypedChain<S, Ns, E, Sel, Inc>;
1170
+ where<F extends keyof FieldsOf<S, Ns, E>>(field: F, operator: Extract<OpsFor<CategoryOfField<S, Ns, E, F>>, NullaryOps>): TypedChain<S, Ns, E, Sel, Inc>;
1171
+ orderBy<F extends keyof FieldsOf<S, Ns, E>>(field: F, direction?: 'asc' | 'desc'): TypedChain<S, Ns, E, Sel, Inc>;
1172
+ limit(count: number): TypedChain<S, Ns, E, Sel, Inc>;
1173
+ offset(count: number): TypedChain<S, Ns, E, Sel, Inc>;
1174
+ /**
1175
+ * Wildcard include: expand every relation discovered on the
1176
+ * current entity, each with the default projection. No callback
1177
+ * is accepted - the sub-builder shape varies per relation, so a
1178
+ * single callback cannot narrow all of them coherently. Use
1179
+ * named includes for per-relation narrowing.
1180
+ *
1181
+ * .include('*') // every inbound and outbound relation, flat
1182
+ *
1183
+ * Listed first so TypeScript's overload resolver picks it before
1184
+ * the generic single-arg form for the literal `'*'` argument.
1185
+ */
1186
+ include(relation: '*'): TypedChain<S, Ns, E, Sel, Inc & {
1187
+ readonly [K in keyof RelationsOfEntity<S, Ns, E>]: WrapForCardinality<CardinalityOf<S, Ns, E, K>, HydratedRow<DefaultSelect<S, TargetNs<S, Ns, E, K>, TargetE<S, Ns, E, K>>, Record<string, never>>>;
1188
+ }>;
1189
+ /**
1190
+ * Shorthand include: attach the relation with default projection
1191
+ * (every field of the related entity, no nested includes).
1192
+ *
1193
+ * .include('orders') // every column of each order, flat
1194
+ *
1195
+ * For narrowing or nesting, use the two-argument overload below.
1196
+ */
1197
+ include<R extends keyof RelationsOfEntity<S, Ns, E>>(relation: R): TypedChain<S, Ns, E, Sel, Inc & {
1198
+ readonly [K in R]: WrapForCardinality<CardinalityOf<S, Ns, E, R>, HydratedRow<DefaultSelect<S, TargetNs<S, Ns, E, R>, TargetE<S, Ns, E, R>>, Record<string, never>>>;
1199
+ }>;
1200
+ /**
1201
+ * Full-control include: the callback receives a zero-state
1202
+ * sub-builder and returns one with the shape you want hydrated
1203
+ * for this include. Supports nested `.include(...)` calls to any
1204
+ * depth, constrained by TypeScript recursion depth.
1205
+ */
1206
+ include<R extends keyof RelationsOfEntity<S, Ns, E>, SubSel extends Selection, SubInc extends IncludeMap>(relation: R, build: (q: TypedChain<S, TargetNs<S, Ns, E, R>, TargetE<S, Ns, E, R>, DefaultSelect<S, TargetNs<S, Ns, E, R>, TargetE<S, Ns, E, R>>, Record<string, never>>) => TypedChain<S, TargetNs<S, Ns, E, R>, TargetE<S, Ns, E, R>, SubSel, SubInc>): TypedChain<S, Ns, E, Sel, Inc & {
1207
+ readonly [K in R]: WrapForCardinality<CardinalityOf<S, Ns, E, R>, HydratedRow<SubSel, SubInc>>;
1208
+ }>;
1209
+ findMany(): Promise<readonly HydratedRow<Sel, Inc>[]>;
1210
+ findFirst(): Promise<HydratedRow<Sel, Inc> | null>;
1211
+ toPlan(): QueryPlan;
1212
+ }
1213
+ /**
1214
+ * Wraps a hydrated nested row in an array for `cardinality: 'many'`
1215
+ * relations, or yields `T | null` for `cardinality: 'one'`.
1216
+ */
1217
+ type WrapForCardinality<Card, T> = Card extends 'many' ? readonly T[] : T | null;
1218
+ /**
1219
+ * Shortcut for extracting the category of a field whose key type is
1220
+ * unioned, used inside the `where` overloads above.
1221
+ */
1222
+ type CategoryOfField<S extends BirefSchemaShape, Ns extends keyof S, E extends keyof S[Ns], F extends keyof FieldsOf<S, Ns, E>> = FieldsOf<S, Ns, E>[F] extends {
1223
+ readonly category: infer C;
1224
+ } ? C : never;
1225
+ /**
1226
+ * Two-layer typed root returned from `biref.query<Schema>(model)`.
1227
+ *
1228
+ * Top level keyed by namespace, inner level keyed by entity. Each
1229
+ * leaf is a zero-state `TypedChain` bound to that namespace/entity.
1230
+ */
1231
+ type TypedQueryRoot<S extends BirefSchemaShape> = {
1232
+ readonly [Ns in keyof S]: {
1233
+ readonly [E in keyof S[Ns]]: TypedChain<S, Ns, E>;
1234
+ };
1235
+ };
1236
+
1237
+ /**
1238
+ * Top-level facade for the SDK.
1239
+ *
1240
+ * Wraps a configured `AdapterRegistry` and exposes scanning and the
1241
+ * typed query builder. The API is ergonomic for the common case of a
1242
+ * single registered adapter and still supports multi-adapter
1243
+ * scenarios via explicit names or URL scheme detection.
1244
+ *
1245
+ * @example
1246
+ * ```ts
1247
+ * const biref = Biref.builder()
1248
+ * .withAdapter(somePostgresAdapter)
1249
+ * .build();
1250
+ * const model = await biref.scan();
1251
+ * const rows = await biref.query(model).public.users.findMany();
1252
+ * ```
1253
+ */
1254
+ declare class Biref {
1255
+ private readonly registry;
1256
+ constructor(registry: AdapterRegistry);
1257
+ /** Entry point for the fluent builder. */
1258
+ static builder(): BirefBuilder;
1259
+ /**
1260
+ * Introspect a data store. With a single registered adapter, no
1261
+ * arguments are needed. Pass `IntrospectOptions` to customize the
1262
+ * scan, or an adapter name when more than one adapter is registered.
1263
+ */
1264
+ scan(): Promise<DataModel>;
1265
+ scan(options: IntrospectOptions): Promise<DataModel>;
1266
+ scan(adapterName: AdapterName, options?: IntrospectOptions): Promise<DataModel>;
1267
+ /**
1268
+ * Introspect a data store using whichever registered adapter handles
1269
+ * the URL's scheme. The URL is only used to pick the adapter; the
1270
+ * actual connection is still owned by the client the user passed to
1271
+ * the adapter at construction time.
1272
+ */
1273
+ scanByUrl(url: string, options?: IntrospectOptions): Promise<DataModel>;
1274
+ /**
1275
+ * Typed query entry point. Returns a namespace-level Proxy over the
1276
+ * given `DataModel`:
1277
+ *
1278
+ * biref.query(model).public.users
1279
+ * .select('id', 'email')
1280
+ * .where('active', 'eq', true)
1281
+ * .include('orders', (q) => q.select('id', 'total'))
1282
+ * .findMany();
1283
+ *
1284
+ * The Proxy layers (`namespace` → `entity`) enumerate what's
1285
+ * actually in the scanned model, so `Object.keys(biref.query(model))`
1286
+ * returns the discovered namespaces. Access to a non-existent
1287
+ * namespace or entity returns `undefined`; the chain methods throw
1288
+ * with a clear error when given unknown fields or relations.
1289
+ *
1290
+ * With a single registered adapter, the adapter is picked
1291
+ * automatically. Pass `adapterName` explicitly to target a specific
1292
+ * adapter when more than one is registered.
1293
+ */
1294
+ query<Schema extends BirefSchemaShape = never>(model: DataModel, adapterName?: AdapterName): [Schema] extends [never] ? UntypedQueryRoot : TypedQueryRoot<Schema>;
1295
+ /** Direct access to the underlying registry. */
1296
+ get adapters(): AdapterRegistry;
1297
+ private executorFor;
1298
+ private resolveScanArgs;
1299
+ private requireSingleAdapter;
1300
+ }
1301
+ /**
1302
+ * Fallback return type for `biref.query(model)` when called without a
1303
+ * schema generic. Degrades to a two-layer record of dynamic
1304
+ * `ChainBuilder`s so chains still work at runtime - callers just
1305
+ * don't get the narrowed compile-time shapes.
1306
+ */
1307
+ type UntypedQueryRoot = Readonly<Record<string, Readonly<Record<string, ChainBuilder>>>>;
1308
+ /**
1309
+ * Fluent builder for `Biref`. Collects adapters and produces a wired
1310
+ * `Biref` facade. Call `build()` once and discard the builder.
1311
+ */
1312
+ declare class BirefBuilder {
1313
+ private readonly pending;
1314
+ /**
1315
+ * Register an adapter. The adapter's `name` is used as the lookup
1316
+ * key when calling `Biref.scan(name)`. Its optional `urlSchemes` are
1317
+ * used by `Biref.scanByUrl(url)`.
1318
+ */
1319
+ withAdapter(adapter: Adapter): this;
1320
+ /** Convenience for registering multiple adapters in one call. */
1321
+ withAdapters(...adapters: readonly Adapter[]): this;
1322
+ /**
1323
+ * Materialize the builder into a `Biref` facade. Throws if any
1324
+ * adapter has a duplicate name.
1325
+ */
1326
+ build(): Biref;
1327
+ }
1328
+
1329
+ /**
1330
+ * Public facade for introspection.
1331
+ *
1332
+ * Takes an `AdapterRegistry`, looks up an adapter by name, and runs
1333
+ * its introspector to produce a `DataModel`. Used for low-level
1334
+ * scenarios where `Biref` is too high-level; most consumers should
1335
+ * prefer `Biref` instead.
1336
+ *
1337
+ * @example
1338
+ * ```ts
1339
+ * const registry = new AdapterRegistry();
1340
+ * registry.register(somePostgresAdapter);
1341
+ * const scanner = new Scanner(registry);
1342
+ * const model = await scanner.scan('postgres');
1343
+ * ```
1344
+ */
1345
+ declare class Scanner {
1346
+ private readonly registry;
1347
+ constructor(registry: AdapterRegistry);
1348
+ /**
1349
+ * Scan a data store using the named adapter and return a paradigm-
1350
+ * neutral `DataModel`.
1351
+ */
1352
+ scan(adapterName: string, options?: IntrospectOptions): Promise<DataModel>;
1353
+ }
1354
+
1355
+ /**
1356
+ * Port: turns a stream of `ParsedRecord`s into a representation suitable
1357
+ * for transport, storage, or display.
1358
+ *
1359
+ * `TOutput` is generic so different formatters can produce different
1360
+ * shapes:
1361
+ * - `Formatter<string>`: JSON, CSV, NDJSON, etc.
1362
+ * - `Formatter<readonly ParsedRecord[]>`: pass-through (raw)
1363
+ * - `Formatter<Uint8Array>`: binary formats (e.g. parquet)
1364
+ *
1365
+ * Formatters are stateless and pure: the same input always produces the
1366
+ * same output. They do not depend on any database driver.
1367
+ */
1368
+ interface Formatter<TOutput = string> {
1369
+ /** Stable format identifier (e.g. 'json', 'csv', 'raw'). */
1370
+ readonly format: string;
1371
+ /** MIME type, for HTTP responses or file naming. */
1372
+ readonly contentType: string;
1373
+ /** Serialize a stream of records into the formatter's target shape. */
1374
+ serialize(records: readonly ParsedRecord[], options?: SerializeOptions): TOutput;
1375
+ }
1376
+ interface SerializeOptions {
1377
+ /**
1378
+ * Explicit field order for formatters where column order matters
1379
+ * (CSV, fixed-width). If omitted, the formatter derives order from
1380
+ * the keys of the first record.
1381
+ */
1382
+ readonly fields?: readonly string[];
1383
+ }
1384
+
1385
+ interface CsvFormatterOptions {
1386
+ /** Delimiter character. Defaults to ','. */
1387
+ readonly delimiter?: string;
1388
+ /** Whether to emit a header row. Defaults to true. */
1389
+ readonly includeHeader?: boolean;
1390
+ /** Line terminator. Defaults to '\n'. */
1391
+ readonly lineTerminator?: string;
1392
+ }
1393
+ /**
1394
+ * Serializes parsed records to a CSV string.
1395
+ *
1396
+ * Field order:
1397
+ * 1. `options.fields` from the call site, if provided
1398
+ * 2. The keys of the first record, otherwise
1399
+ * 3. Empty string when there are no records
1400
+ *
1401
+ * Escaping follows RFC 4180: cells containing the delimiter, double
1402
+ * quotes, or newlines are wrapped in double quotes, with internal double
1403
+ * quotes doubled.
1404
+ *
1405
+ * Non-string cell values are coerced as follows:
1406
+ * - `Date` → ISO 8601 string
1407
+ * - `bigint` → decimal string
1408
+ * - object/array → JSON-encoded string
1409
+ * - `null` / `undefined` → empty cell
1410
+ */
1411
+ declare class CsvFormatter implements Formatter<string> {
1412
+ private readonly options;
1413
+ readonly format = "csv";
1414
+ readonly contentType = "text/csv";
1415
+ constructor(options?: CsvFormatterOptions);
1416
+ serialize(records: readonly ParsedRecord[], options?: SerializeOptions): string;
1417
+ private resolveFields;
1418
+ private serializeCell;
1419
+ private escape;
1420
+ }
1421
+
1422
+ interface JsonFormatterOptions {
1423
+ /** Indent the output with 2 spaces if true. Defaults to false. */
1424
+ readonly pretty?: boolean;
1425
+ }
1426
+ /**
1427
+ * Serializes parsed records to a JSON array string.
1428
+ *
1429
+ * Handles values that `JSON.stringify` does not natively support:
1430
+ * - `Date` → ISO 8601 string
1431
+ * - `bigint` → decimal string
1432
+ */
1433
+ declare class JsonFormatter implements Formatter<string> {
1434
+ private readonly options;
1435
+ readonly format = "json";
1436
+ readonly contentType = "application/json";
1437
+ constructor(options?: JsonFormatterOptions);
1438
+ serialize(records: readonly ParsedRecord[]): string;
1439
+ private static replacer;
1440
+ }
1441
+
1442
+ /**
1443
+ * "Raw" formatter: returns the parsed records as-is, without
1444
+ * serialization. Useful when the consumer wants to keep working with
1445
+ * native JS values (e.g. piping into another JS function) instead of
1446
+ * converting to a string format.
1447
+ *
1448
+ * Unlike the other built-in formatters, this one's `TOutput` is the
1449
+ * record array itself rather than a string. The `Formatter` interface
1450
+ * is generic precisely so this case fits without special-casing.
1451
+ */
1452
+ declare class RawFormatter implements Formatter<readonly ParsedRecord[]> {
1453
+ readonly format = "raw";
1454
+ readonly contentType = "application/javascript";
1455
+ serialize(records: readonly ParsedRecord[]): readonly ParsedRecord[];
1456
+ }
1457
+
1458
+ /**
1459
+ * Shape of the sibling `biref.schema.overrides.ts` file.
1460
+ *
1461
+ * Keyed by qualified entity name `'namespace.entity'`, each entry
1462
+ * maps field names to the TS type the user wants for that column.
1463
+ * Typical use: forcing a concrete shape on a jsonb column whose
1464
+ * structure the scanner cannot see.
1465
+ *
1466
+ * @example
1467
+ * ```ts
1468
+ * export interface Overrides {
1469
+ * 'identity.users': {
1470
+ * profile: { plan: 'free' | 'pro'; prefs: { darkMode: boolean } };
1471
+ * };
1472
+ * }
1473
+ * ```
1474
+ */
1475
+ interface BirefOverridesShape {
1476
+ readonly [qualifiedEntity: `${string}.${string}`]: {
1477
+ readonly [field: string]: unknown;
1478
+ };
1479
+ }
1480
+ /**
1481
+ * Deep-merge a user `Overrides` map onto a `RawBirefSchema` produced
1482
+ * by codegen, replacing the `ts` slot on matching fields while
1483
+ * preserving every other descriptor property.
1484
+ *
1485
+ * The override key is the qualified entity name
1486
+ * `'namespace.entity'`, matching the single-level shape of
1487
+ * `BirefOverridesShape`. Entities or fields absent from the overrides
1488
+ * pass through unchanged.
1489
+ */
1490
+ type ApplySchemaOverrides<Raw extends BirefSchemaShape, Overrides extends BirefOverridesShape> = {
1491
+ readonly [Ns in keyof Raw]: {
1492
+ readonly [E in keyof Raw[Ns]]: Raw[Ns][E] extends {
1493
+ readonly fields: infer F;
1494
+ readonly identifier: infer I;
1495
+ readonly relations: infer R;
1496
+ } ? {
1497
+ readonly fields: {
1498
+ readonly [FieldName in keyof F]: `${Ns & string}.${E & string}` extends keyof Overrides ? FieldName extends keyof Overrides[`${Ns & string}.${E & string}`] ? ReplaceFieldTs<F[FieldName], Overrides[`${Ns & string}.${E & string}`][FieldName]> : F[FieldName] : F[FieldName];
1499
+ };
1500
+ readonly identifier: I;
1501
+ readonly relations: R;
1502
+ } : Raw[Ns][E];
1503
+ };
1504
+ };
1505
+ type ReplaceFieldTs<Field, NewTs> = Field extends SchemaFieldDescriptor<infer _OldTs, infer Nullable, infer Identifier, infer Category> ? SchemaFieldDescriptor<NewTs, Nullable, Identifier, Category> : Field;
1506
+
1507
+ export { type Adapter, type AdapterFactory, type AdapterName, AdapterRegistry, type ApplySchemaOverrides, Biref, BirefBuilder, type BirefOverridesShape, type BirefSchemaShape, type BuiltQuery, type BuiltQueryMetadata, type CardinalityOf, type CategoryOf, type CategoryOfField, ChainBuilder, type ChainBuilderContext, type Constraint, type ConstraintKind, CsvFormatter, type CsvFormatterOptions, DataModel, DefaultRecordParser, type DefaultSelect, type EngineKind, type Entity, type EntityRef, type Field, type FieldType, type FieldTypeCategory, type FieldsOf, type Filter, type FilterOperator, type Formatter, type HydratedRow, type IncludeMap, type Index, type IndexKind, type IntrospectOptions, type Introspector, JsonFormatter, type JsonFormatterOptions, type KnownAdapterName, type NullaryOps, type OpsFor, type OrderBy, POSTGRES_ADAPTER_NAME, POSTGRES_URL_SCHEMES, type ParsedRecord, type ParsedValue, type PickSelect, type PostgresAdapterName, type PostgresClient, PostgresIntrospector, PostgresQueryEngine, PostgresRecordParser, type QueryEngine, type QueryInclude, type QueryPlan, QueryPlanExecutor, type QuerySpec, RawFormatter, type RawQueryRunner, type RecordParser, type Reference, type ReferentialAction, type RelationsOf, type RelationsOfEntity, type Relationship, type RelationshipDirection, Scanner, type SchemaEntityDescriptor, type SchemaFieldDescriptor, type SchemaFile, type SchemaNamespaceDescriptor, type SchemaRelationDescriptor, type Selection, type SerializeOptions, type Split, type TargetE, type TargetNs, type TypeOf, type TypedChain, type TypedQueryRoot, type UntypedQueryRoot, type ValueFor, type WrapForCardinality, generateSchema, generateSchemaFiles, isPostgresAdapter, overridesScaffold, postgresAdapter, tsTypeFor };