@zeroxsolutions/db 9.5.0 → 10.1.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/CHANGELOG.md +57 -22
- package/README.md +149 -180
- package/dist/base/d1/entity.d.ts.map +1 -1
- package/dist/base/d1/outbox.d.ts.map +1 -1
- package/dist/base/d1/outbox.js +1 -3
- package/dist/base/d1/repository.d.ts +76 -66
- package/dist/base/d1/repository.d.ts.map +1 -1
- package/dist/base/d1/repository.js +134 -122
- package/dist/base/d1/unit-of-work.d.ts.map +1 -1
- package/dist/base/postgres/entity.d.ts.map +1 -1
- package/dist/base/postgres/outbox.d.ts.map +1 -1
- package/dist/base/postgres/outbox.js +1 -3
- package/dist/base/postgres/repository.d.ts +109 -86
- package/dist/base/postgres/repository.d.ts.map +1 -1
- package/dist/base/postgres/repository.js +182 -143
- package/dist/base/postgres/unit-of-work.d.ts.map +1 -1
- package/dist/base/read-engine.d.ts +48 -0
- package/dist/base/read-engine.d.ts.map +1 -0
- package/dist/base/read-engine.js +94 -0
- package/dist/utils/filter.d.ts +44 -59
- package/dist/utils/filter.d.ts.map +1 -1
- package/dist/utils/filter.js +51 -85
- package/dist/utils/index.d.ts +3 -5
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +3 -5
- package/dist/utils/read.d.ts +88 -0
- package/dist/utils/read.d.ts.map +1 -0
- package/dist/utils/read.js +124 -0
- package/dist/utils/reads.d.ts +30 -0
- package/dist/utils/reads.d.ts.map +1 -0
- package/dist/utils/reads.js +26 -0
- package/dist/utils/relationships.d.ts +9 -6
- package/dist/utils/relationships.d.ts.map +1 -1
- package/dist/utils/relationships.js +26 -8
- package/package.json +7 -2
- package/dist/utils/assert-queryable-fields.d.ts +0 -27
- package/dist/utils/assert-queryable-fields.d.ts.map +0 -1
- package/dist/utils/assert-queryable-fields.js +0 -47
- package/dist/utils/constants.d.ts +0 -7
- package/dist/utils/constants.d.ts.map +0 -1
- package/dist/utils/constants.js +0 -6
- package/dist/utils/include.d.ts +0 -80
- package/dist/utils/include.d.ts.map +0 -1
- package/dist/utils/include.js +0 -132
- package/dist/utils/table-query.d.ts +0 -41
- package/dist/utils/table-query.d.ts.map +0 -1
- package/dist/utils/table-query.js +0 -1
- package/dist/utils/total-order.d.ts +0 -15
- package/dist/utils/total-order.d.ts.map +0 -1
- package/dist/utils/total-order.js +0 -35
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type ExtractTablesWithRelations } from 'drizzle-orm';
|
|
2
|
+
import type { VirtualField } from './filter.js';
|
|
3
|
+
import type { ColumnName, OrderEntry } from './read.js';
|
|
4
|
+
/** What holds for one table whichever read reaches it. */
|
|
5
|
+
export interface TableReads<TColumn extends string = string, TContext = unknown> {
|
|
6
|
+
/** The order its rows load in as a relation that holds many; a relation's own `orderBy` replaces it. */
|
|
7
|
+
orderBy?: readonly OrderEntry<TColumn>[];
|
|
8
|
+
/** The filter and order names no column of it holds, each with the expression it becomes. */
|
|
9
|
+
virtual?: Readonly<Record<string, VirtualField<TContext>>>;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* A context's reads, keyed by each table's EXPORT name in the schema. `TContext` types the request values its `virtual`
|
|
13
|
+
* names are decided against.
|
|
14
|
+
*/
|
|
15
|
+
export type Reads<TSchema extends Record<string, unknown> = Record<string, unknown>, TContext = unknown> = {
|
|
16
|
+
readonly [K in keyof ExtractTablesWithRelations<TSchema> & string]?: TableReads<ColumnName<TSchema, K>, TContext>;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Declares, once per context, what holds for each table whichever read reaches it: the order its rows load in as a
|
|
20
|
+
* relation, and its `virtual` filter and order names. Every repository of the context hands the same value to its base.
|
|
21
|
+
*
|
|
22
|
+
* `TContext` is the shape of the `context` a finder is handed for its `virtual` names; each receives it, or
|
|
23
|
+
* `undefined` where the caller handed none.
|
|
24
|
+
*
|
|
25
|
+
* @param schema - the schema barrel the repositories are typed by; each key is checked against it
|
|
26
|
+
* @throws Error at declaration on a key that is no table export of `schema`, or an order naming a column its table
|
|
27
|
+
* does not hold
|
|
28
|
+
*/
|
|
29
|
+
export declare function defineReads<TSchema extends Record<string, unknown>, TContext = unknown>(schema: TSchema, reads: Reads<TSchema, TContext>): Reads<TSchema, TContext>;
|
|
30
|
+
//# sourceMappingURL=reads.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reads.d.ts","sourceRoot":"","sources":["../../src/utils/reads.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,0BAA0B,EAChC,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAExD,0DAA0D;AAC1D,MAAM,WAAW,UAAU,CAAC,OAAO,SAAS,MAAM,GAAG,MAAM,EAAE,QAAQ,GAAG,OAAO;IAC7E,wGAAwG;IACxG,OAAO,CAAC,EAAE,SAAS,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;IACzC,6FAA6F;IAC7F,OAAO,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;CAC5D;AAED;;;GAGG;AACH,MAAM,MAAM,KAAK,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,QAAQ,GAAG,OAAO,IAAI;IACzG,QAAQ,EAAE,CAAC,IAAI,MAAM,0BAA0B,CAAC,OAAO,CAAC,GAAG,MAAM,CAAC,CAAC,EAAE,UAAU,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,QAAQ,CAAC;CAClH,CAAC;AAEF;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,QAAQ,GAAG,OAAO,EACrF,MAAM,EAAE,OAAO,EACf,KAAK,EAAE,KAAK,CAAC,OAAO,EAAE,QAAQ,CAAC,GAC9B,KAAK,CAAC,OAAO,EAAE,QAAQ,CAAC,CAY1B"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { createTableRelationsHelpers, extractTablesRelationalConfig, } from 'drizzle-orm';
|
|
2
|
+
/**
|
|
3
|
+
* Declares, once per context, what holds for each table whichever read reaches it: the order its rows load in as a
|
|
4
|
+
* relation, and its `virtual` filter and order names. Every repository of the context hands the same value to its base.
|
|
5
|
+
*
|
|
6
|
+
* `TContext` is the shape of the `context` a finder is handed for its `virtual` names; each receives it, or
|
|
7
|
+
* `undefined` where the caller handed none.
|
|
8
|
+
*
|
|
9
|
+
* @param schema - the schema barrel the repositories are typed by; each key is checked against it
|
|
10
|
+
* @throws Error at declaration on a key that is no table export of `schema`, or an order naming a column its table
|
|
11
|
+
* does not hold
|
|
12
|
+
*/
|
|
13
|
+
export function defineReads(schema, reads) {
|
|
14
|
+
const { tables } = extractTablesRelationalConfig(schema, createTableRelationsHelpers);
|
|
15
|
+
for (const [key, declared] of Object.entries(reads)) {
|
|
16
|
+
const table = Object.hasOwn(tables, key) ? tables[key] : undefined;
|
|
17
|
+
if (!table)
|
|
18
|
+
throw new Error(`"${key}" is no table export of the schema.`);
|
|
19
|
+
for (const entry of declared?.orderBy ?? []) {
|
|
20
|
+
if (!Object.hasOwn(table.columns, entry.field)) {
|
|
21
|
+
throw new Error(`"${key}" holds no column "${entry.field}" to order by.`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return reads;
|
|
26
|
+
}
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import { type ExtractTablesWithRelations } from 'drizzle-orm';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
import type { RelationTarget } from '@zeroxsolutions/query';
|
|
3
|
+
/** The relations a schema declares on one table, keyed by name, each with the type it reaches. */
|
|
4
|
+
export type RelationsOf<TSchema extends Record<string, unknown>, K extends keyof ExtractTablesWithRelations<TSchema>> = {
|
|
5
|
+
readonly [R in keyof ExtractTablesWithRelations<TSchema>[K]['relations'] & string]: RelationTarget;
|
|
5
6
|
};
|
|
6
7
|
/**
|
|
7
|
-
* The relations `schema` declares on the table exported as `table`, read the way `drizzle({ schema })` reads them
|
|
8
|
-
*
|
|
8
|
+
* The relations `schema` declares on the table exported as `table`, read the way `drizzle({ schema })` reads them: each
|
|
9
|
+
* with the type its fieldset is keyed by (its table's SQL name), its primary key, and its own relations, read when a
|
|
10
|
+
* path walks into them. A permit list names its relationships off it, so a relation added to the schema is one a
|
|
11
|
+
* permit list can name without a second list to edit.
|
|
9
12
|
*
|
|
10
13
|
* @param table - the table's EXPORT name in `schema`, not its SQL name
|
|
11
14
|
* @throws Error when `table` is no table export of `schema`
|
|
12
15
|
*/
|
|
13
|
-
export declare function
|
|
16
|
+
export declare function relationsOf<TSchema extends Record<string, unknown>, K extends keyof ExtractTablesWithRelations<TSchema> & string>(schema: TSchema, table: K): RelationsOf<TSchema, K>;
|
|
14
17
|
//# sourceMappingURL=relationships.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"relationships.d.ts","sourceRoot":"","sources":["../../src/utils/relationships.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"relationships.d.ts","sourceRoot":"","sources":["../../src/utils/relationships.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,0BAA0B,EAChC,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAG5D,kGAAkG;AAClG,MAAM,MAAM,WAAW,CACrB,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACvC,CAAC,SAAS,MAAM,0BAA0B,CAAC,OAAO,CAAC,IACjD;IAAE,QAAQ,EAAE,CAAC,IAAI,MAAM,0BAA0B,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,GAAG,MAAM,GAAG,cAAc;CAAE,CAAC;AAE3G;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CACzB,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACvC,CAAC,SAAS,MAAM,0BAA0B,CAAC,OAAO,CAAC,GAAG,MAAM,EAC5D,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,GAAG,WAAW,CAAC,OAAO,EAAE,CAAC,CAAC,CAwBpD"}
|
|
@@ -1,15 +1,33 @@
|
|
|
1
|
-
import { createTableRelationsHelpers, extractTablesRelationalConfig } from 'drizzle-orm';
|
|
1
|
+
import { createTableRelationsHelpers, extractTablesRelationalConfig, getTableUniqueName, } from 'drizzle-orm';
|
|
2
|
+
import { primaryKeyOf } from './read.js';
|
|
2
3
|
/**
|
|
3
|
-
* The relations `schema` declares on the table exported as `table`, read the way `drizzle({ schema })` reads them
|
|
4
|
-
*
|
|
4
|
+
* The relations `schema` declares on the table exported as `table`, read the way `drizzle({ schema })` reads them: each
|
|
5
|
+
* with the type its fieldset is keyed by (its table's SQL name), its primary key, and its own relations, read when a
|
|
6
|
+
* path walks into them. A permit list names its relationships off it, so a relation added to the schema is one a
|
|
7
|
+
* permit list can name without a second list to edit.
|
|
5
8
|
*
|
|
6
9
|
* @param table - the table's EXPORT name in `schema`, not its SQL name
|
|
7
10
|
* @throws Error when `table` is no table export of `schema`
|
|
8
11
|
*/
|
|
9
|
-
export function
|
|
10
|
-
const { tables } = extractTablesRelationalConfig(schema, createTableRelationsHelpers);
|
|
11
|
-
|
|
12
|
-
if (!config)
|
|
12
|
+
export function relationsOf(schema, table) {
|
|
13
|
+
const { tables, tableNamesMap } = extractTablesRelationalConfig(schema, createTableRelationsHelpers);
|
|
14
|
+
if (!Object.hasOwn(tables, table))
|
|
13
15
|
throw new Error(`"${table}" is no table export of the schema.`);
|
|
14
|
-
|
|
16
|
+
const relationsAt = (key) => Object.fromEntries(Object.entries(tables[key]?.relations ?? {}).map(([name, relation]) => {
|
|
17
|
+
const target = tableNamesMap[getTableUniqueName(relation.referencedTable)] ?? relation.referencedTableName;
|
|
18
|
+
return [name, targetAt(target)];
|
|
19
|
+
}));
|
|
20
|
+
// `relations` is a getter: a schema's relations come back to the tables they left, so building them eagerly
|
|
21
|
+
// would never end.
|
|
22
|
+
const targetAt = (key) => {
|
|
23
|
+
const config = tables[key];
|
|
24
|
+
return {
|
|
25
|
+
type: config?.dbName ?? key,
|
|
26
|
+
primaryKey: config ? primaryKeyOf(config) : [],
|
|
27
|
+
get relations() {
|
|
28
|
+
return relationsAt(key);
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
return relationsAt(table);
|
|
15
33
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zeroxsolutions/db",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "10.1.0",
|
|
4
4
|
"description": "Generic Drizzle toolkit - the id column and audit timestamps a table takes, a base repository, the unit-of-work and transactional outbox impls, and the filter/sort/pagination query helpers, for PostgreSQL (postgres-js) and Cloudflare D1; ships no domain schema. Shared across ZeroXSolutions services.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"tslib": "^2.3.0",
|
|
44
44
|
"uuid": "^14.0.1",
|
|
45
|
-
"@zeroxsolutions/query": "0.7.
|
|
45
|
+
"@zeroxsolutions/query": "0.7.4",
|
|
46
46
|
"@zeroxsolutions/cosmic": "0.3.4"
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
@@ -69,5 +69,10 @@
|
|
|
69
69
|
"drizzle-orm": "^0.45.1",
|
|
70
70
|
"postgres": "^3.4.9",
|
|
71
71
|
"zod": "^4.3.6"
|
|
72
|
+
},
|
|
73
|
+
"nx": {
|
|
74
|
+
"targets": {
|
|
75
|
+
"lint-deps": {}
|
|
76
|
+
}
|
|
72
77
|
}
|
|
73
78
|
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import type { GuardedCollectionQuery } from '@zeroxsolutions/query';
|
|
2
|
-
import type { FilterValue, QueryableFields } from './filter.js';
|
|
3
|
-
/** A `filter` targeted a field outside the entity's filterable whitelist. */
|
|
4
|
-
export declare class InvalidQueryFilter extends Error {
|
|
5
|
-
constructor(field: string);
|
|
6
|
-
}
|
|
7
|
-
/** A `sort` targeted a field outside the entity's sortable whitelist. */
|
|
8
|
-
export declare class InvalidQuerySort extends Error {
|
|
9
|
-
constructor(field: string);
|
|
10
|
-
}
|
|
11
|
-
/**
|
|
12
|
-
* Rejects a `filter`/`sort` on any field outside a listable entity's {@link QueryableFields} whitelist,
|
|
13
|
-
* so a client can never filter or order by a hidden column. A repository finder calls this before
|
|
14
|
-
* building SQL.
|
|
15
|
-
*
|
|
16
|
-
* @param query - the parsed wire query, read for its `filter` / `sort` (a no-op when `undefined`)
|
|
17
|
-
* @param resolved - the names a resolver answers, which no row key could list
|
|
18
|
-
* @throws {@link InvalidQueryFilter} naming the first filtered field outside `allow.filterable` and
|
|
19
|
-
* `resolved.filterable`
|
|
20
|
-
* @throws {@link InvalidQuerySort} naming the first sorted field outside `allow.sortable` and
|
|
21
|
-
* `resolved.sortable`
|
|
22
|
-
*/
|
|
23
|
-
export declare function assertQueryableFields<TSelect>(query: Partial<Pick<GuardedCollectionQuery<FilterValue>, 'filter' | 'sort'>> | undefined, allow: QueryableFields<TSelect>, resolved?: {
|
|
24
|
-
filterable?: readonly string[];
|
|
25
|
-
sortable?: readonly string[];
|
|
26
|
-
}): void;
|
|
27
|
-
//# sourceMappingURL=assert-queryable-fields.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"assert-queryable-fields.d.ts","sourceRoot":"","sources":["../../src/utils/assert-queryable-fields.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AACpE,OAAO,KAAK,EAAc,WAAW,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAG5E,6EAA6E;AAC7E,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,KAAK,EAAE,MAAM;CAG1B;AAED,yEAAyE;AACzE,qBAAa,gBAAiB,SAAQ,KAAK;gBAC7B,KAAK,EAAE,MAAM;CAG1B;AAaD;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAC3C,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,sBAAsB,CAAC,WAAW,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC,CAAC,GAAG,SAAS,EACxF,KAAK,EAAE,eAAe,CAAC,OAAO,CAAC,EAC/B,QAAQ,GAAE;IAAE,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAAC,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAAO,GAC9E,IAAI,CAUN"}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { isFilterGroup } from './filter.js';
|
|
2
|
-
/** A `filter` targeted a field outside the entity's filterable whitelist. */
|
|
3
|
-
export class InvalidQueryFilter extends Error {
|
|
4
|
-
constructor(field) {
|
|
5
|
-
super(`Filtering by "${field}" is not supported.`);
|
|
6
|
-
}
|
|
7
|
-
}
|
|
8
|
-
/** A `sort` targeted a field outside the entity's sortable whitelist. */
|
|
9
|
-
export class InvalidQuerySort extends Error {
|
|
10
|
-
constructor(field) {
|
|
11
|
-
super(`Sorting by "${field}" is not supported.`);
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
/** Walks the tree so a condition nested in a group is checked too, not just the root nodes. */
|
|
15
|
-
function assertFilterNodes(nodes, filterable) {
|
|
16
|
-
for (const node of nodes) {
|
|
17
|
-
if (isFilterGroup(node)) {
|
|
18
|
-
assertFilterNodes(node.children, filterable);
|
|
19
|
-
}
|
|
20
|
-
else if (!filterable.includes(node.field)) {
|
|
21
|
-
throw new InvalidQueryFilter(node.field);
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* Rejects a `filter`/`sort` on any field outside a listable entity's {@link QueryableFields} whitelist,
|
|
27
|
-
* so a client can never filter or order by a hidden column. A repository finder calls this before
|
|
28
|
-
* building SQL.
|
|
29
|
-
*
|
|
30
|
-
* @param query - the parsed wire query, read for its `filter` / `sort` (a no-op when `undefined`)
|
|
31
|
-
* @param resolved - the names a resolver answers, which no row key could list
|
|
32
|
-
* @throws {@link InvalidQueryFilter} naming the first filtered field outside `allow.filterable` and
|
|
33
|
-
* `resolved.filterable`
|
|
34
|
-
* @throws {@link InvalidQuerySort} naming the first sorted field outside `allow.sortable` and
|
|
35
|
-
* `resolved.sortable`
|
|
36
|
-
*/
|
|
37
|
-
export function assertQueryableFields(query, allow, resolved = {}) {
|
|
38
|
-
// Widened to `string`: the membership check runs against client-supplied names, not keys of TSelect.
|
|
39
|
-
const filterable = [...allow.filterable, ...(resolved.filterable ?? [])];
|
|
40
|
-
const sortable = [...allow.sortable, ...(resolved.sortable ?? [])];
|
|
41
|
-
assertFilterNodes(query?.filter ?? [], filterable);
|
|
42
|
-
for (const order of query?.sort ?? []) {
|
|
43
|
-
if (!sortable.includes(order.field)) {
|
|
44
|
-
throw new InvalidQuerySort(order.field);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
/** Hard cap on a page's row count - a larger `limit` is silently clamped to this, not rejected. */
|
|
2
|
-
export declare const MAX_PAGE_SIZE = 100;
|
|
3
|
-
/** The offset applied when a query omits `offset` - starts at the first row. */
|
|
4
|
-
export declare const DEFAULT_OFFSET = 0;
|
|
5
|
-
/** The page size applied when a query omits `limit`. */
|
|
6
|
-
export declare const DEFAULT_PAGE_SIZE = 20;
|
|
7
|
-
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/utils/constants.ts"],"names":[],"mappings":"AAAA,mGAAmG;AACnG,eAAO,MAAM,aAAa,MAAM,CAAC;AAEjC,gFAAgF;AAChF,eAAO,MAAM,cAAc,IAAI,CAAC;AAEhC,wDAAwD;AACxD,eAAO,MAAM,iBAAiB,KAAK,CAAC"}
|
package/dist/utils/constants.js
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
/** Hard cap on a page's row count - a larger `limit` is silently clamped to this, not rejected. */
|
|
2
|
-
export const MAX_PAGE_SIZE = 100;
|
|
3
|
-
/** The offset applied when a query omits `offset` - starts at the first row. */
|
|
4
|
-
export const DEFAULT_OFFSET = 0;
|
|
5
|
-
/** The page size applied when a query omits `limit`. */
|
|
6
|
-
export const DEFAULT_PAGE_SIZE = 20;
|
package/dist/utils/include.d.ts
DELETED
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import { type Table, type View } from 'drizzle-orm';
|
|
2
|
-
/** An include path named a relation the schema does not hold. */
|
|
3
|
-
export declare class UnknownIncludePath extends Error {
|
|
4
|
-
readonly path: string;
|
|
5
|
-
constructor(path: string);
|
|
6
|
-
}
|
|
7
|
-
/** One relation, narrowed to what walking a path needs. */
|
|
8
|
-
interface RelationTarget {
|
|
9
|
-
referencedTableName: string;
|
|
10
|
-
}
|
|
11
|
-
/** A column, narrowed to what picking it needs. */
|
|
12
|
-
interface ColumnTarget {
|
|
13
|
-
name: string;
|
|
14
|
-
}
|
|
15
|
-
/** The relational metadata drizzle computes when the client is built (`db._.schema`), narrowed the same way. */
|
|
16
|
-
export interface RelationalTables {
|
|
17
|
-
[exportName: string]: {
|
|
18
|
-
dbName: string;
|
|
19
|
-
columns: Record<string, ColumnTarget>;
|
|
20
|
-
primaryKey: readonly ColumnTarget[];
|
|
21
|
-
relations: Record<string, RelationTarget>;
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
/** What a finder was asked to load, as its options carry it. */
|
|
25
|
-
export interface RelationalReadOptions {
|
|
26
|
-
/** Fieldsets keyed by table name; a table with none loads every column. */
|
|
27
|
-
fields?: Readonly<Record<string, readonly string[] | undefined>>;
|
|
28
|
-
/** Relation paths, dot-separated as the wire states them. */
|
|
29
|
-
include?: readonly string[];
|
|
30
|
-
/** Root columns loaded on top of the root fieldset; never narrows it. */
|
|
31
|
-
columns?: Readonly<Record<string, boolean | undefined>>;
|
|
32
|
-
/** A relational config the caller spells out, winning over `include` wherever both name a relation. */
|
|
33
|
-
with?: unknown;
|
|
34
|
-
}
|
|
35
|
-
/** One level of drizzle's relational config: the columns it selects, and the relations it loads under it. */
|
|
36
|
-
export interface RelationalRead {
|
|
37
|
-
/** Absent where every column loads. */
|
|
38
|
-
columns?: Record<string, true>;
|
|
39
|
-
/** Absent where no relation loads, which is what lets a finder take the plain `select()` path. */
|
|
40
|
-
with?: Record<string, unknown>;
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* A single table or view described the way {@link RelationalTables} describes one, for a read given no schema.
|
|
44
|
-
*
|
|
45
|
-
* A view declares no primary key of its own; the key it reads under is whichever selected column is a primary key of
|
|
46
|
-
* the table it was selected from.
|
|
47
|
-
*/
|
|
48
|
-
export declare function describeTable(table: Table | View): RelationalTables[string];
|
|
49
|
-
/**
|
|
50
|
-
* `selectable` narrowed to the keys a {@link RelationalRead} level selects, for a plain `select()`.
|
|
51
|
-
*
|
|
52
|
-
* @param columns - a level's `columns`; absent, every one of `selectable` is kept
|
|
53
|
-
*/
|
|
54
|
-
export declare function selectedColumns<TSelectable extends Record<string, unknown>>(selectable: TSelectable, columns: Readonly<Record<string, true>> | undefined): TSelectable;
|
|
55
|
-
/**
|
|
56
|
-
* What one finder call loads: resolved against the relational schema `executor` was built with where the repository
|
|
57
|
-
* has a `queryKey`, and against its own table alone where it has not - so a fieldset still narrows the row.
|
|
58
|
-
*
|
|
59
|
-
* @param executor - the drizzle client or transaction the call runs on; its `_.schema` is drizzle's relational config
|
|
60
|
-
* @throws when `include` is given and the repository has no `queryKey`, or its client no schema
|
|
61
|
-
* @throws {@link UnknownIncludePath} as {@link buildRelationalRead} does
|
|
62
|
-
*/
|
|
63
|
-
export declare function resolveRelationalRead(executor: unknown, queryKey: PropertyKey | undefined, table: Table | View, options: RelationalReadOptions | undefined): RelationalRead;
|
|
64
|
-
/**
|
|
65
|
-
* Resolves a read into drizzle's relational config: `fields` into each level's `columns`, `include` into nested
|
|
66
|
-
* `with`, then `columns` and `with` as the caller spelled them.
|
|
67
|
-
*
|
|
68
|
-
* - A fieldset selects its columns and the table's primary key; a relation it names loads that relation's primary keys,
|
|
69
|
-
* which is the linkage a relationship publishes. A name that is neither loads nothing: the permit-list and the
|
|
70
|
-
* `TableQuery` type are what refuse one.
|
|
71
|
-
* - An included relation replaces any linkage the fieldset gave it, and loads under its own table's fieldset.
|
|
72
|
-
*
|
|
73
|
-
* @param rootTable - the EXPORT name of the table the read starts from, not its table name
|
|
74
|
-
* @throws {@link UnknownIncludePath} naming the path up to and including the segment that failed - drizzle@0.45.1
|
|
75
|
-
* raises on a relation it does not hold too, but as `Cannot read properties of undefined (reading
|
|
76
|
-
* 'referencedTable')`, naming neither the path nor the relation
|
|
77
|
-
*/
|
|
78
|
-
export declare function buildRelationalRead(tables: RelationalTables, rootTable: string, options: RelationalReadOptions): RelationalRead;
|
|
79
|
-
export {};
|
|
80
|
-
//# sourceMappingURL=include.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"include.d.ts","sourceRoot":"","sources":["../../src/utils/include.ts"],"names":[],"mappings":"AAAA,OAAO,EAA6E,KAAK,KAAK,EAAE,KAAK,IAAI,EAAE,MAAM,aAAa,CAAC;AAE/H,iEAAiE;AACjE,qBAAa,kBAAmB,SAAQ,KAAK;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM;gBAAZ,IAAI,EAAE,MAAM;CAGlC;AAED,2DAA2D;AAC3D,UAAU,cAAc;IACtB,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AAED,mDAAmD;AACnD,UAAU,YAAY;IACpB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,gHAAgH;AAChH,MAAM,WAAW,gBAAgB;IAC/B,CAAC,UAAU,EAAE,MAAM,GAAG;QACpB,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;QACtC,UAAU,EAAE,SAAS,YAAY,EAAE,CAAC;QACpC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;KAC3C,CAAC;CACH;AAED,gEAAgE;AAChE,MAAM,WAAW,qBAAqB;IACpC,2EAA2E;IAC3E,MAAM,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC,CAAC;IACjE,6DAA6D;IAC7D,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,yEAAyE;IACzE,OAAO,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,GAAG,SAAS,CAAC,CAAC,CAAC;IACxD,uGAAuG;IACvG,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,6GAA6G;AAC7G,MAAM,WAAW,cAAc;IAC7B,uCAAuC;IACvC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAC/B,kGAAkG;IAClG,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAY3E;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,WAAW,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACzE,UAAU,EAAE,WAAW,EACvB,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,GAAG,SAAS,GAClD,WAAW,CAGb;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,OAAO,EACjB,QAAQ,EAAE,WAAW,GAAG,SAAS,EACjC,KAAK,EAAE,KAAK,GAAG,IAAI,EACnB,OAAO,EAAE,qBAAqB,GAAG,SAAS,GACzC,cAAc,CAShB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,gBAAgB,EACxB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,qBAAqB,GAC7B,cAAc,CAmEhB"}
|
package/dist/utils/include.js
DELETED
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
import { getTableColumns, getTableName, getViewName, getViewSelectedFields, isView } from 'drizzle-orm';
|
|
2
|
-
/** An include path named a relation the schema does not hold. */
|
|
3
|
-
export class UnknownIncludePath extends Error {
|
|
4
|
-
path;
|
|
5
|
-
constructor(path) {
|
|
6
|
-
super(`Including "${path}" is not supported.`);
|
|
7
|
-
this.path = path;
|
|
8
|
-
}
|
|
9
|
-
}
|
|
10
|
-
/**
|
|
11
|
-
* A single table or view described the way {@link RelationalTables} describes one, for a read given no schema.
|
|
12
|
-
*
|
|
13
|
-
* A view declares no primary key of its own; the key it reads under is whichever selected column is a primary key of
|
|
14
|
-
* the table it was selected from.
|
|
15
|
-
*/
|
|
16
|
-
export function describeTable(table) {
|
|
17
|
-
const view = isView(table);
|
|
18
|
-
const columns = (view ? getViewSelectedFields(table) : getTableColumns(table));
|
|
19
|
-
return {
|
|
20
|
-
dbName: view ? getViewName(table) : getTableName(table),
|
|
21
|
-
columns,
|
|
22
|
-
primaryKey: Object.values(columns).filter((column) => column.primary),
|
|
23
|
-
relations: {},
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
/**
|
|
27
|
-
* `selectable` narrowed to the keys a {@link RelationalRead} level selects, for a plain `select()`.
|
|
28
|
-
*
|
|
29
|
-
* @param columns - a level's `columns`; absent, every one of `selectable` is kept
|
|
30
|
-
*/
|
|
31
|
-
export function selectedColumns(selectable, columns) {
|
|
32
|
-
if (!columns)
|
|
33
|
-
return selectable;
|
|
34
|
-
return Object.fromEntries(Object.entries(selectable).filter(([key]) => columns[key]));
|
|
35
|
-
}
|
|
36
|
-
/**
|
|
37
|
-
* What one finder call loads: resolved against the relational schema `executor` was built with where the repository
|
|
38
|
-
* has a `queryKey`, and against its own table alone where it has not - so a fieldset still narrows the row.
|
|
39
|
-
*
|
|
40
|
-
* @param executor - the drizzle client or transaction the call runs on; its `_.schema` is drizzle's relational config
|
|
41
|
-
* @throws when `include` is given and the repository has no `queryKey`, or its client no schema
|
|
42
|
-
* @throws {@link UnknownIncludePath} as {@link buildRelationalRead} does
|
|
43
|
-
*/
|
|
44
|
-
export function resolveRelationalRead(executor, queryKey, table, options) {
|
|
45
|
-
if (!options)
|
|
46
|
-
return {};
|
|
47
|
-
const tables = executor?._?.schema;
|
|
48
|
-
if (queryKey !== undefined && tables)
|
|
49
|
-
return buildRelationalRead(tables, String(queryKey), options);
|
|
50
|
-
if (options.include?.length) {
|
|
51
|
-
throw new Error('"include" needs a queryKey and a client built with its schema.');
|
|
52
|
-
}
|
|
53
|
-
const described = describeTable(table);
|
|
54
|
-
return buildRelationalRead({ [described.dbName]: described }, described.dbName, options);
|
|
55
|
-
}
|
|
56
|
-
/**
|
|
57
|
-
* Resolves a read into drizzle's relational config: `fields` into each level's `columns`, `include` into nested
|
|
58
|
-
* `with`, then `columns` and `with` as the caller spelled them.
|
|
59
|
-
*
|
|
60
|
-
* - A fieldset selects its columns and the table's primary key; a relation it names loads that relation's primary keys,
|
|
61
|
-
* which is the linkage a relationship publishes. A name that is neither loads nothing: the permit-list and the
|
|
62
|
-
* `TableQuery` type are what refuse one.
|
|
63
|
-
* - An included relation replaces any linkage the fieldset gave it, and loads under its own table's fieldset.
|
|
64
|
-
*
|
|
65
|
-
* @param rootTable - the EXPORT name of the table the read starts from, not its table name
|
|
66
|
-
* @throws {@link UnknownIncludePath} naming the path up to and including the segment that failed - drizzle@0.45.1
|
|
67
|
-
* raises on a relation it does not hold too, but as `Cannot read properties of undefined (reading
|
|
68
|
-
* 'referencedTable')`, naming neither the path nor the relation
|
|
69
|
-
*/
|
|
70
|
-
export function buildRelationalRead(tables, rootTable, options) {
|
|
71
|
-
// A relation names its target by TABLE name while `tables` is keyed by EXPORT name; looking the
|
|
72
|
-
// target up directly reads as "no such relation" wherever the two differ.
|
|
73
|
-
const byTableName = new Map(Object.entries(tables).map(([key, table]) => [table.dbName, key]));
|
|
74
|
-
const exportNameOf = (relation) => byTableName.get(relation.referencedTableName) ?? relation.referencedTableName;
|
|
75
|
-
const primaryKeyOf = (tableKey) => {
|
|
76
|
-
const table = tables[tableKey];
|
|
77
|
-
if (!table)
|
|
78
|
-
return [];
|
|
79
|
-
const names = new Set(table.primaryKey.map((column) => column.name));
|
|
80
|
-
return Object.keys(table.columns).filter((key) => names.has(table.columns[key]?.name ?? ''));
|
|
81
|
-
};
|
|
82
|
-
const selecting = (keys) => Object.fromEntries(keys.map((key) => [key, true]));
|
|
83
|
-
const levelOf = (tableKey) => {
|
|
84
|
-
const table = tables[tableKey];
|
|
85
|
-
const fieldset = table ? options.fields?.[table.dbName] : undefined;
|
|
86
|
-
if (!table || fieldset === undefined)
|
|
87
|
-
return {};
|
|
88
|
-
const level = {
|
|
89
|
-
columns: selecting([...primaryKeyOf(tableKey), ...fieldset.filter((name) => name in table.columns)]),
|
|
90
|
-
};
|
|
91
|
-
for (const name of fieldset) {
|
|
92
|
-
const relation = table.relations[name];
|
|
93
|
-
if (!relation)
|
|
94
|
-
continue;
|
|
95
|
-
level.with ??= {};
|
|
96
|
-
level.with[name] = { columns: selecting(primaryKeyOf(exportNameOf(relation))) };
|
|
97
|
-
}
|
|
98
|
-
return level;
|
|
99
|
-
};
|
|
100
|
-
const root = levelOf(rootTable);
|
|
101
|
-
const included = new Set();
|
|
102
|
-
for (const path of options.include ?? []) {
|
|
103
|
-
let tableKey = rootTable;
|
|
104
|
-
let level = root;
|
|
105
|
-
const walked = [];
|
|
106
|
-
for (const segment of path.split('.')) {
|
|
107
|
-
walked.push(segment);
|
|
108
|
-
const relation = tables[tableKey]?.relations[segment];
|
|
109
|
-
if (!relation)
|
|
110
|
-
throw new UnknownIncludePath(walked.join('.'));
|
|
111
|
-
tableKey = exportNameOf(relation);
|
|
112
|
-
level.with ??= {};
|
|
113
|
-
let next = level.with[segment];
|
|
114
|
-
if (next === undefined || !included.has(next)) {
|
|
115
|
-
next = levelOf(tableKey);
|
|
116
|
-
included.add(next);
|
|
117
|
-
level.with[segment] = next;
|
|
118
|
-
}
|
|
119
|
-
level = next;
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
if (root.columns) {
|
|
123
|
-
for (const [key, loaded] of Object.entries(options.columns ?? {})) {
|
|
124
|
-
if (loaded)
|
|
125
|
-
root.columns[key] = true;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
if (options.with !== undefined) {
|
|
129
|
-
root.with = { ...root.with, ...options.with };
|
|
130
|
-
}
|
|
131
|
-
return root;
|
|
132
|
-
}
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import type { ExtractTablesWithRelations, FindTableByDBName, TableRelationalConfig, TablesRelationalConfig } from 'drizzle-orm';
|
|
2
|
-
/** How many relations deep {@link IncludePath} spells a path; a deeper one is spelled out with `with`. */
|
|
3
|
-
type IncludeDepth = 3;
|
|
4
|
-
/** Indexed by a depth, the depth one relation further in. */
|
|
5
|
-
type OneLess = [never, 0, 1, 2];
|
|
6
|
-
/**
|
|
7
|
-
* Every dotted relation path that starts at `TTable`, up to three relations deep.
|
|
8
|
-
*
|
|
9
|
-
* A path may come back to a table it passed through (`author.books` from a book), so depth is what keeps the type
|
|
10
|
-
* finite, not the tables visited.
|
|
11
|
-
*/
|
|
12
|
-
export type IncludePath<TSchema extends TablesRelationalConfig, TTable extends TableRelationalConfig, TDepth extends number = IncludeDepth> = [TDepth] extends [0] ? never : TTable extends TableRelationalConfig ? {
|
|
13
|
-
[K in keyof TTable['relations'] & string]: K | `${K}.${IncludePath<TSchema, FindTableByDBName<TSchema, TTable['relations'][K]['referencedTableName']>, OneLess[TDepth]>}`;
|
|
14
|
-
}[keyof TTable['relations'] & string] : never;
|
|
15
|
-
/**
|
|
16
|
-
* A JSON:API read of one table, typed off its drizzle schema: fieldsets keyed by TABLE name holding column and relation
|
|
17
|
-
* names, include paths walked by relation name.
|
|
18
|
-
*
|
|
19
|
-
* Its shape is the query `@zeroxsolutions/query` guards, so a guarded query whose permit-list was declared `as const`
|
|
20
|
-
* and checked with `satisfies TableQuery<typeof schema, '<export name>'>` passes to a finder without a cast. A
|
|
21
|
-
* repository whose schema is not given types it by name only.
|
|
22
|
-
*/
|
|
23
|
-
export type TableQuery<TSchema extends Record<string, unknown>, TTableName extends keyof ExtractTablesWithRelations<TSchema>> = [TTableName] extends [never] ? {
|
|
24
|
-
/** As the schema-typed branch below: keyed by table name, holding column and relation names. */
|
|
25
|
-
fields?: Readonly<Record<string, readonly string[]>>;
|
|
26
|
-
/** As the schema-typed branch below: relation paths, dot-separated. */
|
|
27
|
-
include?: readonly string[];
|
|
28
|
-
} : {
|
|
29
|
-
/**
|
|
30
|
-
* Fieldsets keyed by table name. A table given one loads those columns and its primary key, and each relation it
|
|
31
|
-
* names loads the related primary keys; an empty one loads the primary key alone; a table given none loads every
|
|
32
|
-
* column.
|
|
33
|
-
*/
|
|
34
|
-
fields?: {
|
|
35
|
-
[K in keyof ExtractTablesWithRelations<TSchema> as ExtractTablesWithRelations<TSchema>[K]['dbName']]?: readonly ((keyof ExtractTablesWithRelations<TSchema>[K]['columns'] | keyof ExtractTablesWithRelations<TSchema>[K]['relations']) & string)[];
|
|
36
|
-
};
|
|
37
|
-
/** Relation paths, dot-separated; each relation loads under its own table's fieldset. Needs a `queryKey`. */
|
|
38
|
-
include?: readonly IncludePath<ExtractTablesWithRelations<TSchema>, ExtractTablesWithRelations<TSchema>[TTableName]>[];
|
|
39
|
-
};
|
|
40
|
-
export {};
|
|
41
|
-
//# sourceMappingURL=table-query.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"table-query.d.ts","sourceRoot":"","sources":["../../src/utils/table-query.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,0BAA0B,EAC1B,iBAAiB,EACjB,qBAAqB,EACrB,sBAAsB,EACvB,MAAM,aAAa,CAAC;AAErB,0GAA0G;AAC1G,KAAK,YAAY,GAAG,CAAC,CAAC;AAEtB,6DAA6D;AAC7D,KAAK,OAAO,GAAG,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;AAEhC;;;;;GAKG;AACH,MAAM,MAAM,WAAW,CACrB,OAAO,SAAS,sBAAsB,EACtC,MAAM,SAAS,qBAAqB,EACpC,MAAM,SAAS,MAAM,GAAG,YAAY,IAClC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,GACpB,KAAK,GACL,MAAM,SAAS,qBAAqB,GAClC;KACG,CAAC,IAAI,MAAM,MAAM,CAAC,WAAW,CAAC,GAAG,MAAM,GACpC,CAAC,GACD,GAAG,CAAC,IAAI,WAAW,CAAC,OAAO,EAAE,iBAAiB,CAAC,OAAO,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE;CAC/H,CAAC,MAAM,MAAM,CAAC,WAAW,CAAC,GAAG,MAAM,CAAC,GACrC,KAAK,CAAC;AAEZ;;;;;;;GAOG;AACH,MAAM,MAAM,UAAU,CACpB,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACvC,UAAU,SAAS,MAAM,0BAA0B,CAAC,OAAO,CAAC,IAC1D,CAAC,UAAU,CAAC,SAAS,CAAC,KAAK,CAAC,GAC5B;IACE,gGAAgG;IAChG,MAAM,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAAC,CAAC;IACrD,uEAAuE;IACvE,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC7B,GACD;IACE;;;;OAIG;IACH,MAAM,CAAC,EAAE;SACN,CAAC,IAAI,MAAM,0BAA0B,CAAC,OAAO,CAAC,IAAI,0BAA0B,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC,CAC7G,MAAM,0BAA0B,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,GACvD,MAAM,0BAA0B,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAC5D,GACC,MAAM,CAAC,EAAE;KACZ,CAAC;IACF,6GAA6G;IAC7G,OAAO,CAAC,EAAE,SAAS,WAAW,CAAC,0BAA0B,CAAC,OAAO,CAAC,EAAE,0BAA0B,CAAC,OAAO,CAAC,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC;CACxH,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import type { SortField } from '@zeroxsolutions/query';
|
|
2
|
-
import { PgTable, type TableConfig as PgTableConfig } from 'drizzle-orm/pg-core';
|
|
3
|
-
import { type SQLiteTable, type TableConfig as SQLiteTableConfig } from 'drizzle-orm/sqlite-core';
|
|
4
|
-
type OrderableTable = PgTable<PgTableConfig> | SQLiteTable<SQLiteTableConfig>;
|
|
5
|
-
/**
|
|
6
|
-
* The caller's sort followed by each primary key column it does not already name, ascending, so no
|
|
7
|
-
* two rows tie and every page of a walk is cut from the same order.
|
|
8
|
-
*
|
|
9
|
-
* Kept apart from `buildOrderBy`, which a consumer applies one clause at a time to build its own
|
|
10
|
-
* order: a key appended to each clause would land in the middle of that order. Kept out of
|
|
11
|
-
* `utils/index.ts` too, which re-exports everything it names.
|
|
12
|
-
*/
|
|
13
|
-
export declare function totalOrder(table: OrderableTable, sort: SortField[] | undefined): SortField[];
|
|
14
|
-
export {};
|
|
15
|
-
//# sourceMappingURL=total-order.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"total-order.d.ts","sourceRoot":"","sources":["../../src/utils/total-order.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAEvD,OAAO,EAAsC,OAAO,EAAE,KAAK,WAAW,IAAI,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACrH,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,WAAW,IAAI,iBAAiB,EACtC,MAAM,yBAAyB,CAAC;AAEjC,KAAK,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC,GAAG,WAAW,CAAC,iBAAiB,CAAC,CAAC;AAE9E;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,SAAS,GAAG,SAAS,EAAE,CAO5F"}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { getTableColumns, is } from 'drizzle-orm';
|
|
2
|
-
import { getTableConfig as getPgTableConfig, PgTable } from 'drizzle-orm/pg-core';
|
|
3
|
-
import { getTableConfig as getSQLiteTableConfig, } from 'drizzle-orm/sqlite-core';
|
|
4
|
-
/**
|
|
5
|
-
* The caller's sort followed by each primary key column it does not already name, ascending, so no
|
|
6
|
-
* two rows tie and every page of a walk is cut from the same order.
|
|
7
|
-
*
|
|
8
|
-
* Kept apart from `buildOrderBy`, which a consumer applies one clause at a time to build its own
|
|
9
|
-
* order: a key appended to each clause would land in the middle of that order. Kept out of
|
|
10
|
-
* `utils/index.ts` too, which re-exports everything it names.
|
|
11
|
-
*/
|
|
12
|
-
export function totalOrder(table, sort) {
|
|
13
|
-
const requested = sort ?? [];
|
|
14
|
-
const named = new Set(requested.map((item) => item.field));
|
|
15
|
-
const appended = primaryKeyFields(table)
|
|
16
|
-
.filter((field) => !named.has(field))
|
|
17
|
-
.map((field) => ({ field, direction: 'asc' }));
|
|
18
|
-
return [...requested, ...appended];
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* The property keys of the table's primary key columns, in the order the key declares them.
|
|
22
|
-
*
|
|
23
|
-
* A property key and not the SQL name, because a `SortField` names a column the way
|
|
24
|
-
* `getTableColumns` keys it. A composite key sets no column's `primary` flag, so it is read off the
|
|
25
|
-
* table config instead, and matched back by SQL name: pg-core hands the key its own copies of the
|
|
26
|
-
* columns, so the key's column objects are not the table's (drizzle-orm@0.45.1).
|
|
27
|
-
*/
|
|
28
|
-
function primaryKeyFields(table) {
|
|
29
|
-
const columns = getTableColumns(table);
|
|
30
|
-
const fieldOf = new Map(Object.entries(columns).map(([field, column]) => [column.name, field]));
|
|
31
|
-
const declared = Object.values(columns).filter((column) => column.primary);
|
|
32
|
-
const composite = (is(table, PgTable) ? getPgTableConfig(table) : getSQLiteTableConfig(table)).primaryKeys[0]?.columns;
|
|
33
|
-
const keyColumns = declared.length > 0 ? declared : (composite ?? []);
|
|
34
|
-
return keyColumns.flatMap((column) => fieldOf.get(column.name) ?? []);
|
|
35
|
-
}
|