@rebasepro/server-postgres 0.12.0 → 0.12.1-canary.g009ed95
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/PostgresBackendDriver.d.ts +1 -1
- package/dist/PostgresBootstrapper.d.ts +25 -1
- package/dist/auth/services.d.ts +21 -0
- package/dist/backup/backup-service.d.ts +10 -1
- package/dist/backup/pg-tools.d.ts +47 -0
- package/dist/backup-service-CD8o_1Sl.js +8999 -0
- package/dist/backup-service-CD8o_1Sl.js.map +1 -0
- package/dist/cli-helpers.d.ts +39 -0
- package/dist/connection-BuZ97wsr.js +250 -0
- package/dist/connection-BuZ97wsr.js.map +1 -0
- package/dist/connection.d.ts +42 -0
- package/dist/ensure-collection-policies-BrUVgjz3.js +57 -0
- package/dist/ensure-collection-policies-BrUVgjz3.js.map +1 -0
- package/dist/ensure-collection-tables-Da2oGkX2.js +650 -0
- package/dist/ensure-collection-tables-Da2oGkX2.js.map +1 -0
- package/dist/index.es.js +816 -9679
- package/dist/index.es.js.map +1 -1
- package/dist/policy-CeA1JcxP.js +105 -0
- package/dist/policy-CeA1JcxP.js.map +1 -0
- package/dist/schema/auth-schema.d.ts +83 -144
- package/dist/schema/ensure-collection-policies.d.ts +60 -0
- package/dist/schema/ensure-collection-tables.d.ts +44 -2
- package/dist/schema/generate-postgres-ddl-logic.d.ts +135 -1
- package/dist/schema/introspect-db-constraints.d.ts +57 -0
- package/dist/schema/introspect-db-logic.d.ts +94 -5
- package/dist/schema/introspect-db-queries.d.ts +119 -0
- package/dist/schema/introspect-db-structure.d.ts +263 -0
- package/dist/schema/introspect-db-types.d.ts +11 -0
- package/dist/services/FetchService.d.ts +4 -1
- package/dist/services/RelationService.d.ts +24 -1
- package/dist/services/channel-bus/index.d.ts +1 -7
- package/dist/services/collection-helpers.d.ts +24 -1
- package/dist/services/dataService.d.ts +3 -1
- package/dist/services/row-pipeline.d.ts +1 -1
- package/dist/{src-BbFOPJ1S.js → src-CzbghKwf.js} +271 -173
- package/dist/src-CzbghKwf.js.map +1 -0
- package/dist/{src-Zqwaw3P5.js → src-DoU9yPqq.js} +3 -159
- package/dist/src-DoU9yPqq.js.map +1 -0
- package/dist/utils/connection-string.d.ts +29 -0
- package/dist/utils/drizzle-conditions.d.ts +5 -4
- package/dist/utils/pg-error-utils.d.ts +19 -0
- package/dist/websocket-B2LsrINK.js +530 -0
- package/dist/websocket-B2LsrINK.js.map +1 -0
- package/package.json +14 -14
- package/src/PostgresAdapter.ts +21 -2
- package/src/PostgresBackendDriver.ts +4 -0
- package/src/PostgresBootstrapper.ts +192 -33
- package/src/auth/ensure-tables.ts +164 -9
- package/src/auth/services.ts +24 -2
- package/src/backup/backup-cli.ts +41 -2
- package/src/backup/backup-service.ts +38 -5
- package/src/backup/pg-tools.ts +96 -3
- package/src/cli-helpers.ts +70 -0
- package/src/cli.ts +44 -26
- package/src/collections/validate-relations.ts +15 -0
- package/src/connection.ts +73 -0
- package/src/data-transformer.ts +9 -3
- package/src/databasePoolManager.ts +5 -2
- package/src/schema/auth-schema.ts +30 -19
- package/src/schema/ensure-collection-policies.ts +105 -0
- package/src/schema/ensure-collection-tables.test.ts +105 -9
- package/src/schema/ensure-collection-tables.ts +220 -32
- package/src/schema/generate-drizzle-schema-logic.ts +23 -6
- package/src/schema/generate-postgres-ddl-logic.ts +382 -19
- package/src/schema/introspect-db-constraints.ts +385 -0
- package/src/schema/introspect-db-inference.ts +18 -8
- package/src/schema/introspect-db-logic.ts +385 -71
- package/src/schema/introspect-db-queries.ts +326 -0
- package/src/schema/introspect-db-structure.ts +670 -0
- package/src/schema/introspect-db-types.ts +56 -0
- package/src/schema/introspect-db.ts +37 -80
- package/src/schema/introspect-runtime.test.ts +56 -8
- package/src/schema/introspect-runtime.ts +31 -9
- package/src/security/policy-drift.test.ts +11 -3
- package/src/services/FetchService.ts +76 -14
- package/src/services/PersistService.ts +20 -6
- package/src/services/RelationService.ts +249 -48
- package/src/services/channel-bus/index.ts +0 -9
- package/src/services/collection-helpers.ts +40 -1
- package/src/services/dataService.ts +3 -1
- package/src/services/realtimeService.ts +3 -3
- package/src/services/row-pipeline.ts +1 -1
- package/src/utils/connection-string.ts +58 -0
- package/src/utils/drizzle-conditions.ts +31 -6
- package/src/utils/pg-error-utils.ts +46 -0
- package/src/websocket.ts +18 -9
- package/dist/chunk-DSJWtz9O.js +0 -40
- package/dist/ensure-collection-tables-CNTcZGvn.js +0 -304
- package/dist/ensure-collection-tables-CNTcZGvn.js.map +0 -1
- package/dist/src-BbFOPJ1S.js.map +0 -1
- package/dist/src-Zqwaw3P5.js.map +0 -1
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a schema's *structure* says about the app on top of it.
|
|
3
|
+
*
|
|
4
|
+
* Introspection has always been a table mirror: one table in, one collection
|
|
5
|
+
* out, one nav entry each, every column a form field. A schema of thirty tables
|
|
6
|
+
* produces thirty sidebar entries, and a panel whose navigation is a list of
|
|
7
|
+
* table names reads as a database browser however good the fields are — which
|
|
8
|
+
* is the actual complaint about generated admin panels, and is structural, not
|
|
9
|
+
* cosmetic.
|
|
10
|
+
*
|
|
11
|
+
* Most of what separates the eight nouns a user navigates by from the thirty
|
|
12
|
+
* tables underneath them is written down in the schema already: which tables
|
|
13
|
+
* only exist to join two others, which are small referenced code lists, which
|
|
14
|
+
* rows cannot outlive a parent row. This module reads that.
|
|
15
|
+
*
|
|
16
|
+
* ## Structure only
|
|
17
|
+
*
|
|
18
|
+
* Nothing here looks at a column or table *name*. Name heuristics — `status`,
|
|
19
|
+
* `*_url`, `image`, `created_at` — are wrong exactly when a schema is not in
|
|
20
|
+
* English, or is domain-specific, or spells things differently, and they are
|
|
21
|
+
* wrong silently. Every rule below is a fact the database enforces: key
|
|
22
|
+
* composition, foreign-key direction and delete rule, uniqueness, nullability,
|
|
23
|
+
* declared type and length, generated-ness, row count.
|
|
24
|
+
*
|
|
25
|
+
* That constraint has a cost, and it is worth stating: a schema that declares
|
|
26
|
+
* nothing beyond `NOT NULL` gives this module very little to work with, and it
|
|
27
|
+
* returns `entity` for everything rather than guessing. Under-classifying is
|
|
28
|
+
* the intended failure mode. A table wrongly hidden from the navigation is a
|
|
29
|
+
* table the user cannot find; a table wrongly left in it is merely the status
|
|
30
|
+
* quo.
|
|
31
|
+
*
|
|
32
|
+
* Pure module: no I/O. Row counts come in on {@link SchemaMetadata.rowCounts},
|
|
33
|
+
* which the caller fills from {@link ./introspect-db-queries.countRowsUpTo} for
|
|
34
|
+
* the tables {@link lookupCandidates} names.
|
|
35
|
+
*/
|
|
36
|
+
import type { ForeignKeyRow, SchemaMetadata, TableColumn, TableMeta } from "./introspect-db-logic";
|
|
37
|
+
import type { CheckFactsByTable } from "./introspect-db-constraints";
|
|
38
|
+
/**
|
|
39
|
+
* The row count above which a referenced table is a real entity rather than a
|
|
40
|
+
* code list. Deliberately low: `pagila.category` has 16 rows and `language` 6,
|
|
41
|
+
* while `actor` has 200 and `country` 109 — the gap between "a fixed set
|
|
42
|
+
* somebody typed once" and "data the app accumulates" is wide, and picking a
|
|
43
|
+
* number in the middle of it costs nothing.
|
|
44
|
+
*/
|
|
45
|
+
export declare const LOOKUP_MAX_ROWS = 50;
|
|
46
|
+
/**
|
|
47
|
+
* The most payload columns a code list may carry. A code list is an id, a
|
|
48
|
+
* label, and perhaps a sort key or a flag; past that it is a table with
|
|
49
|
+
* attributes, which is an entity.
|
|
50
|
+
*/
|
|
51
|
+
export declare const LOOKUP_MAX_PAYLOAD_COLUMNS = 3;
|
|
52
|
+
/**
|
|
53
|
+
* The most enum values a board can usefully have as columns. A kanban with
|
|
54
|
+
* thirty columns is a horizontally scrolling table.
|
|
55
|
+
*/
|
|
56
|
+
export declare const KANBAN_MAX_VALUES = 12;
|
|
57
|
+
/** Below this, a "board" is one or two columns — a filter, not a board. */
|
|
58
|
+
export declare const KANBAN_MIN_VALUES = 2;
|
|
59
|
+
/**
|
|
60
|
+
* How many columns a generated list view shows before it stops being readable.
|
|
61
|
+
* Only applied when a table has more properties than this; a six-column table
|
|
62
|
+
* gets no `listProperties` at all rather than a restatement of its own columns.
|
|
63
|
+
*/
|
|
64
|
+
export declare const LIST_PROPERTIES_CAP = 6;
|
|
65
|
+
/**
|
|
66
|
+
* What a table *is*, structurally.
|
|
67
|
+
*
|
|
68
|
+
* - `entity` — a thing the app is about. Gets a collection and a nav entry.
|
|
69
|
+
* - `junction` — exists only to relate two other tables. Gets no collection at
|
|
70
|
+
* all; it becomes a many-to-many relation on both sides.
|
|
71
|
+
* - `lookup` — a small, referenced, self-contained code list. Gets a collection,
|
|
72
|
+
* grouped away from the entities rather than listed beside them.
|
|
73
|
+
* - `owned-child` — rows that belong to exactly one parent row and are reached
|
|
74
|
+
* through it. Gets a collection (it is a real table with real rows, and the
|
|
75
|
+
* API still serves it) but no nav entry: it already renders as a tab on its
|
|
76
|
+
* parent.
|
|
77
|
+
*/
|
|
78
|
+
export type TableRole = "entity" | "junction" | "lookup" | "owned-child";
|
|
79
|
+
/**
|
|
80
|
+
* Why a table was called someone's child, weakest last.
|
|
81
|
+
*
|
|
82
|
+
* Carried into the generated file as a comment. A reader who disagrees with the
|
|
83
|
+
* classification needs to see what it was based on to know which line to change.
|
|
84
|
+
*/
|
|
85
|
+
export type OwnershipEvidence =
|
|
86
|
+
/** The only foreign key declared `ON DELETE CASCADE`. */
|
|
87
|
+
"cascade-delete"
|
|
88
|
+
/** The only foreign key that is part of the table's primary key. */
|
|
89
|
+
| "identifying-key"
|
|
90
|
+
/** The only foreign key that is `NOT NULL`. */
|
|
91
|
+
| "sole-required-key"
|
|
92
|
+
/** First column of a composite primary key made entirely of foreign keys. */
|
|
93
|
+
| "leading-key-column";
|
|
94
|
+
export interface JunctionShape {
|
|
95
|
+
sourceTable: string;
|
|
96
|
+
sourceColumn: string;
|
|
97
|
+
targetTable: string;
|
|
98
|
+
targetColumn: string;
|
|
99
|
+
}
|
|
100
|
+
export interface TableClassification {
|
|
101
|
+
table: string;
|
|
102
|
+
role: TableRole;
|
|
103
|
+
/** One line, in prose, for the generated file. */
|
|
104
|
+
reason: string;
|
|
105
|
+
/** Set when `role === "owned-child"`. */
|
|
106
|
+
owner?: {
|
|
107
|
+
table: string;
|
|
108
|
+
column: string;
|
|
109
|
+
evidence: OwnershipEvidence;
|
|
110
|
+
};
|
|
111
|
+
/** Set when `role === "junction"`. */
|
|
112
|
+
junction?: JunctionShape;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* A timestamp the database maintains: a temporal column defaulting to the
|
|
116
|
+
* transaction clock.
|
|
117
|
+
*
|
|
118
|
+
* This is the structural stand-in for the `created_at`/`updated_at` name check.
|
|
119
|
+
* It is strictly better than the name: it catches `fecha_creacion` and
|
|
120
|
+
* `last_update` (pagila's spelling, which the name list misses), and it does not
|
|
121
|
+
* fire on a user-editable `created_at date` column that has no default and which
|
|
122
|
+
* the name check would wrongly make read-only.
|
|
123
|
+
*/
|
|
124
|
+
export declare function isAutoTimestamp(column: TableColumn): boolean;
|
|
125
|
+
/** A key the database fills in: identity, serial, or a uuid-generating default. */
|
|
126
|
+
export declare function isGeneratedKey(column: TableColumn): boolean;
|
|
127
|
+
/** A column Postgres computes; writing to it is an error. */
|
|
128
|
+
export declare function isGeneratedColumn(column: TableColumn): boolean;
|
|
129
|
+
/**
|
|
130
|
+
* Types that exist to be searched or indexed, never to be typed into.
|
|
131
|
+
*
|
|
132
|
+
* A `tsvector` column is a derived search index — maintained by a trigger, a
|
|
133
|
+
* generated expression, or an application job — and its contents are lexeme
|
|
134
|
+
* positions, not text. Pagila's `film.fulltext` is one, and introspection used
|
|
135
|
+
* to emit it as an ordinary required string: a mandatory form field whose
|
|
136
|
+
* correct value no user can produce, on the sixth column of the list view.
|
|
137
|
+
*/
|
|
138
|
+
export declare function isDerivedIndexColumn(column: TableColumn): boolean;
|
|
139
|
+
/** Anything the user cannot meaningfully edit, whatever the reason. */
|
|
140
|
+
export declare function isReadOnlyColumn(column: TableColumn): boolean;
|
|
141
|
+
/**
|
|
142
|
+
* A string column with a declared maximum length.
|
|
143
|
+
*
|
|
144
|
+
* `varchar(50)` and `text` are the same type to an application but not to the
|
|
145
|
+
* author: choosing a bound is a statement that the value is short and
|
|
146
|
+
* label-like, which is what makes this usable for picking a display column.
|
|
147
|
+
*/
|
|
148
|
+
export declare function isBoundedString(column: TableColumn): boolean;
|
|
149
|
+
/**
|
|
150
|
+
* A column carrying data rather than structure: not a key, not a foreign key,
|
|
151
|
+
* not a database-maintained timestamp, not computed.
|
|
152
|
+
*
|
|
153
|
+
* The count of these is what tells a pure join table from an association that
|
|
154
|
+
* carries its own attributes — `northwind.order_details` has the key shape of a
|
|
155
|
+
* junction and three payload columns, so it is not one.
|
|
156
|
+
*/
|
|
157
|
+
export declare function isPayloadColumn(column: TableColumn, pks: string[], fkColumns: Set<string>): boolean;
|
|
158
|
+
/** One foreign key, with its columns grouped back together. */
|
|
159
|
+
export interface ForeignKeyConstraint {
|
|
160
|
+
name: string;
|
|
161
|
+
table: string;
|
|
162
|
+
columns: string[];
|
|
163
|
+
foreignTable: string;
|
|
164
|
+
foreignColumns: string[];
|
|
165
|
+
deleteRule?: string;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Groups per-column foreign key rows back into constraints.
|
|
169
|
+
*
|
|
170
|
+
* Rows arrive one per referencing column. A composite key looks exactly like two
|
|
171
|
+
* separate keys until they are grouped by constraint name, and the difference
|
|
172
|
+
* matters: two single-column keys to two tables can be a junction, one
|
|
173
|
+
* two-column key never is.
|
|
174
|
+
*/
|
|
175
|
+
export declare function groupForeignKeys(fks: ForeignKeyRow[]): ForeignKeyConstraint[];
|
|
176
|
+
/**
|
|
177
|
+
* Names the tables whose classification depends on a row count.
|
|
178
|
+
*
|
|
179
|
+
* The caller counts these — and only these — before calling
|
|
180
|
+
* {@link classifyTables}. On a schema of any size this is a handful of tables,
|
|
181
|
+
* and the count itself is capped (see `countRowsUpTo`), so the whole extra cost
|
|
182
|
+
* is bounded regardless of how much data the database holds.
|
|
183
|
+
*/
|
|
184
|
+
export declare function lookupCandidates(metadata: SchemaMetadata, tables: Map<string, TableMeta>): string[];
|
|
185
|
+
/**
|
|
186
|
+
* Classifies every table in the schema.
|
|
187
|
+
*
|
|
188
|
+
* Order matters: junction is the most specific and most consequential (the
|
|
189
|
+
* table disappears), so it is tested first; then lookup, which needs no
|
|
190
|
+
* ownership reasoning; then ownership. Anything unmatched is an entity, which
|
|
191
|
+
* is also what every rule falls back to when its evidence is ambiguous.
|
|
192
|
+
*/
|
|
193
|
+
export declare function classifyTables(metadata: SchemaMetadata, tables: Map<string, TableMeta>): Map<string, TableClassification>;
|
|
194
|
+
/**
|
|
195
|
+
* The columns a property-level derivation needs, resolved once.
|
|
196
|
+
*/
|
|
197
|
+
export interface ColumnFacts {
|
|
198
|
+
column: TableColumn;
|
|
199
|
+
isPk: boolean;
|
|
200
|
+
isFk: boolean;
|
|
201
|
+
/** Covered by a single-column unique constraint or unique index. */
|
|
202
|
+
isUniqueAlone: boolean;
|
|
203
|
+
isAutoTimestamp: boolean;
|
|
204
|
+
isGenerated: boolean;
|
|
205
|
+
/** Allowed values, from a Postgres enum type or a readable CHECK. */
|
|
206
|
+
enumValues?: string[];
|
|
207
|
+
propType: string;
|
|
208
|
+
}
|
|
209
|
+
export declare function buildColumnFacts(meta: TableMeta, metadata: SchemaMetadata, enumMap: Map<string, string[]>, checkFacts: CheckFactsByTable): Map<string, ColumnFacts>;
|
|
210
|
+
/**
|
|
211
|
+
* The column that identifies a row to a human.
|
|
212
|
+
*
|
|
213
|
+
* Structural, in three rungs, strongest first:
|
|
214
|
+
*
|
|
215
|
+
* 1. A single-column unique constraint on a required string. This is as close
|
|
216
|
+
* as a schema comes to declaring "this is what a row is called": it is the
|
|
217
|
+
* column a person looks a row up by, and the database guarantees it picks
|
|
218
|
+
* out one row.
|
|
219
|
+
* 2. The first required string that declares a length, when the table also has
|
|
220
|
+
* strings that do not. Choosing `varchar(n)` for one column and `text` for
|
|
221
|
+
* another is the author distinguishing a label from prose.
|
|
222
|
+
* 3. The first required string in declaration order. Weak, but it is the same
|
|
223
|
+
* rung the panel's own fallback stands on, and column order carries real
|
|
224
|
+
* information — the identifying column of a table is written near the top of
|
|
225
|
+
* it, in every schema, in every language.
|
|
226
|
+
*
|
|
227
|
+
* Deliberately not: a column called `name`, or `title`. That works on English
|
|
228
|
+
* schemas written by someone who read the same tutorial. This picks
|
|
229
|
+
* `film.title`, `actor.first_name` and `category.name` out of pagila without
|
|
230
|
+
* knowing what any of those words mean.
|
|
231
|
+
*/
|
|
232
|
+
export declare function deriveTitleProperty(facts: Map<string, ColumnFacts>): string | undefined;
|
|
233
|
+
/**
|
|
234
|
+
* The enum column a board should have as its columns.
|
|
235
|
+
*
|
|
236
|
+
* A board needs a small, closed, always-present set of states. `NOT NULL` is
|
|
237
|
+
* required because a null has no column to sit in; the bounds keep out
|
|
238
|
+
* two-state flags (a filter, not a board) and long code lists (a scrolling
|
|
239
|
+
* table). The first qualifying column in declaration order wins, so the output
|
|
240
|
+
* is stable across runs.
|
|
241
|
+
*/
|
|
242
|
+
export declare function deriveKanbanProperty(facts: Map<string, ColumnFacts>): string | undefined;
|
|
243
|
+
/**
|
|
244
|
+
* The column a list should be sorted by, newest first.
|
|
245
|
+
*
|
|
246
|
+
* Only when the table has exactly one database-maintained timestamp. With two —
|
|
247
|
+
* a created and an updated stamp — the two orderings differ and the schema does
|
|
248
|
+
* not say which the user means, so neither is chosen.
|
|
249
|
+
*/
|
|
250
|
+
export declare function deriveSort(facts: Map<string, ColumnFacts>): [string, "desc"] | undefined;
|
|
251
|
+
/**
|
|
252
|
+
* The first `LIST_PROPERTIES_CAP` visible properties, or nothing.
|
|
253
|
+
*
|
|
254
|
+
* Returning nothing when the table is already narrow matters: `listProperties`
|
|
255
|
+
* that restates every column is config the reader has to check against the
|
|
256
|
+
* property list to discover it does nothing, and it silently stops new columns
|
|
257
|
+
* from appearing in the list view when someone adds one later.
|
|
258
|
+
*
|
|
259
|
+
* `hidden` names the properties already marked `hideFromCollection` — spending
|
|
260
|
+
* one of six columns on a value the list does not render is worse than not
|
|
261
|
+
* capping at all.
|
|
262
|
+
*/
|
|
263
|
+
export declare function deriveListProperties(propertiesOrder: string[], hidden?: ReadonlySet<string>): string[] | undefined;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The PostgreSQL type → Rebase property type mapping.
|
|
3
|
+
*
|
|
4
|
+
* Split out of `introspect-db-logic` so that the structural analysis can use it
|
|
5
|
+
* without importing the generator, which imports the analysis. Re-exported from
|
|
6
|
+
* `introspect-db-logic` so existing callers keep their import path.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Map a PostgreSQL data type to a Rebase property type.
|
|
10
|
+
*/
|
|
11
|
+
export declare function mapPgType(dataType: string): string;
|
|
@@ -141,7 +141,7 @@ export declare class FetchService {
|
|
|
141
141
|
/**
|
|
142
142
|
* Fallback path used when db.query is unavailable.
|
|
143
143
|
*
|
|
144
|
-
* The primary path runs the results through `
|
|
144
|
+
* The primary path runs the results through `toFlatRow`, which maps
|
|
145
145
|
* relations from what drizzle already nested — no query per row. This one
|
|
146
146
|
* has no nesting to read, so it resolves relations itself, in batches.
|
|
147
147
|
*
|
|
@@ -177,6 +177,7 @@ export declare class FetchService {
|
|
|
177
177
|
*/
|
|
178
178
|
count<M extends Record<string, unknown>>(collectionPath: string, options?: {
|
|
179
179
|
filter?: FilterValues<Extract<keyof M, string>>;
|
|
180
|
+
logical?: LogicalCondition;
|
|
180
181
|
searchString?: string;
|
|
181
182
|
databaseId?: string;
|
|
182
183
|
}): Promise<number>;
|
|
@@ -198,6 +199,8 @@ export declare class FetchService {
|
|
|
198
199
|
*/
|
|
199
200
|
fetchCollectionForRest<M extends Record<string, unknown>>(collectionPath: string, options?: {
|
|
200
201
|
filter?: FilterValues<Extract<keyof M, string>>;
|
|
202
|
+
/** An `or(...)`/`and(...)` group, applied alongside `filter`. */
|
|
203
|
+
logical?: LogicalCondition;
|
|
201
204
|
orderBy?: string;
|
|
202
205
|
order?: "desc" | "asc";
|
|
203
206
|
limit?: number;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { DrizzleClient } from "../interfaces";
|
|
2
|
-
import { CollectionConfig, FilterValues, ResolvedRelation, ResolvedManyToMany } from "@rebasepro/types";
|
|
2
|
+
import { CollectionConfig, FilterValues, ResolvedRelation, ResolvedManyToMany, ResolvedHasMany, ResolvedHasOne } from "@rebasepro/types";
|
|
3
3
|
import { type ResolvedVia } from "@rebasepro/types";
|
|
4
4
|
import { PostgresCollectionRegistry } from "../collections/PostgresCollectionRegistry";
|
|
5
5
|
import type { NestedPathHop } from "./nested-path";
|
|
@@ -56,6 +56,29 @@ export declare class RelationService {
|
|
|
56
56
|
* hand a tenant's rows to its neighbour — say so instead.
|
|
57
57
|
*/
|
|
58
58
|
private assertSingleKeyAddressable;
|
|
59
|
+
/**
|
|
60
|
+
* What the target's foreign key holds, for each of these parent rows.
|
|
61
|
+
*
|
|
62
|
+
* Ordinarily the parent's id, and then this is free. When the relation
|
|
63
|
+
* declares a `sourceKey` the two are different values, and the mapping
|
|
64
|
+
* between them lives in the source table — so it costs one SELECT, issued
|
|
65
|
+
* once for the whole batch rather than per parent.
|
|
66
|
+
*
|
|
67
|
+
* Both directions come back because both are needed and deriving one from
|
|
68
|
+
* the other by hand is how a batch loader ends up attributing a child to the
|
|
69
|
+
* wrong parent: reads translate id → key to build the WHERE, and then
|
|
70
|
+
* translate key → id to attribute each row that comes back.
|
|
71
|
+
*/
|
|
72
|
+
/**
|
|
73
|
+
* The value a related row's foreign key must hold to belong to this parent.
|
|
74
|
+
*
|
|
75
|
+
* `undefined` when the parent's source key is null — which is not an error
|
|
76
|
+
* here, only in the callers that were about to write it. Exposed for
|
|
77
|
+
* {@link PersistService}, which stamps this onto a child created under a
|
|
78
|
+
* nested path and would otherwise write the id and lose the row.
|
|
79
|
+
*/
|
|
80
|
+
parentKeyValue(parentCollection: CollectionConfig, relation: ResolvedHasOne | ResolvedHasMany, parentId: string | number, db?: DrizzleClient): Promise<string | number | undefined>;
|
|
81
|
+
private resolveSourceKeys;
|
|
59
82
|
/**
|
|
60
83
|
* Fetch rows related to a parent row through a specific relation
|
|
61
84
|
*/
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* `types/channel_bus.ts` for the contract such a package implements.
|
|
16
16
|
*/
|
|
17
17
|
import { NodePgDatabase } from "drizzle-orm/node-postgres";
|
|
18
|
-
import { type ChannelBus, type
|
|
18
|
+
import { type ChannelBus, type ChannelBusSetting } from "@rebasepro/types";
|
|
19
19
|
export * from "./ChannelBus";
|
|
20
20
|
export { PostgresChannelBus, CHANNEL_BUS_NOTIFY_CHANNEL, PG_NOTIFY_MAX_PAYLOAD_BYTES, DEFAULT_BATCH_WINDOW_MS, parseChannelBusFrame, parseChannelBusPayload } from "./PostgresChannelBus";
|
|
21
21
|
export interface ChannelBusDeps {
|
|
@@ -37,12 +37,6 @@ export interface ChannelBusDeps {
|
|
|
37
37
|
* mean silently discarding the object the application handed us.
|
|
38
38
|
*/
|
|
39
39
|
export declare function resolveChannelBusSetting(configured?: ChannelBusSetting): ChannelBusSetting;
|
|
40
|
-
/**
|
|
41
|
-
* @deprecated Use {@link resolveChannelBusSetting}, which also accepts a
|
|
42
|
-
* supplied {@link ChannelBus} instance. Kept as a narrow alias so existing
|
|
43
|
-
* config-only callers keep their exact types.
|
|
44
|
-
*/
|
|
45
|
-
export declare function resolveChannelBusConfig(configured?: ChannelBusConfig): ChannelBusConfig;
|
|
46
40
|
/**
|
|
47
41
|
* Produce the bus a setting asks for.
|
|
48
42
|
*
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { PgTable, AnyPgColumn } from "drizzle-orm/pg-core";
|
|
2
|
-
import { CollectionConfig } from "@rebasepro/types";
|
|
2
|
+
import { CollectionConfig, ResolvedHasMany, ResolvedHasOne } from "@rebasepro/types";
|
|
3
3
|
import { PostgresCollectionRegistry } from "../collections/PostgresCollectionRegistry";
|
|
4
4
|
export { buildCompositeId, parseIdValues, COMPOSITE_ID_SEPARATOR } from "@rebasepro/common";
|
|
5
5
|
export type { PrimaryKeyInfo } from "@rebasepro/common";
|
|
@@ -55,6 +55,29 @@ export declare function getPrimaryKeys(collection: CollectionConfig, registry: P
|
|
|
55
55
|
* wrong and which collection it is wrong about.
|
|
56
56
|
*/
|
|
57
57
|
export declare function requirePrimaryKeys(collection: CollectionConfig, registry: PostgresCollectionRegistry): PrimaryKeyInfo[];
|
|
58
|
+
/**
|
|
59
|
+
* The column on the *source* table that a `hasOne`/`hasMany` link points at.
|
|
60
|
+
*
|
|
61
|
+
* `sourceKey` is authored when the two sides join on a natural key — an
|
|
62
|
+
* external identity id, a SKU — and left off when they join on the row id,
|
|
63
|
+
* which is the overwhelming majority. That makes `undefined` the only optional
|
|
64
|
+
* field on a resolved relation, so it gets exactly one reader: this function.
|
|
65
|
+
* Every consumer that needs the column asks here, and none of them re-derives
|
|
66
|
+
* "or else the primary key" for itself. That is the whole point — the fallback
|
|
67
|
+
* chains this codebase removed from relation resolution were dangerous because
|
|
68
|
+
* they were *duplicated* and could disagree, not because they existed.
|
|
69
|
+
*/
|
|
70
|
+
export declare function sourceKeyField(relation: ResolvedHasOne | ResolvedHasMany, sourceCollection: CollectionConfig, registry: PostgresCollectionRegistry): string;
|
|
71
|
+
/**
|
|
72
|
+
* Whether this link joins on something other than the source's primary key.
|
|
73
|
+
*
|
|
74
|
+
* Callers that hold a parent *id* — which is most of them, since an id is what
|
|
75
|
+
* a URL carries — must translate it to the source key's value before it can be
|
|
76
|
+
* compared with the target's foreign key. Those that hold the parent *row*, or
|
|
77
|
+
* that build a correlated subquery over the source table, can read the column
|
|
78
|
+
* directly and skip the lookup.
|
|
79
|
+
*/
|
|
80
|
+
export declare function joinsOnNaturalKey(relation: ResolvedHasOne | ResolvedHasMany, sourceCollection: CollectionConfig, registry: PostgresCollectionRegistry): boolean;
|
|
58
81
|
/**
|
|
59
82
|
* Collections whose key the *browser* cannot resolve, and what it will do
|
|
60
83
|
* instead.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FilterValues } from "@rebasepro/types";
|
|
1
|
+
import { FilterValues, LogicalCondition } from "@rebasepro/types";
|
|
2
2
|
import type { VectorSearchParams } from "@rebasepro/types";
|
|
3
3
|
import { FetchService } from "./FetchService";
|
|
4
4
|
import { PersistService } from "./PersistService";
|
|
@@ -60,6 +60,8 @@ export declare class DataService implements DataRepository {
|
|
|
60
60
|
*/
|
|
61
61
|
count<M extends Record<string, unknown>>(collectionPath: string, options?: {
|
|
62
62
|
filter?: FilterValues<Extract<keyof M, string>>;
|
|
63
|
+
/** An `or(...)`/`and(...)` group, applied alongside `filter`. */
|
|
64
|
+
logical?: LogicalCondition;
|
|
63
65
|
searchString?: string;
|
|
64
66
|
databaseId?: string;
|
|
65
67
|
}): Promise<number>;
|
|
@@ -47,7 +47,7 @@ export declare function relationTargetAddress(targetRow: Record<string, unknown>
|
|
|
47
47
|
* expects real types. The row's own address is *not* among the columns — it is
|
|
48
48
|
* derived by the consumer from the collection's primary keys.
|
|
49
49
|
*/
|
|
50
|
-
export declare function
|
|
50
|
+
export declare function toFlatRow(row: Record<string, unknown>, collection: CollectionConfig, registry: PostgresCollectionRegistry): Record<string, unknown>;
|
|
51
51
|
/**
|
|
52
52
|
* The row REST serves: every column under its own name, with the value Postgres
|
|
53
53
|
* returned, and relations inlined as the target's columns.
|