@juit/pgproxy-persister 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,228 @@
1
+ # PostgreSQL Proxy Client (Persister Interface)
2
+
3
+ The persister interface for PostgreSQL Proxy is a higher-level interface
4
+ offering (on top of the usual connection and query interface) a CRUD
5
+ abstraction over database tables and few utility methods.
6
+
7
+ * [Connecting](#connecting)
8
+ * [Schema Definition](#schema-defintion)
9
+ * [Model Views](#model-views)
10
+ * [Create](#create)
11
+ * [Upsert](#upsert)
12
+ * [Read](#read)
13
+ * [Find](#find)
14
+ * [Update](#update)
15
+ * [Delete](#delete)
16
+ * [Pinging the database](#pinging-the-database)
17
+ * [PGProxy](https://github.com/juitnow/juit-pgproxy/blob/main/README.md)
18
+ * [Copyright Notice](https://github.com/juitnow/juit-pgproxy/blob/main/NOTICE.md)
19
+ * [License](https://github.com/juitnow/juit-pgproxy/blob/main/NOTICE.md)
20
+
21
+
22
+
23
+ ### Connecting
24
+
25
+ In the code, you can simply depend on the `Persister` class:
26
+
27
+ ```ts
28
+ import { Persister } from '@juit/pgproxy-persister'
29
+
30
+ const client = new Persister()
31
+ ```
32
+
33
+ As with the standard client (`PGClient`) persisters can be constructed with a
34
+ `url` as a parameter, indicating the endpoint of the connection _and_
35
+ the specific client to be used.
36
+
37
+
38
+
39
+ ### Schema Definition
40
+
41
+ The `Persister` interface (and the `Model`s bound to it) is a _generic_
42
+ interface. The `Schema` type parameter can be used to provide a fully typed
43
+ view over the columns (and related `Model`s) it manages.
44
+
45
+ Formally, the `Schema` is a type mapping table and column names to column
46
+ definitions. Each column definition is a type containing the following
47
+ properties:
48
+
49
+ * `type`: the _type_ of the column
50
+ * `isNullable` _(optional)_: if `true` the column is _nullable_ and henceforth
51
+ the `null` value can be used in lieu of the `type` above.
52
+ * `hasDefault` _(optional)_: if `true` the column _specifies a default value_
53
+ and therefore can be omitted in create operations.
54
+
55
+ An example of a `Schema` is as follows:
56
+
57
+ ```ts
58
+ /** Definition for all modelable columns */
59
+ export interface MySchema {
60
+ /** Columns for the `users` table */
61
+ users: {
62
+ /** Definition for the `id` column in `users` */
63
+ id: { type: number, hasDefault: true } // not nullable, but has default
64
+ /** Definition for the `email` column in `users` */
65
+ email: { type: string } // not nullable, no default, required creating
66
+ /** Definition for the `age` column in `users` */
67
+ age: { type: number, isNullable: true, hasDefault: false }
68
+
69
+ // ... all other columns
70
+ },
71
+
72
+ // ... all other tables
73
+ }
74
+ ```
75
+
76
+ The `@juit/pgproxy-utils` comes with a useful schema generator, querying a
77
+ database for all of its tables and generating a proper TypeScript interface.
78
+
79
+
80
+
81
+ ### Model views
82
+
83
+ Model views offer a very basic interface to **C**reate, **R**ead, **U**pdate
84
+ and **D**elete data from a table.
85
+
86
+ A _CRUD_ model can be obtained by calling the `in(tableName)` on a `Persister`
87
+ or `connection` object, for example:
88
+
89
+ ```ts
90
+ const model = persister.in('myTable')
91
+ model.create({ ... })
92
+ model.delete({ ... })
93
+
94
+ persister.connect(async (connection) => {
95
+ const model = connection.in('myTable')
96
+ await model.create({ ... })
97
+ await model.delete({ ... })
98
+ })
99
+ ```
100
+
101
+ #### Create
102
+
103
+ The model's `create(object)` function will create `INSERT INTO ... RETURNING *`
104
+ statements based on the specified object.
105
+
106
+ Each key in the object will represent a _column name_ and its associated value
107
+ will be inserted in place.
108
+
109
+ This function will return (obviously) the values inserted, including any default
110
+ value calculated by the database.
111
+
112
+ ```typescript
113
+ persisterOrConnection.in('myTable').create({ myString: 'foo', myNumber: 123 })
114
+ // INSERT INTO "myTable" ("myString", "myNumber") VALUES ('foo', 123) RETURNING *
115
+
116
+ persisterOrConnection.in('myTable').create({})
117
+ // INSERT INTO "myTable" DEFAULT VALUES RETURNING *
118
+ ```
119
+
120
+ #### Upsert
121
+
122
+ The model's `upsert(keys, data)` function will create upsert statements like
123
+ `INSERT INTO ... ON CONFLICT (...) DO UPDATE ... RETURNING *`.
124
+
125
+ The `keys` object passed as a first argument indicates the columns (and values
126
+ to set) for which conflicts are to be detected, while `data` is an object
127
+ containing other columns to update.
128
+
129
+ This function will return the values inserted and/or updated.
130
+
131
+ ```typescript
132
+ persisterOrConnection.in('myTable').upsert({
133
+ myKey: 'myValue', anotherKey: 'anotherValue'
134
+ }, {
135
+ myString: 'foo', myNumber: 123
136
+ })
137
+ // INSERT INTO "myTable" ("myKey", "anotherKey", "myString", "myNumber")
138
+ // VALUES ('myValue', 'anotherValue', 'foo', 123)
139
+ // ON CONFLICT ("myKey", "anotherKey") DO UPDATE
140
+ // SET "myString"='foo',
141
+ // "myNumber"=123
142
+ // RETURNING *
143
+ ```
144
+
145
+ #### Read
146
+
147
+ The model's `read(query, sort)` function will create `SELECT * FROM ...`
148
+ statements based on the specified query and sort parameters.
149
+
150
+ Each key/value mapping in the query object will be mapped to a `WHERE key=value`
151
+ statement part.
152
+
153
+ The sort parameter must be an `Array` of `string`(s) containing the column name
154
+ and (optionally) the keywords `ASC` or `DESC`:
155
+
156
+ ```ts
157
+ persisterOrConnection.in('myTable').read({ myString: 'foo', myNumber: 123 }, [
158
+ 'mySortColumn',
159
+ 'anotherSortColumn ASC',
160
+ 'yetAnotherSortColumn DESC',
161
+ ])
162
+ // SELECT * FROM "myTable" WHERE "myString"='foo' AND "myNumber"=123
163
+ // ORDER BY "mySortColumn", "anotherSortColumn" ASC, "yetAnotherSortColumn" DESC
164
+ ```
165
+
166
+ #### Find
167
+
168
+ Similar to `read(...)` this method will return the _first_ result of the
169
+ generated `SELECT` query, or _undefined_ in case of no results:
170
+
171
+ ```ts
172
+ persisterOrConnection.in('myTable').find({ myString: 'foo', myNumber: 123 }, [
173
+ 'mySortColumn',
174
+ 'anotherSortColumn ASC',
175
+ 'yetAnotherSortColumn DESC',
176
+ ])
177
+ // SELECT * FROM "myTable" WHERE "myString"='foo' AND "myNumber"=123
178
+ // ORDER BY "mySortColumn", "anotherSortColumn" ASC, "yetAnotherSortColumn" DESC
179
+ // LIMIT 1
180
+ ```
181
+
182
+ #### Update
183
+
184
+ The model's `update(query, patch)` function will create
185
+ `UPDATE ... WHERE ... SET ... RETURNING *` statements.
186
+
187
+ * the `query` parameter will work as in [read](#read), generating `WHERE ...`
188
+ statement parts.
189
+ * the `patch` parameter will work similarly to [create](#create), generating
190
+ `SET ...=...` statement parts.
191
+
192
+ This function will _cowardly_ fail when the `query` parameter is an empty object
193
+ (by design, we don't allow modification of _all_ rows in a database).
194
+
195
+ This function will return an `Array` of all rows modified by this call.
196
+
197
+ ```javascript
198
+ persisterOrConnection.in('myTable').update({ myString: 'foo'}, { myNumber: 123 })
199
+ // UPDATE "myTable" SET "myNumber=123 WHERE "myString"='foo' RETURNING *
200
+ ```
201
+
202
+ #### Delete
203
+
204
+ The model's `delete(query)` function will create
205
+ `DELETE FROM ... WHERE ... RETURNING *` statements.
206
+
207
+ * the `query` parameter will work as in [read](#read), generating `WHERE ...`
208
+ statement parts.
209
+ * the `patch` parameter will work similarly to [create](#create), generating
210
+ `SET ...=...` statement parts.
211
+
212
+ This function will _cowardly_ fail when the `query` parameter is an empty object
213
+ (by design, we don't allow deletion of _all_ rows in a database).
214
+
215
+ This function will return the _number of rows_ deleted by the query.
216
+
217
+ ```javascript
218
+ persisterOrConnection.in('myTable').delete({ myString: 'foo'})
219
+ // DELETE FROM "myTable" WHERE "myString"='foo' RETURNING *
220
+ ```
221
+
222
+
223
+
224
+ ### Pinging the database
225
+
226
+ The `ping()` method on `Persister` is a simple shortcut to
227
+ `void query('SELECT now()')` and can be used to ping the database (for health
228
+ checks, connectivity checks, keepalives, ...).
package/dist/index.cjs ADDED
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __copyProps = (to, from, except, desc) => {
7
+ if (from && typeof from === "object" || typeof from === "function") {
8
+ for (let key of __getOwnPropNames(from))
9
+ if (!__hasOwnProp.call(to, key) && key !== except)
10
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
11
+ }
12
+ return to;
13
+ };
14
+ var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
15
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
16
+
17
+ // index.ts
18
+ var src_exports = {};
19
+ module.exports = __toCommonJS(src_exports);
20
+ __reExport(src_exports, require("./model.cjs"), module.exports);
21
+ __reExport(src_exports, require("./persister.cjs"), module.exports);
22
+ // Annotate the CommonJS export names for ESM import in node:
23
+ 0 && (module.exports = {
24
+ ...require("./model.cjs"),
25
+ ...require("./persister.cjs")
26
+ });
27
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1,6 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/index.ts"],
4
+ "mappings": ";;;;;;;;;;;;;;;;;AAAA;AAAA;AACA,wBAAc,wBADd;AAEA,wBAAc,4BAFd;",
5
+ "names": []
6
+ }
@@ -0,0 +1,2 @@
1
+ export * from './model';
2
+ export * from './persister';
package/dist/index.mjs ADDED
@@ -0,0 +1,4 @@
1
+ // index.ts
2
+ export * from "./model.mjs";
3
+ export * from "./persister.mjs";
4
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1,6 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/index.ts"],
4
+ "mappings": ";AACA,cAAc;AACd,cAAc;",
5
+ "names": []
6
+ }
package/dist/model.cjs ADDED
@@ -0,0 +1,211 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // model.ts
21
+ var model_exports = {};
22
+ __export(model_exports, {
23
+ Model: () => Model,
24
+ escape: () => escape
25
+ });
26
+ module.exports = __toCommonJS(model_exports);
27
+ function assert(assertion, message) {
28
+ if (!assertion)
29
+ throw new Error(message);
30
+ }
31
+ function assertArray(value, message) {
32
+ assert(Array.isArray(value), message);
33
+ }
34
+ function assertObject(value, message) {
35
+ assert(value && typeof value === "object", message);
36
+ }
37
+ function where(query, params) {
38
+ const conditions = [];
39
+ let count = 0;
40
+ for (const [column, value] of Object.entries(query)) {
41
+ if (value === void 0)
42
+ continue;
43
+ if (value === null) {
44
+ conditions.push(`${escape(column)} IS NULL`);
45
+ } else {
46
+ const index = params.push(value);
47
+ conditions.push(`${escape(column)}=$${index}`);
48
+ }
49
+ count++;
50
+ }
51
+ return [
52
+ conditions.length ? ` WHERE ${conditions.join(" AND ")}` : "",
53
+ params,
54
+ count
55
+ ];
56
+ }
57
+ function insert(schema, table, query) {
58
+ assertObject(query, "Called INSERT with a non-object");
59
+ const columns = [];
60
+ const placeholders = [];
61
+ const values = [];
62
+ for (const [column, value] of Object.entries(query)) {
63
+ if (value === void 0)
64
+ continue;
65
+ const index = columns.push(`${escape(column)}`);
66
+ placeholders.push(`$${index}`);
67
+ values.push(value);
68
+ }
69
+ return [
70
+ columns.length == 0 ? `INSERT INTO ${escape(schema)}.${escape(table)} DEFAULT VALUES RETURNING *` : `INSERT INTO ${escape(schema)}.${escape(table)} (${columns.join()}) VALUES (${placeholders.join()}) RETURNING *`,
71
+ values
72
+ ];
73
+ }
74
+ function upsert(schema, table, keys, data) {
75
+ assertObject(keys, "Called UPSERT with a non-object for keys");
76
+ assertObject(data, "Called UPSERT with a non-object for data");
77
+ assert(Object.keys(keys).length > 0, "Called UPSERT with no conflict keys");
78
+ assert(Object.keys(data).length > 0, "Called UPSERT with no updateable data");
79
+ const object = { ...keys, ...data, ...keys };
80
+ const columns = [];
81
+ const placeholders = [];
82
+ const values = [];
83
+ for (const [column, value] of Object.entries(object)) {
84
+ if (value === void 0)
85
+ continue;
86
+ const index = columns.push(`${escape(column)}`);
87
+ placeholders.push(`$${index}`);
88
+ values.push(value);
89
+ }
90
+ const conflictKeys = [];
91
+ for (const [column, value] of Object.entries(keys)) {
92
+ if (value !== void 0)
93
+ conflictKeys.push(escape(column));
94
+ }
95
+ const updates = [];
96
+ for (const [column, value] of Object.entries(data)) {
97
+ if (value === void 0)
98
+ continue;
99
+ updates.push(`${escape(column)}=$${updates.length + columns.length + 1}`);
100
+ values.push(value);
101
+ }
102
+ return [
103
+ `INSERT INTO ${escape(schema)}.${escape(table)} (${columns.join()}) VALUES (${placeholders.join()}) ON CONFLICT (${conflictKeys.join(",")}) DO UPDATE SET ${updates.join(",")} RETURNING *`,
104
+ values
105
+ ];
106
+ }
107
+ function select(schema, table, query, sort, offset, limit) {
108
+ if (typeof sort === "string")
109
+ sort = [sort];
110
+ assertObject(query, "Called SELECT with a non-object query");
111
+ assertArray(sort, "Called SELECT with a non-array sort");
112
+ const [conditions, values] = where(query, []);
113
+ const order = [];
114
+ for (const field of sort) {
115
+ if (field.toLowerCase().endsWith(" desc")) {
116
+ order.push(`${escape(field.slice(0, -5))} DESC`);
117
+ } else if (field.toLowerCase().endsWith(" asc")) {
118
+ order.push(`${escape(field.slice(0, -4))} ASC`);
119
+ } else {
120
+ order.push(escape(field));
121
+ }
122
+ }
123
+ const orderby = order.length == 0 ? "" : ` ORDER BY ${order.join(",")}`;
124
+ let sql = `SELECT * FROM ${escape(schema)}.${escape(table)}${conditions}${orderby}`;
125
+ if (offset && offset > 0) {
126
+ sql += ` OFFSET $${values.length + 1}`;
127
+ values.push(Math.floor(offset));
128
+ }
129
+ if (limit && limit > 0) {
130
+ sql += ` LIMIT $${values.length + 1}`;
131
+ values.push(Math.floor(limit));
132
+ }
133
+ return [sql, values];
134
+ }
135
+ function update(schema, table, query, patch) {
136
+ assertObject(query, "Called UPDATE with a non-object query");
137
+ assertObject(patch, "Called UPDATE with a non-object patch");
138
+ const patches = [];
139
+ const values = [];
140
+ for (const [column, value] of Object.entries(patch)) {
141
+ if (value === void 0)
142
+ continue;
143
+ const index = values.push(value);
144
+ patches.push(`${escape(column)}=$${index}`);
145
+ }
146
+ if (patches.length === 0)
147
+ return select(schema, table, query, [], 0, 0);
148
+ const [conditions, , count] = where(query, values);
149
+ assert(count > 0, "Cowardly refusing to run UPDATE with empty query");
150
+ const statement = `UPDATE ${escape(schema)}.${escape(table)} SET ${patches.join()}${conditions} RETURNING *`;
151
+ return [statement, values];
152
+ }
153
+ function del(schema, table, query) {
154
+ assertObject(query, "Called DELETE with a non-object query");
155
+ const [conditions, values, count] = where(query, []);
156
+ assert(count > 0, "Cowardly refusing to run DELETE with empty query");
157
+ return [`DELETE FROM ${escape(schema)}.${escape(table)}${conditions} RETURNING *`, values];
158
+ }
159
+ var ModelImpl = class {
160
+ _connection;
161
+ _schema;
162
+ _table;
163
+ constructor(connection, name) {
164
+ this._connection = connection;
165
+ const [schemaOrTable, maybeTable, ...extra] = name.split(".");
166
+ assert(extra.length === 0, `Invalid table name "${name}"`);
167
+ const [schema, table] = maybeTable ? [schemaOrTable, maybeTable] : ["public", schemaOrTable];
168
+ assert(table, `Invalid table name "${name}"`);
169
+ this._schema = schema || "public";
170
+ this._table = table;
171
+ }
172
+ async create(data) {
173
+ const [sql, params] = insert(this._schema, this._table, data);
174
+ const result = await this._connection.query(sql, params);
175
+ return result.rows[0];
176
+ }
177
+ async upsert(keys, data) {
178
+ const [sql, params] = upsert(this._schema, this._table, keys, data);
179
+ const result = await this._connection.query(sql, params);
180
+ return result.rows[0];
181
+ }
182
+ async read(query = {}, sort = [], offset = 0, limit = 0) {
183
+ const [sql, params] = select(this._schema, this._table, query, sort, offset, limit);
184
+ const result = await this._connection.query(sql, params);
185
+ return result.rows;
186
+ }
187
+ async find(query, sort) {
188
+ const result = await this.read(query, sort, 0, 1);
189
+ return result[0];
190
+ }
191
+ async update(query, patch) {
192
+ const [sql, params] = update(this._schema, this._table, query, patch);
193
+ const result = await this._connection.query(sql, params);
194
+ return result.rows;
195
+ }
196
+ async delete(query) {
197
+ const [sql, params] = del(this._schema, this._table, query);
198
+ const result = await this._connection.query(sql, params);
199
+ return result.rowCount;
200
+ }
201
+ };
202
+ function escape(str) {
203
+ return `"${str.replaceAll('"', '""').trim()}"`;
204
+ }
205
+ var Model = ModelImpl;
206
+ // Annotate the CommonJS export names for ESM import in node:
207
+ 0 && (module.exports = {
208
+ Model,
209
+ escape
210
+ });
211
+ //# sourceMappingURL=model.cjs.map
@@ -0,0 +1,6 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/model.ts"],
4
+ "mappings": "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",
5
+ "names": []
6
+ }
@@ -0,0 +1,95 @@
1
+ import type { PGQueryable } from '@juit/pgproxy-client';
2
+ type SimplifyIntersection<T> = {
3
+ [K in keyof T]: T[K];
4
+ };
5
+ type OnlyStrings<T> = T extends string ? T : never;
6
+ /** The definition of a column */
7
+ export interface ColumnDefinition<T = any> {
8
+ /** The TypeScript type of the column (from the type parser) */
9
+ type: T;
10
+ /** Whether the column is _nulable_ or not */
11
+ isNullable?: boolean;
12
+ /** Whether the column _specifies a default value_ or not */
13
+ hasDefault?: boolean;
14
+ }
15
+ /** Infer the TypeScript type suitable for an `INSERT` in a table */
16
+ export type InferInsertType<Table extends Record<string, ColumnDefinition>> = SimplifyIntersection<{
17
+ [Column in keyof Table as Column extends string ? Table[Column]['isNullable'] extends true ? Column : Table[Column]['hasDefault'] extends true ? Column : never : never]?: Table[Column]['isNullable'] extends true ? Table[Column]['type'] | null : Table[Column]['type'];
18
+ } & {
19
+ [Column in keyof Table as Column extends string ? Table[Column]['isNullable'] extends true ? never : Table[Column]['hasDefault'] extends true ? never : Column : never]-?: Table[Column]['isNullable'] extends true ? Table[Column]['type'] | null : Table[Column]['type'];
20
+ }>;
21
+ /** Infer the TypeScript type suitable for a `SELECT` from a table */
22
+ export type InferSelectType<Table extends Record<string, ColumnDefinition>> = {
23
+ [Column in keyof Table as Column extends string ? Column : never]-?: Table[Column]['isNullable'] extends true ? Table[Column]['type'] | null : Table[Column]['type'];
24
+ };
25
+ /** Infer the TypeScript type suitable for a `UPDATE` in a table */
26
+ export type InferUpdateType<Table extends Record<string, ColumnDefinition>> = {
27
+ [Column in keyof Table as Column extends string ? Column : never]?: Table[Column]['isNullable'] extends true ? Table[Column]['type'] | null : Table[Column]['type'];
28
+ };
29
+ /** Infer the available sort values for a table (as required by `ORDER BY`) */
30
+ export type InferSort<Table extends Record<string, ColumnDefinition>> = `${OnlyStrings<keyof Table>}${' ASC' | ' asc' | ' DESC' | ' desc' | ''}`;
31
+ /** The model interface defines a CRUD interface to PosgreSQL tables */
32
+ export interface Model<Table extends Record<string, ColumnDefinition>> {
33
+ /**
34
+ * Create a row in the table.
35
+ *
36
+ * @param data - The data to insert in the table
37
+ * @returns A record containing all colums from the table (including defaults)
38
+ */
39
+ create(data: InferInsertType<Table>): Promise<InferSelectType<Table>>;
40
+ /**
41
+ * Insert a row in the database or update its contents on conflict.
42
+ *
43
+ * @param keys - The data uniquely identifying the row to upsert (primary key)
44
+ * @param data - The data to associate with the given key (all extra columns)
45
+ * @returns A record containing all colums from the table (including defaults)
46
+ */
47
+ upsert<K extends InferUpdateType<Table>>(keys: K, data: Omit<InferInsertType<Table>, keyof K>): Promise<InferSelectType<Table>>;
48
+ /**
49
+ * Read all rows in the table associated with the specified query
50
+ *
51
+ * @param query - The columns whose values need to be queried (for equality)
52
+ * @param sort - Any sort criteria to order the data
53
+ * @param offset - The offset of the results to return
54
+ * @param length - The maximum number of rows to return
55
+ * @returns An array of records containing all columns from the table
56
+ */
57
+ read(query?: InferUpdateType<Table>, sort?: InferSort<Table> | InferSort<Table>[], offset?: number, limit?: number): Promise<InferSelectType<Table>[]>;
58
+ /**
59
+ * Find the _first_ rows in the table associated with the specified query
60
+ *
61
+ * @param query - The columns whose values need to be queried (for equality)
62
+ * @param sort - Any sort criteria to order the data
63
+ * @returns The first records matching the query or `undefined`
64
+ */
65
+ find(query?: InferUpdateType<Table>, sort?: InferSort<Table> | InferSort<Table>[]): Promise<InferSelectType<Table> | undefined>;
66
+ /**
67
+ * Update all rows in the table matching the specified query.
68
+ *
69
+ * This method _will fail_ when query is the empty object `{}` as we cowardly
70
+ * refuse to update all records in a table (by design).
71
+ *
72
+ * @param query - The columns whose values need to be queried (for equality)
73
+ * @param patch - The updated data to persist in the table
74
+ * @returns An array of updated records containing all columns from the table
75
+ */
76
+ update(query: InferUpdateType<Table>, patch: InferUpdateType<Table>): Promise<InferSelectType<Table>[]>;
77
+ /**
78
+ * Delete all rows in the table matching the specified query.
79
+ *
80
+ * This method _will fail_ when query is the empty object `{}` as we cowardly
81
+ * refuse to delete all records in a table (by design).
82
+ *
83
+ * @param query - The columns whose values need to be queried (for equality)
84
+ * @returns The number of rows deleted
85
+ */
86
+ delete(query: InferUpdateType<Table>): Promise<number>;
87
+ }
88
+ /** Constructor for model instances */
89
+ export interface ModelConstructor {
90
+ new <Schema extends Record<string, ColumnDefinition>>(queryable: PGQueryable, table: string): Model<Schema>;
91
+ }
92
+ /** Escape a PostgreSQL identifier (table, column, ... names) */
93
+ export declare function escape(str: string): string;
94
+ export declare const Model: ModelConstructor;
95
+ export {};