turbine-orm 0.49.0 → 0.50.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.
Files changed (157) hide show
  1. package/README.md +122 -39
  2. package/dist/cjs/adapters/cockroachdb.d.ts +39 -0
  3. package/dist/cjs/adapters/index.d.ts +110 -0
  4. package/dist/cjs/adapters/yugabytedb.d.ts +51 -0
  5. package/dist/cjs/cli/config.d.ts +181 -0
  6. package/dist/cjs/cli/config.js +32 -6
  7. package/dist/cjs/cli/destructive.d.ts +38 -0
  8. package/dist/cjs/cli/index.d.ts +359 -0
  9. package/dist/cjs/cli/index.js +228 -56
  10. package/dist/cjs/cli/loader.d.ts +61 -0
  11. package/dist/cjs/cli/mcp.d.ts +42 -0
  12. package/dist/cjs/cli/migrate.d.ts +356 -0
  13. package/dist/cjs/cli/migrate.js +131 -40
  14. package/dist/cjs/cli/observe-ui.d.ts +1 -0
  15. package/dist/cjs/cli/observe-ui.js +14 -5
  16. package/dist/cjs/cli/observe.d.ts +25 -0
  17. package/dist/cjs/cli/observe.js +49 -12
  18. package/dist/cjs/cli/pii-tags.d.ts +53 -0
  19. package/dist/cjs/cli/prisma-report.d.ts +33 -0
  20. package/dist/cjs/cli/prisma-report.js +73 -0
  21. package/dist/cjs/cli/prisma-resolve.d.ts +106 -0
  22. package/dist/cjs/cli/prisma-resolve.js +1 -0
  23. package/dist/cjs/cli/prisma-schema.d.ts +176 -0
  24. package/dist/cjs/cli/prisma-schema.js +82 -4
  25. package/dist/cjs/cli/rate-limit.d.ts +32 -0
  26. package/dist/cjs/cli/rate-limit.js +45 -0
  27. package/dist/cjs/cli/studio-demo.d.ts +43 -0
  28. package/dist/cjs/cli/studio-ui.generated.d.ts +1 -0
  29. package/dist/cjs/cli/studio.d.ts +207 -0
  30. package/dist/cjs/cli/studio.js +136 -71
  31. package/dist/cjs/cli/ui.d.ts +73 -0
  32. package/dist/cjs/cli/ui.js +51 -9
  33. package/dist/cjs/client.d.ts +837 -0
  34. package/dist/cjs/client.js +3 -0
  35. package/dist/cjs/dialect.d.ts +516 -0
  36. package/dist/cjs/dialect.js +37 -12
  37. package/dist/cjs/errors.d.ts +370 -0
  38. package/dist/cjs/generate.d.ts +137 -0
  39. package/dist/cjs/generate.js +39 -6
  40. package/dist/cjs/index-advisor.d.ts +153 -0
  41. package/dist/cjs/index-stats.d.ts +384 -0
  42. package/dist/cjs/index.d.ts +55 -0
  43. package/dist/cjs/index.js +7 -2
  44. package/dist/cjs/introspect.d.ts +269 -0
  45. package/dist/cjs/mssql.d.ts +232 -0
  46. package/dist/cjs/mssql.js +6 -0
  47. package/dist/cjs/mysql.d.ts +173 -0
  48. package/dist/cjs/mysql.js +16 -0
  49. package/dist/cjs/nested-write.d.ts +96 -0
  50. package/dist/cjs/nested-write.js +414 -24
  51. package/dist/cjs/observe.d.ts +115 -0
  52. package/dist/cjs/optional-peer-import.d.cts +72 -0
  53. package/dist/cjs/pipeline-submittable.d.ts +93 -0
  54. package/dist/cjs/pipeline.d.ts +71 -0
  55. package/dist/cjs/powdb-introspect.d.ts +84 -0
  56. package/dist/cjs/powdb.d.ts +931 -0
  57. package/dist/cjs/powdb.js +106 -21
  58. package/dist/cjs/powql.d.ts +592 -0
  59. package/dist/cjs/powql.js +42 -6
  60. package/dist/cjs/prisma-compat.d.ts +283 -0
  61. package/dist/cjs/prisma-compat.js +167 -9
  62. package/dist/cjs/query/aggregates.d.ts +92 -0
  63. package/dist/cjs/query/aggregates.js +7 -3
  64. package/dist/cjs/query/batched-loader.d.ts +193 -0
  65. package/dist/cjs/query/builder.d.ts +849 -0
  66. package/dist/cjs/query/builder.js +571 -65
  67. package/dist/cjs/query/compound-unique.d.ts +51 -0
  68. package/dist/cjs/query/deferred.d.ts +223 -0
  69. package/dist/cjs/query/filters.d.ts +201 -0
  70. package/dist/cjs/query/index.d.ts +14 -0
  71. package/dist/cjs/query/index.js +6 -1
  72. package/dist/cjs/query/relations.d.ts +609 -0
  73. package/dist/cjs/query/relations.js +693 -46
  74. package/dist/cjs/query/types.d.ts +1300 -0
  75. package/dist/cjs/query/utils.d.ts +209 -0
  76. package/dist/cjs/query/utils.js +208 -1
  77. package/dist/cjs/query/warn-registry.d.ts +68 -0
  78. package/dist/cjs/query/warn-registry.js +9 -0
  79. package/dist/cjs/query/where-compile.d.ts +139 -0
  80. package/dist/cjs/query/where.d.ts +548 -0
  81. package/dist/cjs/query/where.js +58 -22
  82. package/dist/cjs/query/writes.d.ts +172 -0
  83. package/dist/cjs/query/writes.js +105 -12
  84. package/dist/cjs/realtime.d.ts +70 -0
  85. package/dist/cjs/schema-builder.d.ts +354 -0
  86. package/dist/cjs/schema-metadata.d.ts +83 -0
  87. package/dist/cjs/schema-sql.d.ts +217 -0
  88. package/dist/cjs/schema-sql.js +23 -5
  89. package/dist/cjs/schema.d.ts +356 -0
  90. package/dist/cjs/schema.js +125 -0
  91. package/dist/cjs/seed.d.ts +15 -0
  92. package/dist/cjs/serverless.d.ts +142 -0
  93. package/dist/cjs/sqlite.d.ts +143 -0
  94. package/dist/cjs/sqlite.js +4 -0
  95. package/dist/cjs/typed-sql.d.ts +102 -0
  96. package/dist/cli/config.d.ts +18 -4
  97. package/dist/cli/config.js +31 -6
  98. package/dist/cli/index.d.ts +123 -0
  99. package/dist/cli/index.js +223 -58
  100. package/dist/cli/migrate.d.ts +59 -10
  101. package/dist/cli/migrate.js +128 -41
  102. package/dist/cli/observe-ui.d.ts +1 -1
  103. package/dist/cli/observe-ui.js +14 -5
  104. package/dist/cli/observe.d.ts +7 -1
  105. package/dist/cli/observe.js +48 -12
  106. package/dist/cli/prisma-report.d.ts +14 -0
  107. package/dist/cli/prisma-report.js +72 -0
  108. package/dist/cli/prisma-resolve.d.ts +6 -0
  109. package/dist/cli/prisma-resolve.js +1 -0
  110. package/dist/cli/prisma-schema.d.ts +62 -2
  111. package/dist/cli/prisma-schema.js +81 -4
  112. package/dist/cli/rate-limit.d.ts +32 -0
  113. package/dist/cli/rate-limit.js +40 -0
  114. package/dist/cli/studio.d.ts +5 -5
  115. package/dist/cli/studio.js +135 -70
  116. package/dist/cli/ui.d.ts +1 -1
  117. package/dist/cli/ui.js +51 -9
  118. package/dist/client.d.ts +40 -0
  119. package/dist/client.js +3 -0
  120. package/dist/dialect.d.ts +17 -1
  121. package/dist/dialect.js +37 -12
  122. package/dist/generate.js +40 -7
  123. package/dist/index.d.ts +1 -1
  124. package/dist/index.js +1 -1
  125. package/dist/mssql.js +6 -0
  126. package/dist/mysql.js +16 -0
  127. package/dist/nested-write.d.ts +2 -0
  128. package/dist/nested-write.js +415 -25
  129. package/dist/powdb.d.ts +4 -2
  130. package/dist/powdb.js +106 -21
  131. package/dist/powql.d.ts +5 -0
  132. package/dist/powql.js +42 -6
  133. package/dist/prisma-compat.d.ts +2 -0
  134. package/dist/prisma-compat.js +166 -8
  135. package/dist/query/aggregates.js +7 -3
  136. package/dist/query/builder.d.ts +292 -21
  137. package/dist/query/builder.js +570 -64
  138. package/dist/query/deferred.d.ts +39 -0
  139. package/dist/query/index.d.ts +1 -1
  140. package/dist/query/index.js +1 -1
  141. package/dist/query/relations.d.ts +173 -5
  142. package/dist/query/relations.js +688 -47
  143. package/dist/query/types.d.ts +123 -39
  144. package/dist/query/utils.d.ts +116 -0
  145. package/dist/query/utils.js +198 -0
  146. package/dist/query/warn-registry.d.ts +9 -0
  147. package/dist/query/warn-registry.js +9 -0
  148. package/dist/query/where.d.ts +38 -1
  149. package/dist/query/where.js +58 -23
  150. package/dist/query/writes.d.ts +42 -1
  151. package/dist/query/writes.js +104 -13
  152. package/dist/schema-sql.d.ts +14 -0
  153. package/dist/schema-sql.js +23 -5
  154. package/dist/schema.d.ts +38 -0
  155. package/dist/schema.js +123 -0
  156. package/dist/sqlite.js +4 -0
  157. package/package.json +77 -28
@@ -0,0 +1,153 @@
1
+ /**
2
+ * Index advisor — finds relation probes that lack index support.
3
+ *
4
+ * Turbine loads `with` relations as correlated subqueries: for every parent row,
5
+ * the child table is probed by its FK column(s) (`child.fk = parent.pk`). Relation
6
+ * filters (`some`/`none`/`every`, `is`/`isNot`) probe the same columns. This
7
+ * strategy outperforms batched loading (`WHERE fk IN (ids)`) when the probed
8
+ * column is indexed — but with NO index, each probe is a full table scan and the
9
+ * cost multiplies by the parent rowcount, while batched loading would pay the
10
+ * scan only once. A missing FK index that is invisible under a batched-loader
11
+ * ORM becomes pathological under a correlated one.
12
+ *
13
+ * This module derives every column set Turbine will probe from SchemaMetadata's
14
+ * relations and checks each against the table's known indexes (and primary key).
15
+ * Consumed by `turbine doctor` (CLI report + fix migration) and by the dev-mode
16
+ * runtime warning in query/builder.ts.
17
+ */
18
+ import { type SchemaMetadata, type TableMetadata } from './schema.js';
19
+ export interface RelationProbe {
20
+ /** Table on which the relation is declared */
21
+ from: string;
22
+ /** Relation field name */
23
+ relation: string;
24
+ /** Relation type */
25
+ type: 'hasMany' | 'hasOne' | 'belongsTo' | 'manyToMany';
26
+ }
27
+ export interface MissingRelationIndex {
28
+ /** Table that gets probed per parent row */
29
+ table: string;
30
+ /** Probed column(s) — equality lookups, so a covering index must LEAD with one of them */
31
+ columns: string[];
32
+ /** Every relation that generates this probe */
33
+ probes: RelationProbe[];
34
+ /** Suggested index name (matches the --fix migration) */
35
+ indexName: string;
36
+ /** CREATE INDEX statement for the fix migration */
37
+ createSql: string;
38
+ /** DROP INDEX statement for the fix migration's DOWN section */
39
+ dropSql: string;
40
+ }
41
+ /** Options for {@link buildCreateIndexSql}. All default to the plain, in-transaction form. */
42
+ export interface CreateIndexSqlOptions {
43
+ /**
44
+ * Emit `CREATE INDEX CONCURRENTLY`. A concurrent build never holds a write
45
+ * lock, but it CANNOT run inside a transaction block, so a migration carrying
46
+ * it must also carry the `-- turbine:no-transaction` directive.
47
+ */
48
+ concurrently?: boolean;
49
+ /** Emit `IF NOT EXISTS` (default true — required for idempotent no-transaction migrations). */
50
+ ifNotExists?: boolean;
51
+ /**
52
+ * Emit a partial index `... WHERE <col> IS NOT NULL`. Only applied for a
53
+ * single-column index (a relation probe correlates `child.fk = parent.pk`, and
54
+ * NULL never equals anything, so a mostly-NULL FK is fully served by a partial
55
+ * index at a fraction of the size). Ignored for composite indexes.
56
+ */
57
+ partialNotNull?: boolean;
58
+ }
59
+ /**
60
+ * Build a `CREATE INDEX` statement for a relation-probe fix. Pure string
61
+ * assembly (no stats, no DB read): the caller decides whether to pass
62
+ * `concurrently`/`partialNotNull` based on collected statistics, keeping this
63
+ * module topology-only.
64
+ */
65
+ export declare function buildCreateIndexSql(table: string, columns: string[], indexName: string, options?: CreateIndexSqlOptions): string;
66
+ /** Build the matching `DROP INDEX` statement. `concurrently` requires no-transaction execution. */
67
+ export declare function buildDropIndexSql(indexName: string, options?: {
68
+ concurrently?: boolean;
69
+ ifExists?: boolean;
70
+ }): string;
71
+ /**
72
+ * Whether an equality probe on `columns` is served by the table's indexes.
73
+ *
74
+ * All probe columns are equality predicates, so any index whose FIRST column is
75
+ * one of the probed columns gives the planner an index path (a btree can't use
76
+ * a column that isn't in its leading prefix). This is deliberately the loose
77
+ * direction: it never flags a table that has *any* usable index for the probe,
78
+ * at the cost of not demanding the ideal multi-column index. The primary key
79
+ * counts as an index.
80
+ */
81
+ export declare function isProbeIndexed(meta: TableMetadata, columns: string[]): boolean;
82
+ /**
83
+ * The deterministic index name `doctor --fix` uses for a relation-probe index:
84
+ * `idx_<table>_<cols>` truncated to Postgres's 63-byte identifier limit. `doctor
85
+ * --audit` recomputes this to recognize its own previously-suggested indexes.
86
+ */
87
+ export declare function doctorIndexName(table: string, columns: string[]): string;
88
+ /** A (table, columns) pair whose probe maps to a given deterministic name. */
89
+ export interface DoctorProbeColumns {
90
+ table: string;
91
+ columns: string[];
92
+ }
93
+ /**
94
+ * Map every deterministic doctor index name the schema's relation probes would
95
+ * generate to the distinct column sets that produce it. A name that maps to more
96
+ * than one distinct column set is a post-truncation collision (very long column
97
+ * names slicing to the same 63 bytes): `doctor --audit` reports those as
98
+ * ambiguous rather than issuing a confident drop verdict.
99
+ */
100
+ export declare function collectDoctorProbeIndexNames(schema: SchemaMetadata): Map<string, DoctorProbeColumns[]>;
101
+ /**
102
+ * Every (table, column set) a relation probe touches, deduplicated. Consumed by
103
+ * the subtraction side of `doctor` so an index that still serves a live relation
104
+ * is never handed a DROP: the same run's top half demands that index, and
105
+ * recommending both in one report is how a tool loses a DBA's trust.
106
+ */
107
+ export declare function collectRelationProbeColumns(schema: SchemaMetadata): DoctorProbeColumns[];
108
+ /**
109
+ * Scan every relation in the schema and return the probes with no index support,
110
+ * deduplicated by (table, column set) with all contributing relations attached.
111
+ *
112
+ * Only meaningful when the metadata carries index information (i.e. it came from
113
+ * introspection or a generated client). Callers that may hold index-less metadata
114
+ * should gate on {@link schemaHasIndexInfo} to avoid blanket false positives.
115
+ */
116
+ export declare function findMissingRelationIndexes(schema: SchemaMetadata): MissingRelationIndex[];
117
+ /**
118
+ * True when at least one table in the schema carries real, DB-backed index
119
+ * metadata (i.e. from introspection).
120
+ *
121
+ * Excluded (so they never flip the flag, keeping the schema "index-info
122
+ * unknown"):
123
+ * - doc-field expression indexes (`docPath`): they carry no FK-coverage info;
124
+ * - code-first DECLARED indexes (`declared`): the SQL DDL generators do NOT
125
+ * emit `TableDef.indexes` yet, so a declared index does not reflect a real
126
+ * index on the SQL engines. Counting one would arm blanket FK false
127
+ * positives (the FK auto-index the push path DID create is then reported
128
+ * "missing") and, inversely, suppress warnings for indexes never created.
129
+ * A pure code-first schema therefore stays silent exactly as it did before
130
+ * `TableDef.indexes` existed.
131
+ */
132
+ export declare function schemaHasIndexInfo(schema: SchemaMetadata): boolean;
133
+ /**
134
+ * Single-relation check for the dev-mode runtime warning: the (table, columns)
135
+ * this relation probes and whether that probe is unindexed. Returns null when
136
+ * indexed, unknown, or when the schema carries no index info at all (metadata
137
+ * built without introspection — warning would be a blanket false positive).
138
+ */
139
+ export declare function missingIndexForRelation(schema: SchemaMetadata, relDef: {
140
+ name: string;
141
+ type: RelationProbe['type'];
142
+ to: string;
143
+ foreignKey: string | string[];
144
+ referenceKey: string | string[];
145
+ through?: {
146
+ table: string;
147
+ sourceKey: string | string[];
148
+ };
149
+ }): {
150
+ table: string;
151
+ columns: string[];
152
+ createSql: string;
153
+ } | null;
@@ -0,0 +1,384 @@
1
+ /**
2
+ * Index statistics - cost-aware triage for the missing-index advisor.
3
+ *
4
+ * `index-advisor.ts` decides WHICH relation probes lack an index from pure
5
+ * schema topology. This module decides whether adding each one is worth it,
6
+ * reading live Postgres statistics (table size, write volume, existing indexes,
7
+ * HOT-update ratio, column null fraction) and scoring every finding into a tier:
8
+ *
9
+ * - take-freely large table, low write rate, few existing indexes;
10
+ * - take-deliberately real write rate, many existing indexes, or HOT at risk;
11
+ * - scrutinize tiny table, append-only log shape, or stats too young.
12
+ *
13
+ * DESIGN: the file splits into two halves.
14
+ * - The COLLECTOR half (`collectStatsSnapshot`) reads pg catalogs. It uses a
15
+ * single one-connection pool, sets a statement_timeout, and treats every
16
+ * catalog read as INDIVIDUALLY OPTIONAL: a read that fails (privileges,
17
+ * CockroachDB/YugabyteDB catalog gaps, missing view) degrades that one
18
+ * signal and records a notice rather than aborting the whole snapshot.
19
+ * - The PURE half (`scoreMissingIndex`, `findInvalidIndexes`, and the exported
20
+ * threshold constants) takes a typed {@link StatsSnapshot} and imports NO pg.
21
+ * It is fully unit-testable with hand-built snapshots and is what carries the
22
+ * thresholds the CLI prints alongside every verdict.
23
+ *
24
+ * All statistics features are Postgres-only. On a non-Postgres engine the
25
+ * collector returns an unavailable snapshot and the caller falls back to the
26
+ * topology-only report.
27
+ */
28
+ /**
29
+ * Heuristic thresholds behind the tier decision. Exported and surfaced in the
30
+ * doctor output so the reasoning is never a black box: if a threshold is wrong
31
+ * for a workload, the user can see the exact number that drove the verdict.
32
+ */
33
+ export declare const STATS_THRESHOLDS: {
34
+ /** reltuples below this = tiny table; a missing index there is noise → scrutinize. */
35
+ readonly tinyTableRows: 1000;
36
+ /**
37
+ * Writes/day (n_tup_ins+upd+del normalized by stats age) at or above this is a
38
+ * "real" write rate: the per-index write tax is worth a second look → take deliberately.
39
+ */
40
+ readonly highWritesPerDay: 50000;
41
+ /**
42
+ * Existing index count at or above this: every extra index compounds the write
43
+ * and storage tax, so a new one is no longer free → take deliberately.
44
+ */
45
+ readonly manyIndexes: 4;
46
+ /**
47
+ * HOT ratio (n_tup_hot_upd / n_tup_upd) at or above this, WITH real update
48
+ * volume, means the table currently relies on heap-only-tuple updates. A new
49
+ * index can disqualify HOT and amplify write cost → warning + take deliberately.
50
+ */
51
+ readonly hotRatioAtRisk: 0.3;
52
+ /** Minimum n_tup_upd for the HOT signal to be statistically meaningful. */
53
+ readonly hotMinUpdates: 1000;
54
+ /**
55
+ * null_frac at or above this on a probed FK column: the column is mostly NULL,
56
+ * so a partial `WHERE col IS NOT NULL` index covers every relation probe at a
57
+ * fraction of the size.
58
+ */
59
+ readonly partialNullFrac: 0.9;
60
+ /**
61
+ * Stats younger than this (days since stats_reset) cannot normalize a write
62
+ * rate; the whole report degrades to topology-only output.
63
+ */
64
+ readonly minStatsAgeDays: 1;
65
+ /** Append-heavy log shape: n_tup_ins at or above this ... */
66
+ readonly appendHeavyMinInserts: 100000;
67
+ /** ... with inserts making up at or above this fraction of all writes ... */
68
+ readonly appendHeavyInsertRatio: 0.95;
69
+ /** ... and seq_scan at or below this ("near-zero probe reads") → scrutinize. */
70
+ readonly appendHeavyMaxSeqScan: 5;
71
+ /**
72
+ * Default `--min-scans` for the unused-index report: an index with fewer than
73
+ * this many scans since stats_reset is flagged never-scanned. Default 1 makes
74
+ * the bare report mean exactly "idx_scan = 0".
75
+ */
76
+ readonly unusedMinScans: 1;
77
+ /**
78
+ * Queries/min (from _turbine_metrics) at or above this makes a table "hot in
79
+ * your workload": a benefit signal that boosts the finding's priority and
80
+ * annotates it. It never downgrades a cost tier (write cost is a separate axis).
81
+ */
82
+ readonly heatMinQueriesPerMin: 1;
83
+ };
84
+ /** Per-table live statistics. Any field may be absent when its catalog read degraded. */
85
+ export interface TableStats {
86
+ table: string;
87
+ /** pg_class.reltuples. 0 or -1 means never-analyzed → treated as UNKNOWN (null rows). */
88
+ reltuples: number;
89
+ /** pg_stat_user_tables.n_live_tup, a cross-check for reltuples. */
90
+ nLiveTup?: number;
91
+ nTupIns?: number;
92
+ nTupUpd?: number;
93
+ nTupDel?: number;
94
+ nTupHotUpd?: number;
95
+ seqScan?: number;
96
+ seqTupRead?: number;
97
+ /** pg_total_relation_size (table + indexes + toast), bytes. */
98
+ totalSizeBytes?: number;
99
+ /** pg_relation_size (heap only), bytes. */
100
+ tableSizeBytes?: number;
101
+ /** Count of indexes already on the table (pg_index). */
102
+ existingIndexCount?: number;
103
+ }
104
+ /**
105
+ * Placeholder for an index column that is an EXPRESSION, not a plain column
106
+ * (`pg_index.indkey` stores 0 for those, and no `pg_attribute` row has attnum 0).
107
+ * It keeps expression POSITIONS in `IndexStat.columns` instead of silently
108
+ * collapsing `(tenant_id, lower(email))` to `['tenant_id']`, which would make a
109
+ * functional index look like a droppable prefix of an unrelated plain index.
110
+ */
111
+ export declare const EXPRESSION_COLUMN = "(expression)";
112
+ /** A single index's identity + validity, from pg_index / pg_stat_user_indexes. */
113
+ export interface IndexStat {
114
+ table: string;
115
+ indexName: string;
116
+ /** Key + INCLUDE columns in order; an expression slot is {@link EXPRESSION_COLUMN}. */
117
+ columns: string[];
118
+ /** pg_am.amname (btree / gin / gist / hash / brin / spgist). Absent when unread. */
119
+ accessMethod?: string;
120
+ /** True when the index has at least one expression column (pg_index.indexprs). */
121
+ hasExpressions?: boolean;
122
+ /** The partial-index predicate text (pg_index.indpred), or null when not partial. */
123
+ predicate?: string | null;
124
+ /** pg_get_indexdef, the authoritative definition (used for honest reporting). */
125
+ indexDef?: string;
126
+ idxScan?: number;
127
+ isValid: boolean;
128
+ isUnique: boolean;
129
+ isPrimary: boolean;
130
+ isReplicaIdent: boolean;
131
+ /** Backs an exclusion constraint (pg_constraint.contype = 'x'). */
132
+ isExclusion?: boolean;
133
+ /** pg_relation_size of the index heap, bytes. Size reclaimed by a drop. */
134
+ sizeBytes?: number;
135
+ }
136
+ /**
137
+ * A point-in-time read of the statistics the triage needs. Every part is
138
+ * optional at the field level so the pure scorer degrades honestly.
139
+ */
140
+ export interface StatsSnapshot {
141
+ /** True when at least the core table statistics were readable. */
142
+ available: boolean;
143
+ /** pg_stat_database.stats_reset for the current DB. NULL when never reset. */
144
+ statsReset: Date | null;
145
+ /** Days since stats_reset, or null when stats_reset is NULL / in the future. */
146
+ statsAgeDays: number | null;
147
+ /** Per-table stats, keyed by table name. */
148
+ tables: Record<string, TableStats>;
149
+ /** All indexes read (used for invalid-index detection). */
150
+ indexes: IndexStat[];
151
+ /** null_frac per probed column, keyed `table.column`. */
152
+ nullFrac: Record<string, number>;
153
+ /** Per-signal degradation notices (privileges, catalog gaps, timeouts). */
154
+ notices: string[];
155
+ }
156
+ /** Build an empty (fully unavailable) snapshot - the honest "no stats" baseline. */
157
+ export declare function emptyStatsSnapshot(notices?: string[]): StatsSnapshot;
158
+ export type IndexTier = 'take-freely' | 'take-deliberately' | 'scrutinize';
159
+ /** The numbers behind a verdict, all nullable so unknowns never masquerade as zero. */
160
+ export interface FindingMetrics {
161
+ /** reltuples, or null when never-analyzed (0/-1). */
162
+ rows: number | null;
163
+ /** pg_total_relation_size in bytes. */
164
+ sizeBytes: number | null;
165
+ /** Writes/day since stats reset, or null when the rate cannot be normalized. */
166
+ writesPerDay: number | null;
167
+ existingIndexCount: number | null;
168
+ probingRelations: number;
169
+ /** n_tup_hot_upd / n_tup_upd, or null. */
170
+ hotRatio: number | null;
171
+ /** Highest null_frac across the probed columns, or null when unknown. */
172
+ nullFrac: number | null;
173
+ statsAgeDays: number | null;
174
+ }
175
+ export interface ScoredMissingIndex {
176
+ table: string;
177
+ columns: string[];
178
+ tier: IndexTier;
179
+ /** Human-readable reasons, each carrying the number that drove it. */
180
+ reasons: string[];
181
+ metrics: FindingMetrics;
182
+ /** The HOT-update caveat when the table is at risk of losing HOT, else null. */
183
+ hotWarning: string | null;
184
+ /** When true, the emitted index should be a partial `WHERE col IS NOT NULL`. */
185
+ partialNotNull: boolean;
186
+ /** True when workload heat lifted this finding's priority (see {@link TableHeatEntry}). */
187
+ heatBoosted: boolean;
188
+ /** Sort key within a tier: bigger, more-probed tables first; hot tables first of all. */
189
+ benefitScore: number;
190
+ }
191
+ /** Per-table workload heat, derived from the _turbine_metrics per-minute aggregates. */
192
+ export interface TableHeatEntry {
193
+ /** Average queries per minute across the observed window. */
194
+ queriesPerMin: number;
195
+ /** Worst observed p95 latency (ms) across the window. */
196
+ p95Ms: number;
197
+ }
198
+ /** The subset of a topology finding the scorer needs. */
199
+ export interface ScorableMissingIndex {
200
+ table: string;
201
+ columns: string[];
202
+ probes: unknown[];
203
+ }
204
+ /** Format a byte count as a short human string (KB/MB/GB). */
205
+ export declare function formatBytes(bytes: number | null | undefined): string;
206
+ /**
207
+ * Score a single missing-index finding against a snapshot. Pure and total:
208
+ * every unknown degrades to a caveat rather than a fabricated number.
209
+ */
210
+ export declare function scoreMissingIndex(missing: ScorableMissingIndex, snapshot: StatsSnapshot, heat?: TableHeatEntry): ScoredMissingIndex;
211
+ export interface InvalidIndex {
212
+ table: string;
213
+ indexName: string;
214
+ columns: string[];
215
+ /** `DROP INDEX CONCURRENTLY IF EXISTS` - the repair for an INVALID corpse. */
216
+ dropSql: string;
217
+ }
218
+ /**
219
+ * Indexes left INVALID (indisvalid = false) - the failure artifact of a
220
+ * `CREATE INDEX CONCURRENTLY` that errored. `IF NOT EXISTS` on a retry silently
221
+ * skips the corpse, so the fix is DROP INDEX CONCURRENTLY then rerun.
222
+ */
223
+ export declare function findInvalidIndexes(snapshot: StatsSnapshot): InvalidIndex[];
224
+ /** A never-scanned index and the DROP statement that would reclaim it. */
225
+ export interface UnusedIndex {
226
+ table: string;
227
+ indexName: string;
228
+ columns: string[];
229
+ /** idx_scan since the last stats reset (0 or below --min-scans). */
230
+ idxScan: number;
231
+ /** Size reclaimed by the drop, bytes (null when the size read degraded). */
232
+ sizeBytes: number | null;
233
+ /** `DROP INDEX CONCURRENTLY IF EXISTS`: printed only, never written to a migration. */
234
+ dropSql: string;
235
+ /**
236
+ * What is special about this index (functional / partial / non-btree), or null
237
+ * for a plain btree. `idx_scan = 0` is a fact either way, but the caveat says
238
+ * what is actually being dropped: a rebuild is not always a `CREATE INDEX (col)`.
239
+ */
240
+ caveat: string | null;
241
+ /**
242
+ * The same information as `caveat`, structured, so a `--json` consumer never
243
+ * has to regex prose. `kinds` is empty for a plain btree index.
244
+ */
245
+ shape: {
246
+ kinds: Array<'expression' | 'partial' | 'non-btree'>;
247
+ accessMethod: string | null;
248
+ definition: string | null;
249
+ };
250
+ }
251
+ /**
252
+ * Indexes never (or barely) scanned since the last stats reset. Report-only:
253
+ * counters reset on a crash/reset and REPLICA READS NEVER FEED PRIMARY COUNTERS,
254
+ * so an index only a read replica uses looks dead here. Constraint-backing and
255
+ * replica-identity indexes are excluded by construction. An index whose idx_scan
256
+ * could not be read (no pg_stat row) is skipped rather than guessed.
257
+ */
258
+ export declare function findUnusedIndexes(snapshot: StatsSnapshot, options?: {
259
+ minScans?: number;
260
+ relationProbes?: Array<{
261
+ table: string;
262
+ columns: string[];
263
+ }>;
264
+ }): UnusedIndex[];
265
+ /** A redundant index whose columns are a leading prefix of a wider index. */
266
+ export interface RedundantIndex {
267
+ table: string;
268
+ indexName: string;
269
+ columns: string[];
270
+ /** The wider index that already covers this one's leading-prefix lookups. */
271
+ coveredBy: string;
272
+ coveredByColumns: string[];
273
+ sizeBytes: number | null;
274
+ dropSql: string;
275
+ }
276
+ /**
277
+ * Non-unique indexes whose column list is a leading prefix of a WIDER index on
278
+ * the same table. A btree serves any leading-prefix lookup, so the narrow index
279
+ * is redundant. Pure metadata (no idx_scan needed).
280
+ *
281
+ * Uniqueness compatibility: only a NON-unique index is ever reported. A unique
282
+ * or primary-key prefix is load-bearing (it enforces a constraint), so it is
283
+ * never called redundant even when a wider index shares its leading columns.
284
+ *
285
+ * Shape compatibility: see {@link isCoverageComparable}. A functional, partial,
286
+ * or non-btree index is never reported as covered by a plain btree.
287
+ */
288
+ export declare function findRedundantIndexes(snapshot: StatsSnapshot): RedundantIndex[];
289
+ /**
290
+ * A `doctor --audit` finding: an existing index that matches doctor's own
291
+ * deterministic naming AND has not been scanned since the stats reset.
292
+ */
293
+ export interface DoctorIndexAudit {
294
+ table: string;
295
+ indexName: string;
296
+ columns: string[];
297
+ idxScan: number;
298
+ sizeBytes: number | null;
299
+ /** `null` when the index still serves a live relation probe (see stillProbed). */
300
+ dropSql: string | null;
301
+ /**
302
+ * True when the truncated (63-byte) name collides across DIFFERENT column sets
303
+ * in the schema's probes, so which suggestion this index realizes is
304
+ * ambiguous. Reported as ambiguous instead of a confident drop verdict.
305
+ */
306
+ ambiguous: boolean;
307
+ /**
308
+ * True when the schema still declares a relation whose probe this index
309
+ * serves. `dropSql` is null in that case: the index is unused only because
310
+ * this workload has not run those relation queries yet, and dropping it
311
+ * recreates the missing-index finding in the same report's top half.
312
+ */
313
+ stillProbed: boolean;
314
+ }
315
+ /**
316
+ * The unused-index machinery scoped to doctor's OWN previously-suggested indexes:
317
+ * existing indexes whose name matches the `idx_<table>_<cols>` (63-byte truncated)
318
+ * shape doctor emits, never scanned since the stats reset. `doctorNames` maps each
319
+ * deterministic name to the distinct column sets that truncate to it (see
320
+ * `collectDoctorProbeIndexNames`); a name with more than one column set is a
321
+ * post-truncation collision, reported as ambiguous.
322
+ */
323
+ export declare function auditDoctorIndexes(snapshot: StatsSnapshot, doctorNames: Map<string, Array<{
324
+ table: string;
325
+ columns: string[];
326
+ }>>, options?: {
327
+ minScans?: number;
328
+ relationProbes?: Array<{
329
+ table: string;
330
+ columns: string[];
331
+ }>;
332
+ }): DoctorIndexAudit[];
333
+ /**
334
+ * Whether the snapshot is trustworthy enough to render tier verdicts. Empty,
335
+ * unavailable, or too-young stats degrade to the topology-only report.
336
+ */
337
+ export declare function isSnapshotUsable(snapshot: StatsSnapshot): boolean;
338
+ /** A tuple identifying a probed column whose null_frac the collector should read. */
339
+ export interface ProbedColumn {
340
+ table: string;
341
+ column: string;
342
+ }
343
+ export interface CollectSnapshotOptions {
344
+ connectionString: string;
345
+ schema: string;
346
+ /** Tables to read table-level stats + sizes for (the probed tables). */
347
+ tables: string[];
348
+ /** Columns to read null_frac for (single-column probes). */
349
+ columns: ProbedColumn[];
350
+ /** statement_timeout for each catalog read. Default 5000ms. */
351
+ statementTimeoutMs?: number;
352
+ }
353
+ /**
354
+ * Read a live statistics snapshot from Postgres. Each catalog read is wrapped
355
+ * individually: a failure records a notice and leaves that signal absent, so a
356
+ * privilege gap or a CockroachDB/YugabyteDB catalog difference degrades one
357
+ * signal rather than the whole snapshot.
358
+ */
359
+ export declare function collectStatsSnapshot(options: CollectSnapshotOptions): Promise<StatsSnapshot>;
360
+ export interface CollectTableHeatOptions {
361
+ /** Where _turbine_metrics lives (the app DB, or a separate --metrics-url DB). */
362
+ connectionString: string;
363
+ /** Model names to read heat for (the probed table names). */
364
+ models: string[];
365
+ /** Trailing window of per-minute buckets to average, minutes. Default 60. */
366
+ windowMinutes?: number;
367
+ statementTimeoutMs?: number;
368
+ }
369
+ export interface TableHeatResult {
370
+ /** True when _turbine_metrics existed and was read. False = heat boosting off. */
371
+ available: boolean;
372
+ /** Per-model heat, keyed by model (= table) name. */
373
+ tables: Record<string, TableHeatEntry>;
374
+ /** One honesty line when heat could not be sourced (absent table / non-pg sink). */
375
+ notice: string | null;
376
+ }
377
+ /**
378
+ * Read per-model workload heat from a `_turbine_metrics` table (written by
379
+ * `db.$observe()` with the default Postgres sink). When the table is absent, the
380
+ * result is `available: false` with a notice: this is the expected state when
381
+ * observe uses a non-Postgres sink, or when no metrics have been collected yet.
382
+ * Every read is best-effort; a failure degrades to unavailable, never throws.
383
+ */
384
+ export declare function collectTableHeat(options: CollectTableHeatOptions): Promise<TableHeatResult>;
@@ -0,0 +1,55 @@
1
+ /**
2
+ * turbine-orm
3
+ *
4
+ * Turbine TypeScript SDK — type-safe Postgres queries with nested relations
5
+ * and pipeline batching. Feels like Prisma, runs at raw-SQL speed.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * // 1. Generate types from your database:
10
+ * // npx turbine generate
11
+ *
12
+ * // 2. Import the generated client:
13
+ * import { turbine } from './generated/turbine';
14
+ *
15
+ * const db = turbine({ connectionString: process.env.DATABASE_URL });
16
+ *
17
+ * // Type-safe queries with auto-complete
18
+ * const user = await db.users.findUnique({ where: { id: 1 } });
19
+ *
20
+ * // Nested relations in a single query (json_agg, no N+1)
21
+ * const userWithPosts = await db.users.findUnique({
22
+ * where: { id: 1 },
23
+ * with: { posts: { with: { comments: true } } },
24
+ * });
25
+ *
26
+ * // Pipeline: multiple queries in one round-trip
27
+ * const [user, count] = await db.pipeline(
28
+ * db.users.buildFindUnique({ where: { id: 1 } }),
29
+ * db.posts.buildCount({ where: { orgId: 1 } }),
30
+ * );
31
+ *
32
+ * await db.disconnect();
33
+ * ```
34
+ */
35
+ export type { DatabaseAdapter, IntrospectionOverrides } from './adapters/index.js';
36
+ export { alloydb, cockroachdb, postgresql, timescale, yugabytedb } from './adapters/index.js';
37
+ export { type Middleware, type MiddlewareNext, type MiddlewareParams, type PgCompatPool, type PgCompatPoolClient, type PgCompatQueryResult, type RetryOptions, TransactionClient, type TransactionOptions, TurbineClient, type TurbineConfig, type TurbineDriver, withRetry, } from './client.js';
38
+ export type { BuiltStatement, BulkInsertStatementInput, ColumnDefinitionInput, ColumnTypeInput, CreateIndexStatementInput, CreateTableStatementInput, Dialect, DialectIntrospector, DialectMigrator, DialectName, InsertStatementInput, IntrospectOptions as DialectIntrospectOptions, ResultStrategy, StreamableConnection, UpsertStatementInput, } from './dialect.js';
39
+ export { postgresDialect } from './dialect.js';
40
+ export { CheckConstraintError, CircularRelationError, ConnectionError, DeadlockError, type ErrorMessageMode, ExclusionConstraintError, ForeignKeyError, getErrorMessageMode, MigrationError, NotFoundError, NotNullViolationError, OptimisticLockError, PipelineError, type PipelineResultSlot, ReadOnlyError, RelationError, SerializationFailureError, setErrorMessageMode, TimeoutError, TurbineError, TurbineErrorCode, UniqueConstraintError, UnsupportedFeatureError, ValidationError, wrapPgError, } from './errors.js';
41
+ export { type GenerateOptions, generate } from './generate.js';
42
+ export { type IntrospectOptions, introspect } from './introspect.js';
43
+ export { executeNestedCreate, executeNestedUpdate, hasRelationFields, type NestedWriteContext, } from './nested-write.js';
44
+ export { HttpJsonSink, type HttpJsonSinkOptions, type MetricsFlushBatch, type MetricsFlushRow, type ObserveConfig, type ObserveHandle, type ObserveSink, PgMetricsSink, type PgMetricsSinkOptions, } from './observe.js';
45
+ export { executePipeline, type PipelineOptions, type PipelineResults, pipelineSupported } from './pipeline.js';
46
+ export { type AggregateArgs, type AggregateResult, type ArrayFilter, AUTO_ASSUMED_ROUND_TRIP_MS, AUTO_JOIN_PENALTY_MS_PER_ROW, AUTO_TO_ONE_JOIN_MAX_ROWS, AUTO_TO_ONE_JOIN_ROWS_MAX, AUTO_TO_ONE_JOIN_ROWS_MIN, type ColumnRef, type ConnectOrCreateOp, type CountArgs, type CreateArgs, type CreateDataInput, type CreateManyArgs, type DeferredQuery, type DeleteArgs, type DeleteManyArgs, type FieldResult, type FindManyArgs, type FindManyStreamArgs, type FindUniqueArgs, type GlobalFilters, type GroupByAggregateSpec, type GroupByArgs, type GroupByDistinctOn, type GroupByResult, type HavingClause, type JsonFilter, type JsonPathAggregateTarget, type JsonPathGroupKey, type JsonPathOrderBy, type MiddlewareFn, type NestedCreateOp, type NestedUpdateOp, type NestedUpdateOpItem, type NestedUpsertOpItem, type OmitResult, type OrderByClause, type OrderByObject, type OrderDirection, type QueryEvent, type QueryEventListener, QueryInterface, type QueryResult, type RelationDescriptor, type RelationFilter, type RelationLoadStrategy, type RelationPickBy, type RelationPickOrderBy, type SelectResult, type SkipGlobalFilters, type TextSearchFilter, type TypedWithClause, type UpdateArgs, type UpdateDataInput, type UpdateInput, type UpdateManyArgs, type UpdateOperatorInput, type UpsertArgs, type VectorDistanceFilter, type VectorFilter, type VectorMetric, type VectorOrderBy, type VectorOrderByDistance, type WhereClause, type WhereOperator, type WhereValue, type WithClause, type WithOptions, type WithOrderByObject, type WithResult, } from './query/index.js';
47
+ export { type ActiveSubscription, type NotificationHandler, type Subscription, validateChannel } from './realtime.js';
48
+ export type { CheckMetadata, ColumnMetadata, IndexMetadata, PrismaCompatMap, PrismaModelMap, PrismaRelationMap, ReferentialAction, RelationDef, SchemaMetadata, TableMetadata, } from './schema.js';
49
+ export { camelToSnake, isDateType, normalizeKeyColumns, pgArrayType, pgTypeToTs, singularize, snakeToCamel, snakeToPascal, withDbFieldNames, } from './schema.js';
50
+ export { applyManyToManyRelations, type CheckDef, ColumnBuilder, type ColumnConfig, type ColumnDef, type ColumnIndexDef, type ColumnType, type ColumnTypeName, column, type DefineSchemaOptions, type DocFieldIndexDef, defineSchema, isDocFieldIndexDef, type ManyToManyDef, type ReferenceDef, type SchemaDef, type SchemaIndexDef, type TableDef, table, } from './schema-builder.js';
51
+ export { schemaDefToMetadata } from './schema-metadata.js';
52
+ export { type AlterColumnDef, type AlterDef, DestructivePushRefusal, type DiffResult, type PushResult, type SchemaSqlOptions, schemaDiff, schemaPush, schemaToSQL, schemaToSQLString, } from './schema-sql.js';
53
+ export { type DefinedSeed, defineSeed, type SeedFunction } from './seed.js';
54
+ export { type TurbineHttpOptions, turbineHttp } from './serverless.js';
55
+ export { buildTypedSql, TypedSqlQuery } from './typed-sql.js';
package/dist/cjs/index.js CHANGED
@@ -34,8 +34,8 @@
34
34
  * ```
35
35
  */
36
36
  Object.defineProperty(exports, "__esModule", { value: true });
37
- exports.snakeToCamel = exports.singularize = exports.pgTypeToTs = exports.pgArrayType = exports.normalizeKeyColumns = exports.isDateType = exports.camelToSnake = exports.validateChannel = exports.QueryInterface = exports.pipelineSupported = exports.executePipeline = exports.PgMetricsSink = exports.HttpJsonSink = exports.hasRelationFields = exports.executeNestedUpdate = exports.executeNestedCreate = exports.introspect = exports.generate = exports.wrapPgError = exports.ValidationError = exports.UnsupportedFeatureError = exports.UniqueConstraintError = exports.TurbineErrorCode = exports.TurbineError = exports.TimeoutError = exports.setErrorMessageMode = exports.SerializationFailureError = exports.RelationError = exports.ReadOnlyError = exports.PipelineError = exports.OptimisticLockError = exports.NotNullViolationError = exports.NotFoundError = exports.MigrationError = exports.getErrorMessageMode = exports.ForeignKeyError = exports.ExclusionConstraintError = exports.DeadlockError = exports.ConnectionError = exports.CircularRelationError = exports.CheckConstraintError = exports.postgresDialect = exports.withRetry = exports.TurbineClient = exports.TransactionClient = exports.yugabytedb = exports.timescale = exports.postgresql = exports.cockroachdb = exports.alloydb = void 0;
38
- exports.TypedSqlQuery = exports.buildTypedSql = exports.turbineHttp = exports.defineSeed = exports.schemaToSQLString = exports.schemaToSQL = exports.schemaPush = exports.schemaDiff = exports.DestructivePushRefusal = exports.schemaDefToMetadata = exports.table = exports.isDocFieldIndexDef = exports.defineSchema = exports.column = exports.ColumnBuilder = exports.applyManyToManyRelations = exports.withDbFieldNames = exports.snakeToPascal = void 0;
37
+ exports.isDateType = exports.camelToSnake = exports.validateChannel = exports.QueryInterface = exports.AUTO_TO_ONE_JOIN_ROWS_MIN = exports.AUTO_TO_ONE_JOIN_ROWS_MAX = exports.AUTO_TO_ONE_JOIN_MAX_ROWS = exports.AUTO_JOIN_PENALTY_MS_PER_ROW = exports.AUTO_ASSUMED_ROUND_TRIP_MS = exports.pipelineSupported = exports.executePipeline = exports.PgMetricsSink = exports.HttpJsonSink = exports.hasRelationFields = exports.executeNestedUpdate = exports.executeNestedCreate = exports.introspect = exports.generate = exports.wrapPgError = exports.ValidationError = exports.UnsupportedFeatureError = exports.UniqueConstraintError = exports.TurbineErrorCode = exports.TurbineError = exports.TimeoutError = exports.setErrorMessageMode = exports.SerializationFailureError = exports.RelationError = exports.ReadOnlyError = exports.PipelineError = exports.OptimisticLockError = exports.NotNullViolationError = exports.NotFoundError = exports.MigrationError = exports.getErrorMessageMode = exports.ForeignKeyError = exports.ExclusionConstraintError = exports.DeadlockError = exports.ConnectionError = exports.CircularRelationError = exports.CheckConstraintError = exports.postgresDialect = exports.withRetry = exports.TurbineClient = exports.TransactionClient = exports.yugabytedb = exports.timescale = exports.postgresql = exports.cockroachdb = exports.alloydb = void 0;
38
+ exports.TypedSqlQuery = exports.buildTypedSql = exports.turbineHttp = exports.defineSeed = exports.schemaToSQLString = exports.schemaToSQL = exports.schemaPush = exports.schemaDiff = exports.DestructivePushRefusal = exports.schemaDefToMetadata = exports.table = exports.isDocFieldIndexDef = exports.defineSchema = exports.column = exports.ColumnBuilder = exports.applyManyToManyRelations = exports.withDbFieldNames = exports.snakeToPascal = exports.snakeToCamel = exports.singularize = exports.pgTypeToTs = exports.pgArrayType = exports.normalizeKeyColumns = void 0;
39
39
  var index_js_1 = require("./adapters/index.js");
40
40
  Object.defineProperty(exports, "alloydb", { enumerable: true, get: function () { return index_js_1.alloydb; } });
41
41
  Object.defineProperty(exports, "cockroachdb", { enumerable: true, get: function () { return index_js_1.cockroachdb; } });
@@ -95,6 +95,11 @@ Object.defineProperty(exports, "executePipeline", { enumerable: true, get: funct
95
95
  Object.defineProperty(exports, "pipelineSupported", { enumerable: true, get: function () { return pipeline_js_1.pipelineSupported; } });
96
96
  // Query builder
97
97
  var index_js_2 = require("./query/index.js");
98
+ Object.defineProperty(exports, "AUTO_ASSUMED_ROUND_TRIP_MS", { enumerable: true, get: function () { return index_js_2.AUTO_ASSUMED_ROUND_TRIP_MS; } });
99
+ Object.defineProperty(exports, "AUTO_JOIN_PENALTY_MS_PER_ROW", { enumerable: true, get: function () { return index_js_2.AUTO_JOIN_PENALTY_MS_PER_ROW; } });
100
+ Object.defineProperty(exports, "AUTO_TO_ONE_JOIN_MAX_ROWS", { enumerable: true, get: function () { return index_js_2.AUTO_TO_ONE_JOIN_MAX_ROWS; } });
101
+ Object.defineProperty(exports, "AUTO_TO_ONE_JOIN_ROWS_MAX", { enumerable: true, get: function () { return index_js_2.AUTO_TO_ONE_JOIN_ROWS_MAX; } });
102
+ Object.defineProperty(exports, "AUTO_TO_ONE_JOIN_ROWS_MIN", { enumerable: true, get: function () { return index_js_2.AUTO_TO_ONE_JOIN_ROWS_MIN; } });
98
103
  Object.defineProperty(exports, "QueryInterface", { enumerable: true, get: function () { return index_js_2.QueryInterface; } });
99
104
  // Realtime — LISTEN/NOTIFY pub/sub
100
105
  var realtime_js_1 = require("./realtime.js");