@simtlix/simfinity-core 3.8.0 → 3.10.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 +5 -2
- package/package.json +2 -1
- package/src/auth/index.js +5 -3
- package/src/const/QLOperator.js +8 -0
- package/src/metadata.js +24 -8
- package/src/query-emptiness.js +222 -0
- package/src/query-operators.js +87 -0
- package/src/query-plan.js +165 -15
- package/src/runtime.js +285 -42
- package/types/index.d.ts +104 -4
package/types/index.d.ts
CHANGED
|
@@ -8,10 +8,48 @@ import type {
|
|
|
8
8
|
GraphQLSchema,
|
|
9
9
|
} from 'graphql';
|
|
10
10
|
|
|
11
|
+
/** The filter operator enum; its values are the {@link QueryOperator} names. */
|
|
11
12
|
export const QLOperator: GraphQLEnumType;
|
|
12
13
|
export const QLSort: GraphQLInputObjectType;
|
|
13
14
|
export const QLValue: GraphQLScalarType<unknown, unknown>;
|
|
14
15
|
|
|
16
|
+
/**
|
|
17
|
+
* A `QLOperator` value. The operators of 3.7.0 are `EQ`, `NE`, `LT`, `LTE`, `GT`, `GTE`, `BTW`, `IN`,
|
|
18
|
+
* `NIN` and `LIKE`. The literal text operators take a non-null string without U+0000 on a `String`
|
|
19
|
+
* field or `String` list (a validated scalar filters by its base scalar), and match it literally:
|
|
20
|
+
* `ILIKE` as a substring ignoring case, `STARTS_WITH` and `ISTARTS_WITH` as a prefix, `ENDS_WITH`
|
|
21
|
+
* and `IENDS_WITH` as a suffix, respecting or ignoring case, and `IEQ` as the whole value ignoring
|
|
22
|
+
* case. An empty value matches every non-null string, except for `IEQ`, where it matches only the
|
|
23
|
+
* empty string. On a list, some element must match.
|
|
24
|
+
*
|
|
25
|
+
* `IS_EMPTY` and `IS_NOT_EMPTY` take no value (omitted or null; any other value fails with
|
|
26
|
+
* `INVALID_FILTER_VALUE`), and every record matches exactly one of them. `IS_EMPTY` holds for a
|
|
27
|
+
* missing, null or `""` String (`" "` is not empty), a missing or null value of any other scalar or
|
|
28
|
+
* enum, a missing, null or `[]` list (`[null]` and `[""]` are not empty), and a missing or null
|
|
29
|
+
* embedded object or one whose stored members are all empty by these rules; the record identity is
|
|
30
|
+
* never empty. They apply to the queried record's own fields: a path that continues inside an
|
|
31
|
+
* embedded list or a collection fails with `INVALID_FILTER_PATH`, and a path that crosses or ends
|
|
32
|
+
* at a reference, a path that ends at a collection, and an embedded object with a reference or
|
|
33
|
+
* collection member fail with `UNSUPPORTED_QUERY` until reference emptiness arrives. A term or
|
|
34
|
+
* group condition without a path tests the field itself.
|
|
35
|
+
*/
|
|
36
|
+
export type QueryOperator =
|
|
37
|
+
| 'EQ' | 'NE' | 'LT' | 'LTE' | 'GT' | 'GTE' | 'BTW' | 'IN' | 'NIN' | 'LIKE'
|
|
38
|
+
| 'ILIKE' | 'STARTS_WITH' | 'ISTARTS_WITH' | 'ENDS_WITH' | 'IENDS_WITH' | 'IEQ'
|
|
39
|
+
| 'IS_EMPTY' | 'IS_NOT_EMPTY';
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* The query language features a {@link DatabaseAdapter} compiles. Declare it so that the runtime
|
|
43
|
+
* sends the adapter only conditions it compiles; later releases add members for later features.
|
|
44
|
+
*/
|
|
45
|
+
export interface QueryFeatures {
|
|
46
|
+
/**
|
|
47
|
+
* The `QLOperator` values the adapter compiles, including an omitted operator's `EQ`. Names this
|
|
48
|
+
* version of Simfinity does not know are ignored.
|
|
49
|
+
*/
|
|
50
|
+
operators: readonly (QueryOperator | (string & {}))[];
|
|
51
|
+
}
|
|
52
|
+
|
|
15
53
|
export interface ModelRegistration {
|
|
16
54
|
gqltype: GraphQLObjectType;
|
|
17
55
|
/** Defaults to true. False excludes embedded-only types until referenced. */
|
|
@@ -253,6 +291,18 @@ export interface MutationLimitsOptions {
|
|
|
253
291
|
maxNestedOperations?: number | null;
|
|
254
292
|
}
|
|
255
293
|
|
|
294
|
+
/**
|
|
295
|
+
* The fourth `createSchema()` argument. Any other key, or a value that is not a plain object,
|
|
296
|
+
* throws `INVALID_SCHEMA_OPTIONS` (500) before models are prepared.
|
|
297
|
+
*/
|
|
298
|
+
export interface CreateSchemaOptions {
|
|
299
|
+
/**
|
|
300
|
+
* Custom queries to include, by name. `null` or omission includes every registered custom query,
|
|
301
|
+
* `[]` includes none, and names that are not registered are ignored.
|
|
302
|
+
*/
|
|
303
|
+
includedCustomQueries?: string[] | null;
|
|
304
|
+
}
|
|
305
|
+
|
|
256
306
|
export interface Runtime<Model = any, Session = any> {
|
|
257
307
|
configureQueryLimits(options?: { maxPageSize?: number }): void;
|
|
258
308
|
/**
|
|
@@ -286,7 +336,7 @@ export interface Runtime<Model = any, Session = any> {
|
|
|
286
336
|
* not virtual, and is not a reference or collection, logs a `Configuration issue` warning once. The
|
|
287
337
|
* first schema binds the types whose relation resolvers it generates to this runtime and
|
|
288
338
|
* reserves reachable types that still have a
|
|
289
|
-
* non-embedded relation field without a resolver, such as an unregistered custom
|
|
339
|
+
* non-embedded relation field without a resolver, such as an unregistered custom operation result.
|
|
290
340
|
* Every entity type resolves `id` to `_id ?? id`, whatever the allowlists include.
|
|
291
341
|
*
|
|
292
342
|
* A generated input that would have no fields is left out, with a `Configuration issue`
|
|
@@ -300,12 +350,16 @@ export interface Runtime<Model = any, Session = any> {
|
|
|
300
350
|
* metadata types `SimfinityFieldExtensionsType` and `SimfinityRelationType` (numbered when
|
|
301
351
|
* taken) for every later schema and logs a warning; once those names are fixed, a type with one
|
|
302
352
|
* of them throws `RESERVED_TYPE_NAME` (500), before `adapter.prepare()` when it is an included
|
|
303
|
-
* endpoint type, a registered
|
|
353
|
+
* endpoint type, a registered custom operation's input or output type, an output type they reach or a
|
|
304
354
|
* field argument's type, and when the schema is constructed otherwise, such as for a type inside
|
|
305
355
|
* an input object's fields. The check before `adapter.prepare()` never reads an input object's
|
|
306
356
|
* fields, so a registered input's `fields` function may call `getInputType()`.
|
|
357
|
+
*
|
|
358
|
+
* `options.includedCustomQueries` selects custom queries by name (see {@link CreateSchemaOptions});
|
|
359
|
+
* an included custom query named like an included generated query throws `QUERY_NAME_CONFLICT`
|
|
360
|
+
* (500). A schema without mutation fields has no `Mutation` root.
|
|
307
361
|
*/
|
|
308
|
-
createSchema(includedQueryTypes?: GraphQLObjectType[] | null, includedMutationTypes?: GraphQLObjectType[] | null, includedCustomMutations?: string[] | null): GraphQLSchema;
|
|
362
|
+
createSchema(includedQueryTypes?: GraphQLObjectType[] | null, includedMutationTypes?: GraphQLObjectType[] | null, includedCustomMutations?: string[] | null, options?: CreateSchemaOptions): GraphQLSchema;
|
|
309
363
|
getModel(type: GraphQLObjectType | { name: string }): Model | null | undefined;
|
|
310
364
|
getType(name: string | { name: string }): GraphQLObjectType | null | undefined;
|
|
311
365
|
/**
|
|
@@ -324,6 +378,13 @@ export interface Runtime<Model = any, Session = any> {
|
|
|
324
378
|
*/
|
|
325
379
|
use(middleware: (params: MiddlewareContext, next: () => Promise<void>) => void | Promise<void>): void;
|
|
326
380
|
registerMutation(name: string, description: string, input: GraphQLInputObjectType | null | undefined, output: GraphQLOutputType, callback: (args: any, session: Session, context: any) => any): void;
|
|
381
|
+
/**
|
|
382
|
+
* Register a custom query on `RootQueryType`, for a result that is not a stored record. The
|
|
383
|
+
* callback receives the input and the request context; no transaction or session wraps it.
|
|
384
|
+
* Throws `INVALID_QUERY_REGISTRATION` (500) for invalid arguments and `QUERY_NAME_CONFLICT`
|
|
385
|
+
* (500) for a name already registered.
|
|
386
|
+
*/
|
|
387
|
+
registerCustomQuery(name: string, description: string, input: GraphQLInputObjectType | null | undefined, output: GraphQLOutputType, callback: (input: any, context: any) => any): void;
|
|
327
388
|
/** Owns the full workflow transaction unless an active caller session is supplied. */
|
|
328
389
|
saveObject(typeName: string, args: Record<string, any>, session?: Session, context?: any): Promise<any>;
|
|
329
390
|
preventCreatingCollection(prevent: boolean): void;
|
|
@@ -331,6 +392,20 @@ export interface Runtime<Model = any, Session = any> {
|
|
|
331
392
|
|
|
332
393
|
/** Driver adapter contract. GraphQL generation and lifecycle live in createRuntime. */
|
|
333
394
|
export interface DatabaseAdapter<Model = any, Session = any> {
|
|
395
|
+
/**
|
|
396
|
+
* Optional. The filter operators this adapter compiles. `createRuntime` reads it once and throws
|
|
397
|
+
* `INVALID_QUERY_FEATURES` (500) unless it is undefined, null or an object whose `operators` is an
|
|
398
|
+
* array of strings. Without it the adapter supports the operators of 3.7.0 (`EQ`, `NE`, `LT`,
|
|
399
|
+
* `LTE`, `GT`, `GTE`, `BTW`, `IN`, `NIN` and `LIKE`). A list, aggregate or collection read, or a
|
|
400
|
+
* by-ID read through a `get_by_id` scope, whose arguments use a `QLOperator` value the adapter
|
|
401
|
+
* does not support fails with `UNSUPPORTED_QUERY` (400), before middleware runs for the client's
|
|
402
|
+
* arguments and before `find`, `count`, `aggregate`, `findChildren` or `findChildrenBatch` for
|
|
403
|
+
* conditions that middleware, scopes and joined scopes added. Operators outside `QLOperator` still
|
|
404
|
+
* reach the adapter, which reports them as before. Declaring `IS_EMPTY` and `IS_NOT_EMPTY` means
|
|
405
|
+
* compiling their semantics on the record's own fields; it does not lift the `UNSUPPORTED_QUERY`
|
|
406
|
+
* that emptiness through references and collections gets in this release.
|
|
407
|
+
*/
|
|
408
|
+
queryFeatures?: QueryFeatures | null | undefined;
|
|
334
409
|
bind?(runtime: Pick<Runtime<Model, Session>, 'getModel' | 'getType' | 'getRegistrations'>): void;
|
|
335
410
|
validateRegistration?(registration: RuntimeRegistration<Model, Session>): void;
|
|
336
411
|
/**
|
|
@@ -448,6 +523,12 @@ export interface DatabaseAdapter<Model = any, Session = any> {
|
|
|
448
523
|
}
|
|
449
524
|
export function createRuntime<Model = any, Session = any>(adapter: DatabaseAdapter<Model, Session>): Runtime<Model, Session>;
|
|
450
525
|
|
|
526
|
+
/**
|
|
527
|
+
* `operator` is a {@link QueryOperator}; the value of a text operator is a validated string. For
|
|
528
|
+
* `IS_EMPTY` and `IS_NOT_EMPTY` the value is null, and `path` may end at an embedded object, an
|
|
529
|
+
* embedded list or a scalar list as well as at a scalar; `IS_NOT_EMPTY` is the exact complement,
|
|
530
|
+
* which the compiler builds.
|
|
531
|
+
*/
|
|
451
532
|
export type QueryPredicate = { kind: 'predicate'; path: string[]; operator: string; value: any } | { kind: 'and' | 'or'; terms: QueryPredicate[] };
|
|
452
533
|
export interface QueryPlan {
|
|
453
534
|
entity: string;
|
|
@@ -457,7 +538,26 @@ export interface QueryPlan {
|
|
|
457
538
|
pagination: { limit: number; offset: number } | null;
|
|
458
539
|
aggregation?: { groupId: string[]; facts: Array<{ path: string[]; factName: string; operation: 'SUM' | 'COUNT' | 'AVG' | 'MIN' | 'MAX' }> };
|
|
459
540
|
}
|
|
460
|
-
|
|
541
|
+
/**
|
|
542
|
+
* Validates the arguments into a plan: paths, operators and values, with `INVALID_FILTER_VALUE` (400)
|
|
543
|
+
* for a text operator on a field that is not a `String` field or list, or with a value that is not
|
|
544
|
+
* a string or contains U+0000. With `operators`, the names the caller compiles, any other
|
|
545
|
+
* `QLOperator` value fails with `UNSUPPORTED_QUERY` (400); unknown operators fail with
|
|
546
|
+
* `INVALID_FILTER_OPERATOR` (400) either way.
|
|
547
|
+
*
|
|
548
|
+
* An emptiness condition is checked in this order: its form, path and group depth
|
|
549
|
+
* (`MISSING_FILTER_PATH` for `IS_EMPTY` or `IS_NOT_EMPTY` on an object filter without terms or beside
|
|
550
|
+
* terms, `AMBIGUOUS_FILTER_FIELD`, `INVALID_FILTER_PATH`, `INVALID_FILTER_FIELD`,
|
|
551
|
+
* `FILTER_DEPTH_EXCEEDED`), then `operators` (`UNSUPPORTED_QUERY`), then `INVALID_FILTER_PATH` for a
|
|
552
|
+
* value inside an embedded list or a collection, `INVALID_FILTER_OPERATOR` for an embedded type that
|
|
553
|
+
* reaches itself through single embedded members, `INVALID_FILTER_VALUE` for a value that is not
|
|
554
|
+
* omitted or null, and `UNSUPPORTED_QUERY` for a reference or collection on the path or among the
|
|
555
|
+
* members. A term without a path (omitted, null or `""`) and a group condition without a path test
|
|
556
|
+
* the field itself. For other operators, a filter path of more than one segment that ends at an
|
|
557
|
+
* object fails with `INVALID_FILTER_PATH`, as on MongoDB; a bare object field keeps
|
|
558
|
+
* `MISSING_FILTER_PATH`.
|
|
559
|
+
*/
|
|
560
|
+
export function createQueryPlan(models: ModelDescription, entityName: string, input?: Record<string, any>, options?: { mode?: QueryPlan['mode']; operators?: Iterable<string> }): QueryPlan;
|
|
461
561
|
export function resolveModelPath(models: ModelDescription, entityName: string, path: string | string[]): FieldDescription[];
|
|
462
562
|
|
|
463
563
|
export function configureQueryLimits(options?: { maxPageSize?: number }): void;
|