@rebasepro/common 0.19.1 → 0.19.2-canary.g08eed46
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/collections/field-access.d.ts +76 -0
- package/dist/collections/index.d.ts +1 -0
- package/dist/data/buildRebaseData.d.ts +11 -12
- package/dist/data/cursor.d.ts +102 -0
- package/dist/data/include-spec.d.ts +128 -0
- package/dist/data/paginate.d.ts +8 -5
- package/dist/data/query_builder.d.ts +15 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.es.js +1415 -110
- package/dist/index.es.js.map +1 -1
- package/dist/util/entities.d.ts +79 -0
- package/dist/util/index.d.ts +1 -0
- package/dist/util/junction-policies.d.ts +17 -1
- package/dist/util/tenant.d.ts +146 -0
- package/package.json +3 -3
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { CollectionConfig, FieldAccess, Property } from "@rebasepro/types";
|
|
2
|
+
/**
|
|
3
|
+
* Field-level access control: one mechanism, read by every enforcement point.
|
|
4
|
+
*
|
|
5
|
+
* A collection's `securityRules` decide which *rows* a caller reaches;
|
|
6
|
+
* `property.access` decides which *fields* of a reached row they see and may
|
|
7
|
+
* set. The two are independent — a field rule never widens row access, and a
|
|
8
|
+
* row a caller cannot read has no fields to talk about.
|
|
9
|
+
*
|
|
10
|
+
* `excludeFromApi` is sugar for `access: { read: [], write: [] }` and is
|
|
11
|
+
* normalised into it by {@link effectiveAccess}, which is the only place either
|
|
12
|
+
* spelling is read. It used to be its own code path in five files — the read
|
|
13
|
+
* strip, the write refusal, the SDK generator, the OpenAPI schema builder and
|
|
14
|
+
* the filter-parameter builder — and the second rule would have made ten.
|
|
15
|
+
* There is one predicate now, and the flag is a shorthand for it.
|
|
16
|
+
*
|
|
17
|
+
* @module
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* The caller a field rule is judged against: whatever the call context carries
|
|
21
|
+
* as the user's application roles.
|
|
22
|
+
*
|
|
23
|
+
* `undefined` is the trusted server plane — an in-process `rebase.data` call
|
|
24
|
+
* with no request behind it, the auth adapter writing a password hash, a
|
|
25
|
+
* migration. Every API boundary has a viewer: an unauthenticated REST request is
|
|
26
|
+
* scoped as `{ uid: ANONYMOUS_USER_ID, roles: ["anon"] }` before it reaches a
|
|
27
|
+
* driver, so "no viewer" cannot be reached from outside.
|
|
28
|
+
*/
|
|
29
|
+
export interface FieldViewer {
|
|
30
|
+
roles?: readonly string[];
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* The role that satisfies any non-empty list.
|
|
34
|
+
*
|
|
35
|
+
* The same arm every baseline policy carries: `security_rules` injects
|
|
36
|
+
* `rolesOverlap(['admin'])` into the default read and write policies, and
|
|
37
|
+
* `rebase.dataAsAdmin` is scoped with `{ uid: "service", roles: ["admin"] }`.
|
|
38
|
+
* Without this an author could declare `access: { read: ["hr"] }` and lock the
|
|
39
|
+
* administrator out of a column of their own database — and lock the Studio out
|
|
40
|
+
* of rendering it.
|
|
41
|
+
*/
|
|
42
|
+
export declare const ADMIN_ROLE = "admin";
|
|
43
|
+
/**
|
|
44
|
+
* What a property's access rules actually are, with `excludeFromApi` expanded.
|
|
45
|
+
*
|
|
46
|
+
* Returns `undefined` when the property constrains nothing, so callers can skip
|
|
47
|
+
* the whole check for the overwhelmingly common case.
|
|
48
|
+
*/
|
|
49
|
+
export declare function effectiveAccess(property: Property | undefined): FieldAccess | undefined;
|
|
50
|
+
/** May this caller receive this field's value? */
|
|
51
|
+
export declare function canReadField(property: Property | undefined, viewer: FieldViewer | undefined): boolean;
|
|
52
|
+
/** May this caller set this field's value? */
|
|
53
|
+
export declare function canWriteField(property: Property | undefined, viewer: FieldViewer | undefined): boolean;
|
|
54
|
+
/**
|
|
55
|
+
* The names on this collection a caller may not touch, in the two spellings a
|
|
56
|
+
* caller can write them in.
|
|
57
|
+
*
|
|
58
|
+
* `declared` is the property keys, which is what has to leave a *known-fields*
|
|
59
|
+
* set. `refused` is those plus the physical column names behind them: a caller
|
|
60
|
+
* who knows the table can send `password_hash` as readily as `passwordHash`, and
|
|
61
|
+
* a rule that only knew the wire name would be one rename away from useless.
|
|
62
|
+
*
|
|
63
|
+
* `kind` picks which half of the rule is read; nothing else differs.
|
|
64
|
+
*/
|
|
65
|
+
export declare function restrictedFieldNames(collection: CollectionConfig, viewer: FieldViewer | undefined, kind: "read" | "write"): {
|
|
66
|
+
declared: string[];
|
|
67
|
+
refused: Set<string>;
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* True when nothing on this collection restricts a field, for either direction.
|
|
71
|
+
*
|
|
72
|
+
* Every read of every row runs through the strip, so the collection that has no
|
|
73
|
+
* rules — which is almost all of them — has to cost one property walk and no
|
|
74
|
+
* allocation.
|
|
75
|
+
*/
|
|
76
|
+
export declare function hasFieldAccessRules(collection: CollectionConfig): boolean;
|
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
import { DataDriver, RebaseData, RebaseSdkData } from "@rebasepro/types";
|
|
2
|
+
/**
|
|
3
|
+
* Derive the response key an aggregate comes back under.
|
|
4
|
+
*
|
|
5
|
+
* `sum(total)` → `sum_total`, `count()` → `count`. Written once, here, because
|
|
6
|
+
* the REST parser derives the same alias from `?select=sum(total)` and the two
|
|
7
|
+
* have to agree — a caller reading `row.sum_total` off an SDK result and off an
|
|
8
|
+
* HTTP response is reading the same key or the SDK is broken.
|
|
9
|
+
*/
|
|
10
|
+
export declare function aggregateAlias(fn: string, field?: string): string;
|
|
2
11
|
export interface EntityDataOptions {
|
|
3
12
|
/**
|
|
4
13
|
* Look up a collection's config by slug, to derive row addresses from its
|
|
@@ -10,20 +19,10 @@ export interface EntityDataOptions {
|
|
|
10
19
|
*/
|
|
11
20
|
resolveCollection?: (slug: string) => {
|
|
12
21
|
properties?: Record<string, unknown>;
|
|
22
|
+
relations?: unknown[];
|
|
23
|
+
slug?: string;
|
|
13
24
|
} | undefined;
|
|
14
25
|
}
|
|
15
|
-
/**
|
|
16
|
-
* Build a `RebaseData` object from a `DataDriver` using JavaScript Proxy.
|
|
17
|
-
*
|
|
18
|
-
* This is the key bridge: any property access like `data.products` returns
|
|
19
|
-
* a `CollectionAccessor` backed by the underlying DataDriver, without
|
|
20
|
-
* needing per-collection code generation.
|
|
21
|
-
*
|
|
22
|
-
* @example
|
|
23
|
-
* const data = buildRebaseData(driver);
|
|
24
|
-
* await data.products.create({ name: "Camera", price: 299 });
|
|
25
|
-
* const { data: items } = await data.products.find({ where: { status: ["==", "published"] } });
|
|
26
|
-
*/
|
|
27
26
|
export declare function buildRebaseData(driver: DataDriver, options?: EntityDataOptions): RebaseData;
|
|
28
27
|
/**
|
|
29
28
|
* Wrap a flat {@link RebaseSdkData} into a Entity-shaped {@link RebaseData}.
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import type { OrderByTuple } from "@rebasepro/types";
|
|
2
|
+
/**
|
|
3
|
+
* The keyset-cursor wire codec.
|
|
4
|
+
*
|
|
5
|
+
* ## Why this is one module
|
|
6
|
+
*
|
|
7
|
+
* Keyset pagination was implemented three times and reachable once. The driver
|
|
8
|
+
* has a NULL-correct multi-key comparison (`FetchService.buildKeysetComparison`)
|
|
9
|
+
* that only a WebSocket `startAfter` could reach; REST could not seek at all;
|
|
10
|
+
* and the SDK's `iterate({cursor})` re-implemented a *single*-column keyset as a
|
|
11
|
+
* `where` clause, which threw on any multi-key sort and silently dropped rows
|
|
12
|
+
* whose sort value was NULL. Three implementations, three answers to "what is
|
|
13
|
+
* page two".
|
|
14
|
+
*
|
|
15
|
+
* There is now one. The driver's comparison is the implementation; this module
|
|
16
|
+
* is the only thing that says how a cursor is written down, and every transport
|
|
17
|
+
* — the REST `?after=`, the WebSocket `startAfter`, the SDK's `iterate()` —
|
|
18
|
+
* carries the string this produces and hands it back unread.
|
|
19
|
+
*
|
|
20
|
+
* ## What a cursor holds
|
|
21
|
+
*
|
|
22
|
+
* The sort keys the query was ordered by, the last served row's value for each
|
|
23
|
+
* of them, and that row's id. The keys travel *with* the values because a
|
|
24
|
+
* cursor that carried only values would be silently reinterpretable: paging a
|
|
25
|
+
* `created_at DESC` listing and then asking for `title ASC` would seek on the
|
|
26
|
+
* dates as though they were titles. Carrying the keys makes that a refusal
|
|
27
|
+
* ({@link CursorMismatchError}) rather than a page of arbitrary rows.
|
|
28
|
+
*
|
|
29
|
+
* ## Opacity
|
|
30
|
+
*
|
|
31
|
+
* The encoding is base64url of JSON, and it is **not** API. It is opaque so it
|
|
32
|
+
* can change — adding a key, changing how a value is tagged — without every
|
|
33
|
+
* client that learned to read it breaking. Nothing outside this file parses it.
|
|
34
|
+
*
|
|
35
|
+
* @module
|
|
36
|
+
*/
|
|
37
|
+
/** The decoded contents of a cursor. */
|
|
38
|
+
export interface DecodedCursor {
|
|
39
|
+
/** The sort keys the cursor was produced under, in order of significance. */
|
|
40
|
+
orderBy: OrderByTuple[];
|
|
41
|
+
/** The last served row's value for each sort key, by field name. */
|
|
42
|
+
values: Record<string, unknown>;
|
|
43
|
+
/** The last served row's id, which breaks ties on the last key. */
|
|
44
|
+
id: unknown;
|
|
45
|
+
}
|
|
46
|
+
/** A cursor that cannot be read at all — truncated, re-encoded, or invented. */
|
|
47
|
+
export declare class CursorError extends Error {
|
|
48
|
+
readonly code = "INVALID_CURSOR";
|
|
49
|
+
constructor(detail: string);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* A cursor that reads fine but describes a different query.
|
|
53
|
+
*
|
|
54
|
+
* Separate from {@link CursorError} because the fix is different: this one is
|
|
55
|
+
* not a corrupt string, it is a correct cursor used against a sort it was not
|
|
56
|
+
* produced under. Seeking anyway would return rows in an order nobody asked
|
|
57
|
+
* for, and — worse — would look like it worked.
|
|
58
|
+
*/
|
|
59
|
+
export declare class CursorMismatchError extends Error {
|
|
60
|
+
readonly code = "CURSOR_ORDER_MISMATCH";
|
|
61
|
+
constructor(cursorKeys: string[], queryKeys: string[]);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Encode "everything strictly after this row, in this order".
|
|
65
|
+
*
|
|
66
|
+
* @param orderBy the sort keys the listing ran under, in order of significance
|
|
67
|
+
* @param row the last row served, which the next page picks up after
|
|
68
|
+
* @param id that row's id — the tiebreaker every keyset comparison ends on
|
|
69
|
+
* @returns the opaque cursor, or `undefined` when no cursor can describe the
|
|
70
|
+
* page. That is not a failure: a listing sorted by relevance has no stored
|
|
71
|
+
* value to compare a later page against (scores are computed per query and
|
|
72
|
+
* are not on the same scale between two of them), so it pages by offset and
|
|
73
|
+
* `meta.nextCursor` is simply absent.
|
|
74
|
+
*/
|
|
75
|
+
export declare function encodeCursor(orderBy: OrderByTuple[] | undefined, row: Record<string, unknown>, id: unknown): string | undefined;
|
|
76
|
+
/**
|
|
77
|
+
* Read a cursor produced by {@link encodeCursor}.
|
|
78
|
+
*
|
|
79
|
+
* @throws {CursorError} when the string is not a cursor this codec wrote.
|
|
80
|
+
*/
|
|
81
|
+
export declare function decodeCursor(raw: string): DecodedCursor;
|
|
82
|
+
/**
|
|
83
|
+
* The `orderBy` a request should run under, given a cursor and whatever sort
|
|
84
|
+
* the request itself named.
|
|
85
|
+
*
|
|
86
|
+
* A request that names no sort **adopts the cursor's** — that is what makes
|
|
87
|
+
* `find({ after })` work without restating the `orderBy` from the previous
|
|
88
|
+
* call, and it cannot be wrong, since the cursor is the only sort in play.
|
|
89
|
+
* A request that names one must name the *same* one, key for key, direction for
|
|
90
|
+
* direction, nulls for nulls; anything else is {@link CursorMismatchError}.
|
|
91
|
+
*
|
|
92
|
+
* @throws {CursorMismatchError}
|
|
93
|
+
*/
|
|
94
|
+
export declare function reconcileCursorOrder(cursor: DecodedCursor, requested: OrderByTuple[] | undefined): OrderByTuple[];
|
|
95
|
+
/**
|
|
96
|
+
* The `startAfter` shape the driver contract takes, built from a cursor.
|
|
97
|
+
*
|
|
98
|
+
* The driver has always accepted `{ id, values }`; this is the one place that
|
|
99
|
+
* shape is produced, so the REST route and the WebSocket ingress cannot drift
|
|
100
|
+
* into two spellings of the same seek.
|
|
101
|
+
*/
|
|
102
|
+
export declare function cursorToStartAfter(cursor: DecodedCursor): Record<string, unknown>;
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import type { FilterValues, IncludeSpec, LogicalCondition, OrderByTuple } from "@rebasepro/types";
|
|
2
|
+
/**
|
|
3
|
+
* The `include` codec: one shape, whatever spelling it arrived in.
|
|
4
|
+
*
|
|
5
|
+
* `include` reaches the driver by four routes — the REST `?include=` parameter,
|
|
6
|
+
* a WebSocket subscribe frame, the SDK's `include(...)`, and the admin panel's
|
|
7
|
+
* "all relations" — and each used to hand the driver something slightly
|
|
8
|
+
* different. This normalises all four to one tree, so the fetch pipeline has a
|
|
9
|
+
* single thing to read and `find()`, `findById()` and `listen()` cannot disagree
|
|
10
|
+
* about what "include the author" means.
|
|
11
|
+
*
|
|
12
|
+
* @module
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* One relation to load, and how.
|
|
16
|
+
*
|
|
17
|
+
* `children` is the nesting: `comments.author` is a `comments` node with an
|
|
18
|
+
* `author` child. Every other field narrows the rows *of this relation* — the
|
|
19
|
+
* same knobs a top-level query has, which is the point.
|
|
20
|
+
*/
|
|
21
|
+
export interface IncludeNode {
|
|
22
|
+
/** Rows to load per parent row. */
|
|
23
|
+
limit?: number;
|
|
24
|
+
/** Filter over the related rows. */
|
|
25
|
+
where?: FilterValues<string>;
|
|
26
|
+
/** An `and`/`or`/`not` group over the related rows. */
|
|
27
|
+
logical?: LogicalCondition;
|
|
28
|
+
/** Sort for the related rows. */
|
|
29
|
+
orderBy?: OrderByTuple[];
|
|
30
|
+
/** Columns of the related row to return. */
|
|
31
|
+
fields?: string[];
|
|
32
|
+
/** Relations of the related row, loaded in turn. */
|
|
33
|
+
children: Record<string, IncludeNode>;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* A whole `include` request: the tree, plus whether the caller asked for
|
|
37
|
+
* *every* relation.
|
|
38
|
+
*
|
|
39
|
+
* The wildcard is kept as a flag rather than expanded into names here, because
|
|
40
|
+
* expanding it needs the collection — which this package does not have. The
|
|
41
|
+
* driver expands it against the relations it actually resolved.
|
|
42
|
+
*/
|
|
43
|
+
export interface NormalizedInclude {
|
|
44
|
+
/** `include=*` — every relation of the collection, one hop deep. */
|
|
45
|
+
wildcard: boolean;
|
|
46
|
+
/** The named relations. Empty when `wildcard` is set alone. */
|
|
47
|
+
tree: Record<string, IncludeNode>;
|
|
48
|
+
}
|
|
49
|
+
/** An `include` that cannot be read, as opposed to one naming a relation that does not exist. */
|
|
50
|
+
export declare class IncludeSpecError extends Error {
|
|
51
|
+
readonly code: string;
|
|
52
|
+
constructor(detail: string, code?: string);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Collapse any {@link IncludeSpec} spelling into one tree.
|
|
56
|
+
*
|
|
57
|
+
* `["author", "comments.author"]` and
|
|
58
|
+
* `{ author: true, comments: { include: { author: true } } }` normalize to the
|
|
59
|
+
* same value — which is the whole point: the REST parameter can only carry the
|
|
60
|
+
* flat spelling, the SDK prefers the tree, and the driver should never learn
|
|
61
|
+
* about either.
|
|
62
|
+
*
|
|
63
|
+
* @throws {IncludeSpecError} for a shape that is not an include at all, or one
|
|
64
|
+
* that nests past {@link MAX_INCLUDE_DEPTH}.
|
|
65
|
+
*/
|
|
66
|
+
export declare function normalizeInclude(spec?: IncludeSpec): NormalizedInclude | undefined;
|
|
67
|
+
/**
|
|
68
|
+
* Every relation name a tree names, as dotted paths — `["comments",
|
|
69
|
+
* "comments.author"]`.
|
|
70
|
+
*
|
|
71
|
+
* Used to report which names an `include` asked for when one of them is not a
|
|
72
|
+
* relation, and to serialize a tree that carries no per-relation options back
|
|
73
|
+
* to the flat wire spelling.
|
|
74
|
+
*/
|
|
75
|
+
export declare function includePaths(tree: Record<string, IncludeNode>, prefix?: string): string[];
|
|
76
|
+
/**
|
|
77
|
+
* The relation names an `include` asks for at the top level.
|
|
78
|
+
*
|
|
79
|
+
* `["author", "comments.author"]` and `{author: true, comments: {...}}` both
|
|
80
|
+
* answer `["author", "comments"]` — a *hop*, not a path, because the only
|
|
81
|
+
* consumer is `?fields=`, which names keys on the row being returned and a
|
|
82
|
+
* nested relation is not one of those.
|
|
83
|
+
*
|
|
84
|
+
* Derived rather than passed: `include` has four spellings and three of them
|
|
85
|
+
* are not a `string[]`, so every consumer that wants the plain names either
|
|
86
|
+
* calls this or reimplements the flattening.
|
|
87
|
+
*/
|
|
88
|
+
export declare function topLevelIncludeNames(spec?: IncludeSpec): string[];
|
|
89
|
+
/**
|
|
90
|
+
* Serialize an {@link IncludeSpec} for the REST `?include=` parameter.
|
|
91
|
+
*
|
|
92
|
+
* Two spellings, and which one is used is decided by the request rather than
|
|
93
|
+
* chosen:
|
|
94
|
+
*
|
|
95
|
+
* - **Comma-separated dotted paths** — `include=author,comments.author`. What a
|
|
96
|
+
* plain include is, what a human types, and what every existing client sends.
|
|
97
|
+
* - **JSON**, when any relation carries options — `include={"comments":{"limit":5,
|
|
98
|
+
* "include":{"author":true}}}`. The flat spelling has nowhere to put a
|
|
99
|
+
* `limit`, and inventing a punctuation for it (`comments(limit:5)`) would be a
|
|
100
|
+
* third grammar to learn beside the two this API already has.
|
|
101
|
+
*
|
|
102
|
+
* The server accepts both on every list and get route, and tells them apart the
|
|
103
|
+
* same way this does: a value starting with `{` is JSON.
|
|
104
|
+
*/
|
|
105
|
+
export declare function serializeInclude(spec?: IncludeSpec): string | undefined;
|
|
106
|
+
/**
|
|
107
|
+
* A normalized tree, back in the {@link IncludeSpec} spelling a caller writes.
|
|
108
|
+
*
|
|
109
|
+
* The round trip is what lets a builder accumulate `include` calls: normalize
|
|
110
|
+
* each, merge, and hand the result back as a spec the next layer can normalize
|
|
111
|
+
* again. Idempotent, so doing it twice changes nothing.
|
|
112
|
+
*/
|
|
113
|
+
export declare function denormalizeInclude(normalized: NormalizedInclude): IncludeSpec;
|
|
114
|
+
/**
|
|
115
|
+
* Combine several `include` requests into one.
|
|
116
|
+
*
|
|
117
|
+
* Repeated `.include(...)` calls on a query builder are additive: each names
|
|
118
|
+
* more of the graph to load, and a later one must not discard what an earlier
|
|
119
|
+
* one asked for. Assigning instead of merging is why `.include("author")
|
|
120
|
+
* .include("tags")` used to load only tags.
|
|
121
|
+
*/
|
|
122
|
+
export declare function mergeIncludeSpecs(existing: IncludeSpec | undefined, additions: (string | IncludeSpec)[]): IncludeSpec | undefined;
|
|
123
|
+
/**
|
|
124
|
+
* Read the REST `?include=` parameter, in either spelling.
|
|
125
|
+
*
|
|
126
|
+
* @throws {IncludeSpecError} for malformed JSON or a tree that nests too deep.
|
|
127
|
+
*/
|
|
128
|
+
export declare function deserializeInclude(raw?: string): IncludeSpec | undefined;
|
package/dist/data/paginate.d.ts
CHANGED
|
@@ -26,12 +26,15 @@ export type PaginationErrorCode =
|
|
|
26
26
|
"max-rows"
|
|
27
27
|
/** The walk made its maximum number of requests without the server finishing. */
|
|
28
28
|
| "max-pages"
|
|
29
|
-
/**
|
|
29
|
+
/**
|
|
30
|
+
* The server said there was another page but issued no cursor to reach it.
|
|
31
|
+
*
|
|
32
|
+
* A query whose ordering has no stored value to seek on — relevance — is the
|
|
33
|
+
* case that produces this. Page it by offset instead.
|
|
34
|
+
*/
|
|
30
35
|
| "cursor-missing"
|
|
31
|
-
/** Two consecutive pages
|
|
32
|
-
| "cursor-stalled"
|
|
33
|
-
/** A `cursor` was asked for on one column while `orderBy` sorted by another. */
|
|
34
|
-
| "cursor-order-mismatch";
|
|
36
|
+
/** Two consecutive pages returned the same cursor, so the walk cannot advance. */
|
|
37
|
+
| "cursor-stalled";
|
|
35
38
|
/**
|
|
36
39
|
* Thrown when a walk stops for a reason the caller needs to know about.
|
|
37
40
|
*
|
|
@@ -1,6 +1,21 @@
|
|
|
1
1
|
import { CollectionAccessor, FilterCondition, FindResponse, LogicalCondition, QueryBuilderInterface, WhereFilterOp, WhereValueFor, type ComputedSortField } from "@rebasepro/types";
|
|
2
2
|
export declare function or(...conditions: (FilterCondition | LogicalCondition)[]): LogicalCondition;
|
|
3
3
|
export declare function and(...conditions: (FilterCondition | LogicalCondition)[]): LogicalCondition;
|
|
4
|
+
/**
|
|
5
|
+
* Negate a group: `not(a)` is `NOT a`, and `not(a, b)` is `NOT (a AND b)`.
|
|
6
|
+
*
|
|
7
|
+
* The conjunction, not the disjunction — one rule, stated on
|
|
8
|
+
* {@link LogicalCondition} and applied identically by the wire codec, the REST
|
|
9
|
+
* `?not=` parameter and every driver compiler. Groups nest, so De Morgan's
|
|
10
|
+
* other half is `not(or(a, b))`.
|
|
11
|
+
*
|
|
12
|
+
* It compiles to a real SQL `NOT (...)` rather than to inverted operators,
|
|
13
|
+
* which matters more than it looks: SQL is three-valued, so `NOT (a AND b)` and
|
|
14
|
+
* `(NOT a) OR (NOT b)` stop agreeing the moment a NULL is involved, and only
|
|
15
|
+
* one of them is the query the caller wrote. It also means a negation includes
|
|
16
|
+
* rows whose column is NULL — which is what `NOT` means.
|
|
17
|
+
*/
|
|
18
|
+
export declare function not(...conditions: (FilterCondition | LogicalCondition)[]): LogicalCondition;
|
|
4
19
|
export declare function cond(column: string, operator: WhereFilterOp, value: unknown): FilterCondition;
|
|
5
20
|
export declare class QueryBuilder<M extends Record<string, unknown> = Record<string, unknown>> implements QueryBuilderInterface<M> {
|
|
6
21
|
private collection;
|
package/dist/index.d.ts
CHANGED
|
@@ -5,6 +5,8 @@ export * from "./data/buildRoutedRebaseData.js";
|
|
|
5
5
|
export * from "./data/resolveDataSource.js";
|
|
6
6
|
export * from "./data/query_builder.js";
|
|
7
7
|
export * from "./data/paginate.js";
|
|
8
|
+
export * from "./data/cursor.js";
|
|
9
|
+
export * from "./data/include-spec.js";
|
|
8
10
|
export * from "./data/filter-conditions.js";
|
|
9
11
|
export * from "./data/filter-dialect.js";
|
|
10
12
|
export * from "./data/sort-dialect.js";
|