@generazioneai/genquery 0.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/LICENSE +28 -0
- package/README.md +218 -0
- package/dist/adapters/base.d.ts +32 -0
- package/dist/adapters/base.d.ts.map +1 -0
- package/dist/adapters/base.js +3 -0
- package/dist/adapters/base.js.map +1 -0
- package/dist/adapters/typeorm/adapter.d.ts +35 -0
- package/dist/adapters/typeorm/adapter.d.ts.map +1 -0
- package/dist/adapters/typeorm/adapter.js +111 -0
- package/dist/adapters/typeorm/adapter.js.map +1 -0
- package/dist/adapters/typeorm/aliases.d.ts +25 -0
- package/dist/adapters/typeorm/aliases.d.ts.map +1 -0
- package/dist/adapters/typeorm/aliases.js +47 -0
- package/dist/adapters/typeorm/aliases.js.map +1 -0
- package/dist/adapters/typeorm/create.d.ts +29 -0
- package/dist/adapters/typeorm/create.d.ts.map +1 -0
- package/dist/adapters/typeorm/create.js +29 -0
- package/dist/adapters/typeorm/create.js.map +1 -0
- package/dist/adapters/typeorm/escape.d.ts +5 -0
- package/dist/adapters/typeorm/escape.d.ts.map +1 -0
- package/dist/adapters/typeorm/escape.js +13 -0
- package/dist/adapters/typeorm/escape.js.map +1 -0
- package/dist/adapters/typeorm/index.d.ts +4 -0
- package/dist/adapters/typeorm/index.d.ts.map +1 -0
- package/dist/adapters/typeorm/index.js +10 -0
- package/dist/adapters/typeorm/index.js.map +1 -0
- package/dist/adapters/typeorm/joins.d.ts +29 -0
- package/dist/adapters/typeorm/joins.d.ts.map +1 -0
- package/dist/adapters/typeorm/joins.js +91 -0
- package/dist/adapters/typeorm/joins.js.map +1 -0
- package/dist/adapters/typeorm/params.d.ts +8 -0
- package/dist/adapters/typeorm/params.d.ts.map +1 -0
- package/dist/adapters/typeorm/params.js +15 -0
- package/dist/adapters/typeorm/params.js.map +1 -0
- package/dist/adapters/typeorm/schema-from-typeorm.d.ts +50 -0
- package/dist/adapters/typeorm/schema-from-typeorm.d.ts.map +1 -0
- package/dist/adapters/typeorm/schema-from-typeorm.js +170 -0
- package/dist/adapters/typeorm/schema-from-typeorm.js.map +1 -0
- package/dist/adapters/typeorm/where.d.ts +26 -0
- package/dist/adapters/typeorm/where.d.ts.map +1 -0
- package/dist/adapters/typeorm/where.js +297 -0
- package/dist/adapters/typeorm/where.js.map +1 -0
- package/dist/datetime.d.ts +4 -0
- package/dist/datetime.d.ts.map +1 -0
- package/dist/datetime.js +86 -0
- package/dist/datetime.js.map +1 -0
- package/dist/engine.d.ts +52 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +52 -0
- package/dist/engine.js.map +1 -0
- package/dist/errors.d.ts +10 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +16 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +28 -0
- package/dist/index.js.map +1 -0
- package/dist/parsed.d.ts +127 -0
- package/dist/parsed.d.ts.map +1 -0
- package/dist/parsed.js +8 -0
- package/dist/parsed.js.map +1 -0
- package/dist/parser.d.ts +5 -0
- package/dist/parser.d.ts.map +1 -0
- package/dist/parser.js +404 -0
- package/dist/parser.js.map +1 -0
- package/dist/schema.d.ts +57 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +36 -0
- package/dist/schema.js.map +1 -0
- package/dist/types.d.ts +180 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +15 -0
- package/dist/types.js.map +1 -0
- package/package.json +70 -0
- package/spec.md +221 -0
package/dist/schema.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Schema describes what fields and relations exist on each entity, so the
|
|
4
|
+
* parser can validate queries and the adapter can produce correct joins and
|
|
5
|
+
* conditions. The schema is intentionally ORM-agnostic.
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.SchemaError = void 0;
|
|
9
|
+
exports.getEntity = getEntity;
|
|
10
|
+
exports.getField = getField;
|
|
11
|
+
exports.getRelation = getRelation;
|
|
12
|
+
exports.primaryKeyOf = primaryKeyOf;
|
|
13
|
+
class SchemaError extends Error {
|
|
14
|
+
constructor(message) {
|
|
15
|
+
super(message);
|
|
16
|
+
this.name = "SchemaError";
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
exports.SchemaError = SchemaError;
|
|
20
|
+
function getEntity(schema, name) {
|
|
21
|
+
const entity = schema.entities[name];
|
|
22
|
+
if (!entity) {
|
|
23
|
+
throw new SchemaError(`Unknown entity '${name}' in schema`);
|
|
24
|
+
}
|
|
25
|
+
return entity;
|
|
26
|
+
}
|
|
27
|
+
function getField(schema, entityName, field) {
|
|
28
|
+
return getEntity(schema, entityName).fields[field];
|
|
29
|
+
}
|
|
30
|
+
function getRelation(schema, entityName, field) {
|
|
31
|
+
return getEntity(schema, entityName).relations?.[field];
|
|
32
|
+
}
|
|
33
|
+
function primaryKeyOf(entity) {
|
|
34
|
+
return entity.primaryKey ?? "id";
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;;AA2DH,8BAMC;AAED,4BAMC;AAED,kCAMC;AAED,oCAEC;AAjCD,MAAa,WAAY,SAAQ,KAAK;IACpC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC;IAC5B,CAAC;CACF;AALD,kCAKC;AAED,SAAgB,SAAS,CAAC,MAAc,EAAE,IAAY;IACpD,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IACrC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,WAAW,CAAC,mBAAmB,IAAI,aAAa,CAAC,CAAC;IAC9D,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAgB,QAAQ,CACtB,MAAc,EACd,UAAkB,EAClB,KAAa;IAEb,OAAO,SAAS,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACrD,CAAC;AAED,SAAgB,WAAW,CACzB,MAAc,EACd,UAAkB,EAClB,KAAa;IAEb,OAAO,SAAS,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,SAAS,EAAE,CAAC,KAAK,CAAC,CAAC;AAC1D,CAAC;AAED,SAAgB,YAAY,CAAC,MAAwB;IACnD,OAAO,MAAM,CAAC,UAAU,IAAI,IAAI,CAAC;AACnC,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Raw input types as accepted from the wire.
|
|
3
|
+
*
|
|
4
|
+
* These mirror the spec literally. Anything received from the frontend should
|
|
5
|
+
* conform to `GenQueryInput`. Use the parser to validate + normalize into the
|
|
6
|
+
* `Parsed*` types defined in `./parsed.ts`.
|
|
7
|
+
*
|
|
8
|
+
* The input types are generic on an entity type `T`. With `T = unknown`
|
|
9
|
+
* (default) you get the loose, untyped form. Pass a concrete entity class
|
|
10
|
+
* (`GenQueryInput<User>`) to get autocomplete and value-shape checking for
|
|
11
|
+
* fields and relations.
|
|
12
|
+
*/
|
|
13
|
+
export type SortOrder = "asc" | "desc";
|
|
14
|
+
export type StringSearchMode = "splitword" | "exact" | "nativeregex";
|
|
15
|
+
export type NumericOp = ">" | "<" | ">=" | "<=" | "==";
|
|
16
|
+
/** Timezone offset. "Z" (UTC) or "+HH:MM" / "-HH:MM" (also "+HHMM" / "+HH"). */
|
|
17
|
+
export type OffsetInput = string;
|
|
18
|
+
export interface DateTimeObjectInput {
|
|
19
|
+
year?: number;
|
|
20
|
+
month?: number;
|
|
21
|
+
day?: number;
|
|
22
|
+
hours?: number;
|
|
23
|
+
minutes?: number;
|
|
24
|
+
seconds?: number;
|
|
25
|
+
offset?: OffsetInput;
|
|
26
|
+
}
|
|
27
|
+
export type DateTimeInput = string | DateTimeObjectInput;
|
|
28
|
+
export interface NumericComparisonInput {
|
|
29
|
+
operation?: NumericOp;
|
|
30
|
+
value: number;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Null-presence check, valid on any primitive field type (string, number,
|
|
34
|
+
* boolean, date, enum).
|
|
35
|
+
*
|
|
36
|
+
* { isNull: true } → IS NULL
|
|
37
|
+
* { isNull: false } → IS NOT NULL
|
|
38
|
+
*/
|
|
39
|
+
export interface NullCheckInput {
|
|
40
|
+
isNull: boolean;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Empty-presence check, valid only on string fields. "Empty" means NULL or
|
|
44
|
+
* the empty string `''`.
|
|
45
|
+
*
|
|
46
|
+
* { isEmpty: true } → (col IS NULL OR col = '')
|
|
47
|
+
* { isEmpty: false } → (col IS NOT NULL AND col <> '')
|
|
48
|
+
*/
|
|
49
|
+
export interface EmptyCheckInput {
|
|
50
|
+
isEmpty: boolean;
|
|
51
|
+
}
|
|
52
|
+
export interface StringSearchObjectInput {
|
|
53
|
+
mode?: StringSearchMode;
|
|
54
|
+
contained?: boolean;
|
|
55
|
+
/** Case-sensitive comparison. Defaults to `false` (case-insensitive). */
|
|
56
|
+
caseSensitive?: boolean;
|
|
57
|
+
value: string;
|
|
58
|
+
}
|
|
59
|
+
export type StringSearchInput = string | StringSearchObjectInput | NullCheckInput | EmptyCheckInput;
|
|
60
|
+
export interface DateRangeInput {
|
|
61
|
+
before?: DateTimeInput;
|
|
62
|
+
after?: DateTimeInput;
|
|
63
|
+
}
|
|
64
|
+
export type DateSearchInput = DateTimeInput | DateRangeInput | NullCheckInput;
|
|
65
|
+
export type NumberSearchInput = number | NumericComparisonInput | NullCheckInput;
|
|
66
|
+
export type BoolSearchInput = boolean | NullCheckInput;
|
|
67
|
+
/** Detects the `any` type, which otherwise satisfies every conditional. */
|
|
68
|
+
type IsAny<T> = 0 extends 1 & T ? true : false;
|
|
69
|
+
/** True iff T is `unknown` or `any` — i.e. caller didn't constrain. */
|
|
70
|
+
type IsLoose<T> = IsAny<T> extends true ? true : unknown extends T ? true : false;
|
|
71
|
+
type Prim = string | number | boolean | Date;
|
|
72
|
+
/**
|
|
73
|
+
* Picks the right search value shape for a single property's TS type.
|
|
74
|
+
*
|
|
75
|
+
* For string literal unions (enum-style), the value is strictly constrained
|
|
76
|
+
* to the union members — both the enum member form (`UserRoles.admin`) and the
|
|
77
|
+
* matching string literal (`"admin"`) compile. Arbitrary `string` variables
|
|
78
|
+
* are rejected at compile time; cast them if you really need to pass them.
|
|
79
|
+
*/
|
|
80
|
+
type SearchValueFor<V> = [
|
|
81
|
+
NonNullable<V>
|
|
82
|
+
] extends [Date] ? DateSearchInput : [
|
|
83
|
+
NonNullable<V>
|
|
84
|
+
] extends [string] ? [string] extends [NonNullable<V>] ? StringSearchInput : NonNullable<V> | `${NonNullable<V> & string}` | NullCheckInput : [
|
|
85
|
+
NonNullable<V>
|
|
86
|
+
] extends [number] ? NumberSearchInput : [
|
|
87
|
+
NonNullable<V>
|
|
88
|
+
] extends [boolean] ? BoolSearchInput : [
|
|
89
|
+
NonNullable<V>
|
|
90
|
+
] extends [(infer U)[]] ? RelationFilterInput<NonNullable<U>> : [
|
|
91
|
+
NonNullable<V>
|
|
92
|
+
] extends [object] ? RelationFilterInput<NonNullable<V>> : unknown;
|
|
93
|
+
/**
|
|
94
|
+
* Wrapper for relation filters with explicit cardinality operators. The short
|
|
95
|
+
* form (a plain `SearchByInput`) is treated as implicit `some`.
|
|
96
|
+
*
|
|
97
|
+
* posts: { title: "x" } // implicit some
|
|
98
|
+
* posts: { some: { title: "x" } } // explicit some
|
|
99
|
+
* posts: { every: { published: true } }
|
|
100
|
+
* posts: { none: { draft: true } }
|
|
101
|
+
* posts: { some: { ... }, none: { ... } } // multiple ops, AND-ed
|
|
102
|
+
*/
|
|
103
|
+
export type RelationFilterInput<T = unknown> = SearchByInput<T> | {
|
|
104
|
+
some?: SearchByInput<T>;
|
|
105
|
+
every?: SearchByInput<T>;
|
|
106
|
+
none?: SearchByInput<T>;
|
|
107
|
+
};
|
|
108
|
+
/** Keys of T whose value is a primitive (string/number/boolean/Date). */
|
|
109
|
+
type FieldKeysOf<T> = {
|
|
110
|
+
[K in keyof T]-?: [NonNullable<T[K]>] extends [Prim] ? K : never;
|
|
111
|
+
}[keyof T];
|
|
112
|
+
/** Keys of T whose value is a relation (array of object, or object). */
|
|
113
|
+
type RelationKeysOf<T> = {
|
|
114
|
+
[K in keyof T]-?: [NonNullable<T[K]>] extends [Prim] ? never : [NonNullable<T[K]>] extends [object] ? K : never;
|
|
115
|
+
}[keyof T];
|
|
116
|
+
/** Strips relation arrays down to their element type. */
|
|
117
|
+
type RelationTargetOf<T, K extends keyof T> = [
|
|
118
|
+
NonNullable<T[K]>
|
|
119
|
+
] extends [(infer U)[]] ? NonNullable<U> : [
|
|
120
|
+
NonNullable<T[K]>
|
|
121
|
+
] extends [object] ? NonNullable<T[K]> : never;
|
|
122
|
+
/**
|
|
123
|
+
* `searchBy` is a recursive object whose keys are field names of the current
|
|
124
|
+
* entity, relation names of the current entity, or the literal `OR`.
|
|
125
|
+
*
|
|
126
|
+
* Values depend on the kind of field:
|
|
127
|
+
* - string field: `StringSearchInput`
|
|
128
|
+
* - number field: `NumberSearchInput`
|
|
129
|
+
* - boolean field: `BoolSearchInput`
|
|
130
|
+
* - date field: `DateSearchInput`
|
|
131
|
+
* - relation: a nested `SearchByInput` against the related entity
|
|
132
|
+
*
|
|
133
|
+
* The `OR` key is special: it takes an array of full `SearchByInput`s; matches
|
|
134
|
+
* if any of them matches. All non-`OR` entries combine with AND.
|
|
135
|
+
*/
|
|
136
|
+
export type SearchByInput<T = unknown> = IsLoose<T> extends true ? LooseSearchByInput : TypedSearchByInput<T>;
|
|
137
|
+
export type LooseSearchByInput = {
|
|
138
|
+
OR?: LooseSearchByInput[];
|
|
139
|
+
[field: string]: unknown;
|
|
140
|
+
};
|
|
141
|
+
export type TypedSearchByInput<T> = {
|
|
142
|
+
OR?: TypedSearchByInput<T>[];
|
|
143
|
+
} & {
|
|
144
|
+
[K in Exclude<keyof T, "OR"> as [NonNullable<T[K]>] extends [Prim | object] ? K : never]?: SearchValueFor<T[K]>;
|
|
145
|
+
};
|
|
146
|
+
export interface OrderByObjectInput<TField extends string = string> {
|
|
147
|
+
field: TField;
|
|
148
|
+
order?: SortOrder;
|
|
149
|
+
}
|
|
150
|
+
export type OrderByInput<T = unknown> = IsLoose<T> extends true ? string | OrderByObjectInput<string> : FieldKeysOf<T> & string extends infer F extends string ? F | OrderByObjectInput<F> : never;
|
|
151
|
+
export type IncludeFieldSpec = boolean;
|
|
152
|
+
export type IncludeRelationSpec<U = unknown> = IsLoose<U> extends true ? "all" | {
|
|
153
|
+
[field: string]: IncludeFieldSpec;
|
|
154
|
+
} : "all" | {
|
|
155
|
+
[K in FieldKeysOf<U>]?: IncludeFieldSpec;
|
|
156
|
+
};
|
|
157
|
+
export type IncludeInput<T = unknown> = IsLoose<T> extends true ? "none" | "all" | {
|
|
158
|
+
[relation: string]: IncludeRelationSpec;
|
|
159
|
+
} : "none" | "all" | {
|
|
160
|
+
[K in RelationKeysOf<T>]?: IncludeRelationSpec<RelationTargetOf<T, K>>;
|
|
161
|
+
};
|
|
162
|
+
export type SelectInput<T = unknown> = IsLoose<T> extends true ? "none" | "all" | {
|
|
163
|
+
[field: string]: boolean;
|
|
164
|
+
} : "none" | "all" | {
|
|
165
|
+
[K in FieldKeysOf<T>]?: boolean;
|
|
166
|
+
};
|
|
167
|
+
export interface PaginationObjectInput {
|
|
168
|
+
page?: number;
|
|
169
|
+
perPage?: number;
|
|
170
|
+
}
|
|
171
|
+
export type PaginationInput = "all" | "first" | PaginationObjectInput;
|
|
172
|
+
export interface GenQueryInput<T = unknown> {
|
|
173
|
+
orderBy?: OrderByInput<T>;
|
|
174
|
+
searchBy?: SearchByInput<T>;
|
|
175
|
+
include?: IncludeInput<T>;
|
|
176
|
+
select?: SelectInput<T>;
|
|
177
|
+
pagination?: PaginationInput;
|
|
178
|
+
}
|
|
179
|
+
export {};
|
|
180
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Raw input types as accepted from the wire.
|
|
4
|
+
*
|
|
5
|
+
* These mirror the spec literally. Anything received from the frontend should
|
|
6
|
+
* conform to `GenQueryInput`. Use the parser to validate + normalize into the
|
|
7
|
+
* `Parsed*` types defined in `./parsed.ts`.
|
|
8
|
+
*
|
|
9
|
+
* The input types are generic on an entity type `T`. With `T = unknown`
|
|
10
|
+
* (default) you get the loose, untyped form. Pass a concrete entity class
|
|
11
|
+
* (`GenQueryInput<User>`) to get autocomplete and value-shape checking for
|
|
12
|
+
* fields and relations.
|
|
13
|
+
*/
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;GAWG"}
|
package/package.json
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@generazioneai/genquery",
|
|
3
|
+
"publishConfig": {
|
|
4
|
+
"access": "public"
|
|
5
|
+
},
|
|
6
|
+
"version": "0.1.0",
|
|
7
|
+
"description": "ORM-agnostic JSON query language with pluggable adapters (TypeORM, ...)",
|
|
8
|
+
"license": "BSD-3-Clause",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/GenerazioneAI-SRL/genquery.git"
|
|
12
|
+
},
|
|
13
|
+
"homepage": "https://github.com/GenerazioneAI-SRL/genquery#readme",
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/GenerazioneAI-SRL/genquery/issues"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"query",
|
|
19
|
+
"query-builder",
|
|
20
|
+
"orm",
|
|
21
|
+
"typeorm",
|
|
22
|
+
"json-query",
|
|
23
|
+
"dsl",
|
|
24
|
+
"search",
|
|
25
|
+
"filter",
|
|
26
|
+
"pagination"
|
|
27
|
+
],
|
|
28
|
+
"main": "dist/index.js",
|
|
29
|
+
"types": "dist/index.d.ts",
|
|
30
|
+
"exports": {
|
|
31
|
+
".": {
|
|
32
|
+
"types": "./dist/index.d.ts",
|
|
33
|
+
"import": "./dist/index.js",
|
|
34
|
+
"require": "./dist/index.js"
|
|
35
|
+
},
|
|
36
|
+
"./typeorm": {
|
|
37
|
+
"types": "./dist/adapters/typeorm/index.d.ts",
|
|
38
|
+
"import": "./dist/adapters/typeorm/index.js",
|
|
39
|
+
"require": "./dist/adapters/typeorm/index.js"
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"files": [
|
|
43
|
+
"dist",
|
|
44
|
+
"README.md",
|
|
45
|
+
"LICENSE",
|
|
46
|
+
"spec.md"
|
|
47
|
+
],
|
|
48
|
+
"scripts": {
|
|
49
|
+
"build": "tsc",
|
|
50
|
+
"build:dev": "tsc && rm -rf node_modules/typeorm",
|
|
51
|
+
"typecheck": "tsc --noEmit",
|
|
52
|
+
"test": "node --test dist/tests",
|
|
53
|
+
"prepublishOnly": "npm run build"
|
|
54
|
+
},
|
|
55
|
+
"peerDependencies": {
|
|
56
|
+
"typeorm": ">=0.3.0"
|
|
57
|
+
},
|
|
58
|
+
"peerDependenciesMeta": {
|
|
59
|
+
"typeorm": {
|
|
60
|
+
"optional": true
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
"devDependencies": {
|
|
64
|
+
"@types/node": "^20.0.0",
|
|
65
|
+
"reflect-metadata": "^0.2.2",
|
|
66
|
+
"sqlite3": "^5.1.7",
|
|
67
|
+
"typeorm": "^0.3.20",
|
|
68
|
+
"typescript": "^5.4.0"
|
|
69
|
+
}
|
|
70
|
+
}
|
package/spec.md
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
# Spec
|
|
2
|
+
|
|
3
|
+
This is somewhat of a "spec" that defines the query language.
|
|
4
|
+
|
|
5
|
+
It's not a proper spec, more like an example than a spec, but it's fairly comprehensive.
|
|
6
|
+
|
|
7
|
+
## OR
|
|
8
|
+
`|` means or in the definition, `"asc" | "desc"` means either "asc" or "desc".
|
|
9
|
+
So `string | {}` means either a string or an empty object.
|
|
10
|
+
|
|
11
|
+
## Default
|
|
12
|
+
`@` means that that is a default value.
|
|
13
|
+
This means that in `"asc" | @"desc"`, `"desc"` is default if the option is not specified.
|
|
14
|
+
|
|
15
|
+
## Non optional
|
|
16
|
+
`*` means that that is not an optional value, it's mandatory inside of the object it is defined into.
|
|
17
|
+
|
|
18
|
+
## Array types
|
|
19
|
+
When a type is specified like this: `[type]` it means that it's an array composed of many `type`.
|
|
20
|
+
|
|
21
|
+
## type definitions:
|
|
22
|
+
```json
|
|
23
|
+
offsetType: string | @"Z",
|
|
24
|
+
|
|
25
|
+
dateTimeType: string (ISO 8601) | {
|
|
26
|
+
year: int,
|
|
27
|
+
month: int,
|
|
28
|
+
day: int,
|
|
29
|
+
hours: int,
|
|
30
|
+
minutes: int,
|
|
31
|
+
seconds: int,
|
|
32
|
+
offset: offsetType
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
numericComparisonType: {
|
|
36
|
+
operation: ">" | "<" | ">=" | "<=" | @"=="
|
|
37
|
+
*value: number
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
// Presence check. Usable as the value of any primitive field
|
|
41
|
+
// (string, number, boolean, date, enum) in `searchBy`.
|
|
42
|
+
//
|
|
43
|
+
// { isNull: true } → IS NULL (NULLABLE fields only)
|
|
44
|
+
// { isNull: false } → IS NOT NULL (NULLABLE fields only)
|
|
45
|
+
// { isEmpty: true } → IS NULL OR = '' (STRING fields only)
|
|
46
|
+
// { isEmpty: false } → IS NOT NULL AND <> '' (STRING fields only)
|
|
47
|
+
//
|
|
48
|
+
// Both keys may appear in the same object; they are AND-ed. The useful
|
|
49
|
+
// combination is `{ isNull: false, isEmpty: true }` which matches rows
|
|
50
|
+
// that are not NULL but are the empty string. At least one of the two
|
|
51
|
+
// keys must be present.
|
|
52
|
+
//
|
|
53
|
+
// Constraints (rejected at parse time):
|
|
54
|
+
// - `isNull` on a field whose schema entry has `nullable: false`
|
|
55
|
+
// (the default) — non-nullable fields can never be NULL, so the
|
|
56
|
+
// check is always degenerate.
|
|
57
|
+
// - `isEmpty` on a non-string field.
|
|
58
|
+
presenceCheckType: {
|
|
59
|
+
isNull: bool,
|
|
60
|
+
isEmpty: bool,
|
|
61
|
+
},
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
In the dateTimeType object when it's not an iso string all fields are optional. If not specified offset will have "Z" as default
|
|
65
|
+
|
|
66
|
+
## String search modes
|
|
67
|
+
|
|
68
|
+
Strings may be searched using different search modes.
|
|
69
|
+
|
|
70
|
+
### Splitword (current default)
|
|
71
|
+
|
|
72
|
+
Any search string will be split by whitespace and searched against every piece of the now split query.
|
|
73
|
+
|
|
74
|
+
For example given this query to find a user in our table:
|
|
75
|
+
|
|
76
|
+
```json
|
|
77
|
+
{
|
|
78
|
+
searchBy: {
|
|
79
|
+
firstname: "mario rossi",
|
|
80
|
+
lastname: "mario rossi",
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
with splitword the result of the query would include anything that respect ALL the following conditions:
|
|
86
|
+
|
|
87
|
+
- firstname: is either "mario" or "rossi"
|
|
88
|
+
- lastname is either "mario" or "rossi"
|
|
89
|
+
|
|
90
|
+
so the following user would all be found by the query:
|
|
91
|
+
```json
|
|
92
|
+
[
|
|
93
|
+
{
|
|
94
|
+
firstname: "mario",
|
|
95
|
+
lastname: "mario"
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
firstname: "mario",
|
|
99
|
+
lastname: "rossi"
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
firstname: "rossi",
|
|
103
|
+
lastname: "rossi"
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
firstname: "rossi",
|
|
107
|
+
lastname: "mario"
|
|
108
|
+
}
|
|
109
|
+
]
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Of course here `firstname` and `lastname` are the same but this might not always be the case.
|
|
113
|
+
|
|
114
|
+
### exact
|
|
115
|
+
|
|
116
|
+
Just as it sounds, the string that is specified is the string that gets found, nothing more, nothing less.
|
|
117
|
+
|
|
118
|
+
### nativeregex
|
|
119
|
+
|
|
120
|
+
A string is searched against a specified regex. The regex is in the language natively supported by the ORM or the database, so no kind of processing is done on it.
|
|
121
|
+
|
|
122
|
+
# example:
|
|
123
|
+
```json
|
|
124
|
+
{
|
|
125
|
+
orderBy: string | { // if orderBy is a string then default order is "desc" just as described below
|
|
126
|
+
|
|
127
|
+
*field: string, // field: nameOfTheFieldToPerformSortingOperationsOn
|
|
128
|
+
order: "asc" | @"desc",
|
|
129
|
+
|
|
130
|
+
},
|
|
131
|
+
|
|
132
|
+
searchBy: {
|
|
133
|
+
|
|
134
|
+
// Any primitive field (string, number, boolean, date, enum) accepts
|
|
135
|
+
// `presenceCheckType` as an alternative value. `isEmpty` inside it is
|
|
136
|
+
// string-only — passing it on a non-string field is rejected.
|
|
137
|
+
|
|
138
|
+
// string
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
exampleStringField: string | presenceCheckType | { // when only a string is specified, default values apply here
|
|
142
|
+
mode: @"splitword" | "exact" | "nativeregex"
|
|
143
|
+
|
|
144
|
+
contained: true | @false, // This means that the query should match as part of a string, so "something" would also match "somethingelse"
|
|
145
|
+
|
|
146
|
+
caseSensitive: true | @false, // When false (default), comparison is case-insensitive. Applies to all modes including nativeregex.
|
|
147
|
+
|
|
148
|
+
*value: "something", // a value
|
|
149
|
+
},
|
|
150
|
+
|
|
151
|
+
firstname: "mario rossi",
|
|
152
|
+
lastname: "mario rossi", // remember splitword
|
|
153
|
+
|
|
154
|
+
fiscalCode: {
|
|
155
|
+
type: "exact"
|
|
156
|
+
value: "AFISCALCODE123"
|
|
157
|
+
},
|
|
158
|
+
|
|
159
|
+
// date
|
|
160
|
+
|
|
161
|
+
date: dateTimeType | presenceCheckType | { before: dateTimeType, after: dateTimeType }, // either before or after can be omitted from the object
|
|
162
|
+
|
|
163
|
+
// relations
|
|
164
|
+
|
|
165
|
+
// Implicit `some` — matches if at least one related row satisfies the filter.
|
|
166
|
+
relationName: {
|
|
167
|
+
otherField: "lorem ipsum"
|
|
168
|
+
},
|
|
169
|
+
|
|
170
|
+
// Explicit cardinality operators. `some` / `every` / `none` can appear
|
|
171
|
+
// alone or combined (multiple ops are AND-ed). Equivalent to Prisma's
|
|
172
|
+
// relation filters.
|
|
173
|
+
relationName: {
|
|
174
|
+
some: { otherField: "x" },
|
|
175
|
+
every: { published: true },
|
|
176
|
+
none: { draft: true }
|
|
177
|
+
},
|
|
178
|
+
|
|
179
|
+
// bool
|
|
180
|
+
|
|
181
|
+
exampleBoolField: true | presenceCheckType,
|
|
182
|
+
|
|
183
|
+
// number
|
|
184
|
+
|
|
185
|
+
exampleNumberField: number | presenceCheckType | {
|
|
186
|
+
operation: ">" | "<" | ">=" | "<=" | @"==",
|
|
187
|
+
*value: number
|
|
188
|
+
},
|
|
189
|
+
|
|
190
|
+
// enum
|
|
191
|
+
|
|
192
|
+
// An enum field must be a string value that matches one of the allowed
|
|
193
|
+
// values declared in the schema. The parser rejects anything else.
|
|
194
|
+
exampleEnumField: "allowedValue1" | "allowedValue2" | ... | presenceCheckType,
|
|
195
|
+
|
|
196
|
+
// OR
|
|
197
|
+
|
|
198
|
+
OR: [
|
|
199
|
+
searchBy, // recursive
|
|
200
|
+
searchBy, // here all the searchBy(s) are evaluated similar to prisma's OR
|
|
201
|
+
searchBy,
|
|
202
|
+
]
|
|
203
|
+
},
|
|
204
|
+
|
|
205
|
+
include: @"none" | "all" | { // which relations to include in the query result
|
|
206
|
+
relationField: {
|
|
207
|
+
firstname: true // only firstname will be included from relationField
|
|
208
|
+
},
|
|
209
|
+
otherRelation: "all", // all fields will be included from otherRelation
|
|
210
|
+
},
|
|
211
|
+
|
|
212
|
+
select: "none" | @"all" | { // which fields to include in the query result
|
|
213
|
+
someField: true, // only field 'someField' will be included in the query this way
|
|
214
|
+
},
|
|
215
|
+
|
|
216
|
+
pagination: @"all" | "first" | { // using first makes this a "findFirst" type query, equivalent to page: 0 and perPage: 1.
|
|
217
|
+
page: int | @0,
|
|
218
|
+
perPage: int | @20,
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
```
|