@juit/pgproxy-model 0.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 +12 -0
- package/dist/index.cjs +19 -0
- package/dist/index.cjs.map +6 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.mjs +1 -0
- package/dist/index.mjs.map +6 -0
- package/dist/model.cjs +19 -0
- package/dist/model.cjs.map +6 -0
- package/dist/model.d.ts +34 -0
- package/dist/model.mjs +1 -0
- package/dist/model.mjs.map +6 -0
- package/dist/search.cjs +19 -0
- package/dist/search.cjs.map +6 -0
- package/dist/search.d.ts +131 -0
- package/dist/search.mjs +1 -0
- package/dist/search.mjs.map +6 -0
- package/dist/utils.cjs +19 -0
- package/dist/utils.cjs.map +6 -0
- package/dist/utils.d.ts +6 -0
- package/dist/utils.mjs +1 -0
- package/dist/utils.mjs.map +6 -0
- package/package.json +45 -0
- package/src/index.ts +2 -0
- package/src/model.ts +77 -0
- package/src/search.ts +194 -0
- package/src/utils.ts +4 -0
package/README.md
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# PostgreSQL Proxy Persister Model Types
|
|
2
|
+
|
|
3
|
+
This package exports _only the types_ required by the [PGProxy Persister
|
|
4
|
+
interface](https://github.com/juitnow/juit-pgproxy/blob/main/workspaces/persister/README.md).
|
|
5
|
+
|
|
6
|
+
This simplifies type management allowing **consumers** of libraries and APIs
|
|
7
|
+
using Persister to ship this (and their inferred types) as a simple dependency
|
|
8
|
+
without bringing in any extra code.
|
|
9
|
+
|
|
10
|
+
* [PGProxy](https://github.com/juitnow/juit-pgproxy/blob/main/README.md)
|
|
11
|
+
* [Copyright Notice](https://github.com/juitnow/juit-pgproxy/blob/main/NOTICE.md)
|
|
12
|
+
* [License](https://github.com/juitnow/juit-pgproxy/blob/main/NOTICE.md)
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
15
|
+
|
|
16
|
+
// index.ts
|
|
17
|
+
var index_exports = {};
|
|
18
|
+
module.exports = __toCommonJS(index_exports);
|
|
19
|
+
//# sourceMappingURL=index.cjs.map
|
package/dist/index.d.ts
ADDED
package/dist/index.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//# sourceMappingURL=index.mjs.map
|
package/dist/model.cjs
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
15
|
+
|
|
16
|
+
// model.ts
|
|
17
|
+
var model_exports = {};
|
|
18
|
+
module.exports = __toCommonJS(model_exports);
|
|
19
|
+
//# sourceMappingURL=model.cjs.map
|
package/dist/model.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { OnlyStrings, Prettify } from './utils';
|
|
2
|
+
/** The definition of a column */
|
|
3
|
+
export interface ColumnDefinition<T = any> {
|
|
4
|
+
/** The TypeScript type of the column (from the type parser) */
|
|
5
|
+
type: T;
|
|
6
|
+
/** Whether the column is _generated_ or not */
|
|
7
|
+
isGenerated?: boolean;
|
|
8
|
+
/** Whether the column is _nullable_ or not */
|
|
9
|
+
isNullable?: boolean;
|
|
10
|
+
/** Whether the column _specifies a default value_ or not */
|
|
11
|
+
hasDefault?: boolean;
|
|
12
|
+
}
|
|
13
|
+
/** Infer the TypeScript type suitable for an `INSERT` in a table */
|
|
14
|
+
export type InferInsertType<Table extends Record<string, ColumnDefinition>> = Prettify<{
|
|
15
|
+
[Column in keyof Table as Column extends string ? Table[Column]['isGenerated'] extends true ? never : 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'];
|
|
16
|
+
} & {
|
|
17
|
+
[Column in keyof Table as Column extends string ? Table[Column]['isGenerated'] extends true ? never : 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'];
|
|
18
|
+
}>;
|
|
19
|
+
/** Infer the TypeScript type suitable for a `SELECT` from a table */
|
|
20
|
+
export type InferSelectType<Table extends Record<string, ColumnDefinition>> = {
|
|
21
|
+
[Column in keyof Table as Column extends string ? Column : never]-?: (Table[Column]['isNullable'] extends true ? Table[Column]['type'] | null : Table[Column]['type']) & (Table[Column] extends {
|
|
22
|
+
branding: infer Brand;
|
|
23
|
+
} ? Brand : unknown);
|
|
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 ? Table[Column]['isGenerated'] extends true ? never : Column : never]?: Table[Column]['isNullable'] extends true ? Table[Column]['type'] | null : Table[Column]['type'];
|
|
28
|
+
};
|
|
29
|
+
/** Infer the TypeScript type used for querying records */
|
|
30
|
+
export type InferQueryType<Table extends Record<string, ColumnDefinition>> = {
|
|
31
|
+
[Column in keyof Table as Column extends string ? Column : never]?: Table[Column]['isNullable'] extends true ? Table[Column]['type'] | null : Table[Column]['type'];
|
|
32
|
+
};
|
|
33
|
+
/** Infer the available sort values for a table (as required by `ORDER BY`) */
|
|
34
|
+
export type InferSort<Table extends Record<string, ColumnDefinition>> = `${OnlyStrings<keyof Table>}${' ASC' | ' asc' | ' DESC' | ' desc' | ''}`;
|
package/dist/model.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//# sourceMappingURL=model.mjs.map
|
package/dist/search.cjs
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
15
|
+
|
|
16
|
+
// search.ts
|
|
17
|
+
var search_exports = {};
|
|
18
|
+
module.exports = __toCommonJS(search_exports);
|
|
19
|
+
//# sourceMappingURL=search.cjs.map
|
package/dist/search.d.ts
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import type { ColumnDefinition, InferSelectType } from './model';
|
|
2
|
+
import type { Prettify } from './utils';
|
|
3
|
+
/** Simple type to extract joins with a specified sortcolumn */
|
|
4
|
+
type SortableJoins<Joins> = keyof {
|
|
5
|
+
[key in keyof Joins as Joins[key] extends {
|
|
6
|
+
sortColumn: string;
|
|
7
|
+
} ? key : never]: undefined;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Definition for a simple (straight) join in a {@link Search}
|
|
11
|
+
*/
|
|
12
|
+
export interface SearchJoin<Schema> {
|
|
13
|
+
/**
|
|
14
|
+
* The column in _the search table_ (passed to the constructor of
|
|
15
|
+
* {@link Search}) referencing the specified `refTable` (defined here).
|
|
16
|
+
*
|
|
17
|
+
* ```sql
|
|
18
|
+
* ... LEFT JOIN "refTable" ON "table"."column" = "refTable"."refColumn"
|
|
19
|
+
* ^^^^^^
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
column: string;
|
|
23
|
+
/**
|
|
24
|
+
* The name of the table to _left join_.
|
|
25
|
+
*
|
|
26
|
+
* ```sql
|
|
27
|
+
* ... LEFT JOIN "refTable" ON "table"."column" = "refTable"."refColumn"
|
|
28
|
+
* ^^^^^^^^ ^^^^^^^^
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
refTable: string & keyof Schema;
|
|
32
|
+
/**
|
|
33
|
+
* The column in the `refTable` referenced by the _the search table_.
|
|
34
|
+
*
|
|
35
|
+
* ```sql
|
|
36
|
+
* ... LEFT JOIN "refTable" ON "table"."column" = "refTable"."refColumn"
|
|
37
|
+
* ^^^^^^^^^
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
refColumn: string;
|
|
41
|
+
/**
|
|
42
|
+
* The column in the referenced table to use as default sort column, when
|
|
43
|
+
* sorting by this join.
|
|
44
|
+
*/
|
|
45
|
+
sortColumn?: string;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Definition for joins in a {@link Search}
|
|
49
|
+
*
|
|
50
|
+
* Each key is the name of the join as it will appear in the results, and the
|
|
51
|
+
* value defines how to perform the join.
|
|
52
|
+
*
|
|
53
|
+
* See {@link StraightJoin} and {@link LinkedJoin} for details on the fields.
|
|
54
|
+
*/
|
|
55
|
+
export interface SearchJoins<Schema> {
|
|
56
|
+
[key: string]: SearchJoin<Schema>;
|
|
57
|
+
}
|
|
58
|
+
/** Internal interface defining operators available to *single values* */
|
|
59
|
+
interface ValueSearchFilter<Schema, Table extends string & keyof Schema> {
|
|
60
|
+
name: string & keyof Schema[Table];
|
|
61
|
+
field?: string;
|
|
62
|
+
op?: '=' | '!=' | '>' | '>=' | '<' | '<=' | '~' | 'like' | 'ilike';
|
|
63
|
+
value: string | number | Date | boolean | null;
|
|
64
|
+
}
|
|
65
|
+
/** Internal interface defining operators available to *array values* */
|
|
66
|
+
interface ArraySearchFilter<Schema, Table extends string & keyof Schema> {
|
|
67
|
+
name: string & keyof Schema[Table];
|
|
68
|
+
field?: string;
|
|
69
|
+
op: 'in' | 'not in';
|
|
70
|
+
value: (string | number | Date | boolean | null)[];
|
|
71
|
+
}
|
|
72
|
+
/** Internal interface defining operators available to *json values* */
|
|
73
|
+
interface JsonSearchFilter<Schema, Table extends string & keyof Schema> {
|
|
74
|
+
name: string & keyof Schema[Table];
|
|
75
|
+
field?: never;
|
|
76
|
+
op: '@>' | '<@';
|
|
77
|
+
value: any;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* A filter for a search that matches a single value
|
|
81
|
+
*
|
|
82
|
+
* - `name` is the column name to filter on
|
|
83
|
+
* - `field` is a field to filter on when the column is a complex type (JSONB)
|
|
84
|
+
* - `op` is the operator to use for the filter (default: `=`)
|
|
85
|
+
* - `value` is the value to filter for
|
|
86
|
+
*
|
|
87
|
+
* All operators are defined as per PostgreSQL documentation, with few notable
|
|
88
|
+
* exceptions:
|
|
89
|
+
*
|
|
90
|
+
* - `~` is an alias to the `ilike` operator
|
|
91
|
+
* - `in` and `not in` are used to match a value against an array of possible
|
|
92
|
+
* values using the `... = ANY(...)` or `... != ALL(...)` constructs
|
|
93
|
+
* - `@>` and `<@` will accept single values as well as arrays.
|
|
94
|
+
* - `!=` and `=` will use the PostgreSQL `IS (NOT) DISTINCT FROM` semantics
|
|
95
|
+
* to properly handle `NULL` comparisons.
|
|
96
|
+
*/
|
|
97
|
+
export type SearchFilter<Schema, Table extends string & keyof Schema> = Prettify<ValueSearchFilter<Schema, Table>> | Prettify<ArraySearchFilter<Schema, Table>> | Prettify<JsonSearchFilter<Schema, Table>>;
|
|
98
|
+
/**
|
|
99
|
+
* Base interface for querying results via our {@link Search}.
|
|
100
|
+
*/
|
|
101
|
+
export type SearchQuery<Schema, Table extends string & keyof Schema, Joins extends SearchJoins<Schema>> = Prettify<{
|
|
102
|
+
/** An optional set of filters to apply */
|
|
103
|
+
filters?: Prettify<SearchFilter<Schema, Table>[]>;
|
|
104
|
+
/** An optional column to sort by */
|
|
105
|
+
sort?: string & (keyof Schema[Table] | SortableJoins<Joins>);
|
|
106
|
+
/** The order to sort by (if `sort` is specified, default: 'asc') */
|
|
107
|
+
order?: 'asc' | 'desc';
|
|
108
|
+
/** An optional full-text search query, available for full-text search */
|
|
109
|
+
q?: string;
|
|
110
|
+
}>;
|
|
111
|
+
/**
|
|
112
|
+
* Full options for querying a limited set of results via our {@link Search}.
|
|
113
|
+
*/
|
|
114
|
+
export type SearchOptions<Schema, Table extends string & keyof Schema, Joins extends SearchJoins<Schema>> = Prettify<SearchQuery<Schema, Table, Joins> & {
|
|
115
|
+
/** Offset to start returning rows from (default: 0) */
|
|
116
|
+
offset?: number;
|
|
117
|
+
/** Maximum number of rows to return (default: 20, unlimited if 0) */
|
|
118
|
+
limit?: number;
|
|
119
|
+
}>;
|
|
120
|
+
/** A single search result row (with joins) */
|
|
121
|
+
export type SearchResult<Schema, Table extends string & keyof Schema, Joins extends SearchJoins<Schema> = {}> = Prettify<Schema[Table] extends Record<string, ColumnDefinition> ? InferSelectType<Schema[Table]> & {
|
|
122
|
+
[key in keyof Joins]: Joins[key]['refTable'] extends keyof Schema ? Schema[Joins[key]['refTable']] extends Record<string, ColumnDefinition> ? Schema[Table][Joins[key]['column']]['isNullable'] extends true ? Prettify<InferSelectType<Schema[Joins[key]['refTable']]>> | null : Prettify<InferSelectType<Schema[Joins[key]['refTable']]>> : unknown : unknown;
|
|
123
|
+
} : never>;
|
|
124
|
+
/** What's being returned by our `search` */
|
|
125
|
+
export interface SearchResults<Schema, Table extends string & keyof Schema, Joins extends SearchJoins<Schema> = {}> {
|
|
126
|
+
/** The total length of all available results (without offset or limit) */
|
|
127
|
+
total: number;
|
|
128
|
+
/** The lines queried (truncated by offset and limit) */
|
|
129
|
+
rows: SearchResult<Schema, Table, Joins>[];
|
|
130
|
+
}
|
|
131
|
+
export {};
|
package/dist/search.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//# sourceMappingURL=search.mjs.map
|
package/dist/utils.cjs
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
15
|
+
|
|
16
|
+
// utils.ts
|
|
17
|
+
var utils_exports = {};
|
|
18
|
+
module.exports = __toCommonJS(utils_exports);
|
|
19
|
+
//# sourceMappingURL=utils.cjs.map
|
package/dist/utils.d.ts
ADDED
package/dist/utils.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//# sourceMappingURL=utils.mjs.map
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@juit/pgproxy-model",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"main": "./dist/index.cjs",
|
|
5
|
+
"module": "./dist/index.mjs",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"require": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"default": "./dist/index.cjs"
|
|
12
|
+
},
|
|
13
|
+
"import": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"default": "./dist/index.mjs"
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"scripts": {},
|
|
20
|
+
"author": "Juit Developers <developers@juit.com>",
|
|
21
|
+
"license": "Apache-2.0",
|
|
22
|
+
"repository": {
|
|
23
|
+
"type": "git",
|
|
24
|
+
"url": "git+ssh://git@github.com/juitnow/juit-pgproxy.git"
|
|
25
|
+
},
|
|
26
|
+
"keywords": [
|
|
27
|
+
"database",
|
|
28
|
+
"pg",
|
|
29
|
+
"pool",
|
|
30
|
+
"postgres",
|
|
31
|
+
"proxy"
|
|
32
|
+
],
|
|
33
|
+
"bugs": {
|
|
34
|
+
"url": "https://github.com/juitnow/juit-pgproxy/issues"
|
|
35
|
+
},
|
|
36
|
+
"homepage": "https://github.com/juitnow/juit-pgproxy#readme",
|
|
37
|
+
"directories": {
|
|
38
|
+
"test": "test"
|
|
39
|
+
},
|
|
40
|
+
"files": [
|
|
41
|
+
"*.md",
|
|
42
|
+
"dist/",
|
|
43
|
+
"src/"
|
|
44
|
+
]
|
|
45
|
+
}
|
package/src/index.ts
ADDED
package/src/model.ts
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { OnlyStrings, Prettify } from './utils'
|
|
2
|
+
|
|
3
|
+
/* ========================================================================== *
|
|
4
|
+
* TYPE INFERENCE: FROM SCHEMA->TABLE->COLUMN->... TO JS TYPES *
|
|
5
|
+
* ========================================================================== */
|
|
6
|
+
|
|
7
|
+
/** The definition of a column */
|
|
8
|
+
export interface ColumnDefinition<T = any> {
|
|
9
|
+
/** The TypeScript type of the column (from the type parser) */
|
|
10
|
+
type: T,
|
|
11
|
+
/** Whether the column is _generated_ or not */
|
|
12
|
+
isGenerated?: boolean,
|
|
13
|
+
/** Whether the column is _nullable_ or not */
|
|
14
|
+
isNullable?: boolean,
|
|
15
|
+
/** Whether the column _specifies a default value_ or not */
|
|
16
|
+
hasDefault?: boolean,
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Infer the TypeScript type suitable for an `INSERT` in a table */
|
|
20
|
+
export type InferInsertType<Table extends Record<string, ColumnDefinition>> = Prettify<{
|
|
21
|
+
/* First part: all nullable or defaulted columns are optional */
|
|
22
|
+
[ Column in keyof Table as Column extends string
|
|
23
|
+
? Table[Column]['isGenerated'] extends true ? never
|
|
24
|
+
: Table[Column]['isNullable'] extends true ? Column
|
|
25
|
+
: Table[Column]['hasDefault'] extends true ? Column
|
|
26
|
+
: never
|
|
27
|
+
: never
|
|
28
|
+
] ? :
|
|
29
|
+
Table[Column]['isNullable'] extends true
|
|
30
|
+
? Table[Column]['type'] | null
|
|
31
|
+
: Table[Column]['type']
|
|
32
|
+
} & {
|
|
33
|
+
/* Second part: all non-nullable or non-defaulted columns are required */
|
|
34
|
+
[ Column in keyof Table as Column extends string
|
|
35
|
+
? Table[Column]['isGenerated'] extends true ? never
|
|
36
|
+
: Table[Column]['isNullable'] extends true ? never
|
|
37
|
+
: Table[Column]['hasDefault'] extends true ? never
|
|
38
|
+
: Column
|
|
39
|
+
: never
|
|
40
|
+
] -? :
|
|
41
|
+
Table[Column]['isNullable'] extends true
|
|
42
|
+
? Table[Column]['type'] | null
|
|
43
|
+
: Table[Column]['type']
|
|
44
|
+
}>
|
|
45
|
+
|
|
46
|
+
/** Infer the TypeScript type suitable for a `SELECT` from a table */
|
|
47
|
+
export type InferSelectType<Table extends Record<string, ColumnDefinition>> = {
|
|
48
|
+
[ Column in keyof Table as Column extends string ? Column : never ] -? :
|
|
49
|
+
( Table[Column]['isNullable'] extends true
|
|
50
|
+
? Table[Column]['type'] | null
|
|
51
|
+
: Table[Column]['type']
|
|
52
|
+
) & ( Table[Column] extends { branding: infer Brand } ? Brand : unknown )
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Infer the TypeScript type suitable for a `UPDATE` in a table */
|
|
56
|
+
export type InferUpdateType<Table extends Record<string, ColumnDefinition>> = {
|
|
57
|
+
[ Column in keyof Table as Column extends string
|
|
58
|
+
? Table[Column]['isGenerated'] extends true ? never
|
|
59
|
+
: Column
|
|
60
|
+
: never
|
|
61
|
+
] ? :
|
|
62
|
+
Table[Column]['isNullable'] extends true
|
|
63
|
+
? Table[Column]['type'] | null
|
|
64
|
+
: Table[Column]['type']
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Infer the TypeScript type used for querying records */
|
|
68
|
+
export type InferQueryType<Table extends Record<string, ColumnDefinition>> ={
|
|
69
|
+
[ Column in keyof Table as Column extends string ? Column : never ] ? :
|
|
70
|
+
Table[Column]['isNullable'] extends true
|
|
71
|
+
? Table[Column]['type'] | null
|
|
72
|
+
: Table[Column]['type']
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Infer the available sort values for a table (as required by `ORDER BY`) */
|
|
76
|
+
export type InferSort<Table extends Record<string, ColumnDefinition>> =
|
|
77
|
+
`${OnlyStrings<keyof Table>}${' ASC' | ' asc' | ' DESC' | ' desc' | ''}`
|
package/src/search.ts
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import type { ColumnDefinition, InferSelectType } from './model'
|
|
2
|
+
import type { Prettify } from './utils'
|
|
3
|
+
|
|
4
|
+
/** Simple type to extract joins with a specified sortcolumn */
|
|
5
|
+
type SortableJoins<Joins> = keyof {
|
|
6
|
+
[ key in keyof Joins as Joins[key] extends { sortColumn: string } ? key : never ] : undefined
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/* ========================================================================== *
|
|
10
|
+
* JOINS *
|
|
11
|
+
* ========================================================================== */
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Definition for a simple (straight) join in a {@link Search}
|
|
15
|
+
*/
|
|
16
|
+
export interface SearchJoin<Schema> {
|
|
17
|
+
/**
|
|
18
|
+
* The column in _the search table_ (passed to the constructor of
|
|
19
|
+
* {@link Search}) referencing the specified `refTable` (defined here).
|
|
20
|
+
*
|
|
21
|
+
* ```sql
|
|
22
|
+
* ... LEFT JOIN "refTable" ON "table"."column" = "refTable"."refColumn"
|
|
23
|
+
* ^^^^^^
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
column: string
|
|
27
|
+
/**
|
|
28
|
+
* The name of the table to _left join_.
|
|
29
|
+
*
|
|
30
|
+
* ```sql
|
|
31
|
+
* ... LEFT JOIN "refTable" ON "table"."column" = "refTable"."refColumn"
|
|
32
|
+
* ^^^^^^^^ ^^^^^^^^
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
refTable: string & keyof Schema
|
|
36
|
+
/**
|
|
37
|
+
* The column in the `refTable` referenced by the _the search table_.
|
|
38
|
+
*
|
|
39
|
+
* ```sql
|
|
40
|
+
* ... LEFT JOIN "refTable" ON "table"."column" = "refTable"."refColumn"
|
|
41
|
+
* ^^^^^^^^^
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
refColumn: string
|
|
45
|
+
/**
|
|
46
|
+
* The column in the referenced table to use as default sort column, when
|
|
47
|
+
* sorting by this join.
|
|
48
|
+
*/
|
|
49
|
+
sortColumn?: string
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Definition for joins in a {@link Search}
|
|
54
|
+
*
|
|
55
|
+
* Each key is the name of the join as it will appear in the results, and the
|
|
56
|
+
* value defines how to perform the join.
|
|
57
|
+
*
|
|
58
|
+
* See {@link StraightJoin} and {@link LinkedJoin} for details on the fields.
|
|
59
|
+
*/
|
|
60
|
+
export interface SearchJoins<Schema> {
|
|
61
|
+
[ key: string ]: SearchJoin<Schema>
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/* ========================================================================== *
|
|
65
|
+
* SEARCH OPTIONS *
|
|
66
|
+
* ========================================================================== */
|
|
67
|
+
|
|
68
|
+
/** Internal interface defining operators available to *single values* */
|
|
69
|
+
interface ValueSearchFilter<
|
|
70
|
+
Schema,
|
|
71
|
+
Table extends string & keyof Schema,
|
|
72
|
+
> {
|
|
73
|
+
name: string & keyof Schema[Table]
|
|
74
|
+
field?: string
|
|
75
|
+
op?: '=' | '!=' | '>' | '>=' | '<' | '<=' | '~' | 'like' | 'ilike'
|
|
76
|
+
value: string | number | Date | boolean | null
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Internal interface defining operators available to *array values* */
|
|
80
|
+
interface ArraySearchFilter<
|
|
81
|
+
Schema,
|
|
82
|
+
Table extends string & keyof Schema,
|
|
83
|
+
> {
|
|
84
|
+
name: string & keyof Schema[Table]
|
|
85
|
+
field?: string
|
|
86
|
+
op: 'in' | 'not in'
|
|
87
|
+
value: (string | number | Date | boolean | null)[]
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Internal interface defining operators available to *json values* */
|
|
91
|
+
interface JsonSearchFilter<
|
|
92
|
+
Schema,
|
|
93
|
+
Table extends string & keyof Schema,
|
|
94
|
+
> {
|
|
95
|
+
name: string & keyof Schema[Table]
|
|
96
|
+
field?: never
|
|
97
|
+
op: '@>' | '<@'
|
|
98
|
+
value: any
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* A filter for a search that matches a single value
|
|
103
|
+
*
|
|
104
|
+
* - `name` is the column name to filter on
|
|
105
|
+
* - `field` is a field to filter on when the column is a complex type (JSONB)
|
|
106
|
+
* - `op` is the operator to use for the filter (default: `=`)
|
|
107
|
+
* - `value` is the value to filter for
|
|
108
|
+
*
|
|
109
|
+
* All operators are defined as per PostgreSQL documentation, with few notable
|
|
110
|
+
* exceptions:
|
|
111
|
+
*
|
|
112
|
+
* - `~` is an alias to the `ilike` operator
|
|
113
|
+
* - `in` and `not in` are used to match a value against an array of possible
|
|
114
|
+
* values using the `... = ANY(...)` or `... != ALL(...)` constructs
|
|
115
|
+
* - `@>` and `<@` will accept single values as well as arrays.
|
|
116
|
+
* - `!=` and `=` will use the PostgreSQL `IS (NOT) DISTINCT FROM` semantics
|
|
117
|
+
* to properly handle `NULL` comparisons.
|
|
118
|
+
*/
|
|
119
|
+
export type SearchFilter<
|
|
120
|
+
Schema,
|
|
121
|
+
Table extends string & keyof Schema,
|
|
122
|
+
> = Prettify<ValueSearchFilter<Schema, Table>>
|
|
123
|
+
| Prettify<ArraySearchFilter<Schema, Table>>
|
|
124
|
+
| Prettify<JsonSearchFilter<Schema, Table>>
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Base interface for querying results via our {@link Search}.
|
|
128
|
+
*/
|
|
129
|
+
export type SearchQuery<
|
|
130
|
+
Schema,
|
|
131
|
+
Table extends string & keyof Schema,
|
|
132
|
+
Joins extends SearchJoins<Schema>,
|
|
133
|
+
> = Prettify<{
|
|
134
|
+
/** An optional set of filters to apply */
|
|
135
|
+
filters?: Prettify<SearchFilter<Schema, Table>[]>
|
|
136
|
+
/** An optional column to sort by */
|
|
137
|
+
sort?: string & (keyof Schema[Table] | SortableJoins<Joins>)
|
|
138
|
+
/** The order to sort by (if `sort` is specified, default: 'asc') */
|
|
139
|
+
order?: 'asc' | 'desc'
|
|
140
|
+
/** An optional full-text search query, available for full-text search */
|
|
141
|
+
q?: string
|
|
142
|
+
}>
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Full options for querying a limited set of results via our {@link Search}.
|
|
146
|
+
*/
|
|
147
|
+
export type SearchOptions<
|
|
148
|
+
Schema,
|
|
149
|
+
Table extends string & keyof Schema,
|
|
150
|
+
Joins extends SearchJoins<Schema>,
|
|
151
|
+
> = Prettify<SearchQuery<Schema, Table, Joins> & {
|
|
152
|
+
/** Offset to start returning rows from (default: 0) */
|
|
153
|
+
offset?: number
|
|
154
|
+
/** Maximum number of rows to return (default: 20, unlimited if 0) */
|
|
155
|
+
limit?: number
|
|
156
|
+
}>
|
|
157
|
+
|
|
158
|
+
/* ========================================================================== *
|
|
159
|
+
* SEARCH RESULTS *
|
|
160
|
+
* ========================================================================== */
|
|
161
|
+
|
|
162
|
+
/** A single search result row (with joins) */
|
|
163
|
+
export type SearchResult<
|
|
164
|
+
Schema,
|
|
165
|
+
Table extends string & keyof Schema,
|
|
166
|
+
Joins extends SearchJoins<Schema> = {},
|
|
167
|
+
> = Prettify<
|
|
168
|
+
Schema[Table] extends Record<string, ColumnDefinition> ?
|
|
169
|
+
// This is the main table's column field
|
|
170
|
+
InferSelectType<Schema[Table]> & {
|
|
171
|
+
// For each join, add a field with the joined table's inferred type
|
|
172
|
+
[ key in keyof Joins ] : Joins[key]['refTable'] extends keyof Schema ?
|
|
173
|
+
// If the column referencing this join is nullable, the result can be null
|
|
174
|
+
Schema[Joins[key]['refTable']] extends Record<string, ColumnDefinition> ?
|
|
175
|
+
Schema[Table][Joins[key]['column']]['isNullable'] extends true ?
|
|
176
|
+
Prettify<InferSelectType<Schema[Joins[key]['refTable']]>> | null :
|
|
177
|
+
Prettify<InferSelectType<Schema[Joins[key]['refTable']]>> :
|
|
178
|
+
// If the joined table isn't a column def, we can't infer anything
|
|
179
|
+
unknown :
|
|
180
|
+
// If the table doesn't exist in the schema, we can't infer anything
|
|
181
|
+
unknown
|
|
182
|
+
} : never>
|
|
183
|
+
|
|
184
|
+
/** What's being returned by our `search` */
|
|
185
|
+
export interface SearchResults<
|
|
186
|
+
Schema,
|
|
187
|
+
Table extends string & keyof Schema,
|
|
188
|
+
Joins extends SearchJoins<Schema> = {},
|
|
189
|
+
> {
|
|
190
|
+
/** The total length of all available results (without offset or limit) */
|
|
191
|
+
total: number
|
|
192
|
+
/** The lines queried (truncated by offset and limit) */
|
|
193
|
+
rows: SearchResult<Schema, Table, Joins>[]
|
|
194
|
+
}
|
package/src/utils.ts
ADDED