@simtlix/simfinity-core 3.10.0 → 3.12.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 +4 -3
- package/package.json +1 -1
- package/src/query-emptiness.js +109 -34
- package/src/query-limits.js +14 -2
- package/src/query-operators.js +15 -10
- package/src/query-plan.js +103 -39
- package/src/runtime.js +553 -88
- package/types/index.d.ts +136 -14
package/types/index.d.ts
CHANGED
|
@@ -2,6 +2,8 @@ import type {
|
|
|
2
2
|
GraphQLEnumType,
|
|
3
3
|
GraphQLError,
|
|
4
4
|
GraphQLInputObjectType,
|
|
5
|
+
GraphQLList,
|
|
6
|
+
GraphQLNonNull,
|
|
5
7
|
GraphQLObjectType,
|
|
6
8
|
GraphQLOutputType,
|
|
7
9
|
GraphQLScalarType,
|
|
@@ -27,17 +29,41 @@ export const QLValue: GraphQLScalarType<unknown, unknown>;
|
|
|
27
29
|
* missing, null or `""` String (`" "` is not empty), a missing or null value of any other scalar or
|
|
28
30
|
* enum, a missing, null or `[]` list (`[null]` and `[""]` are not empty), and a missing or null
|
|
29
31
|
* embedded object or one whose stored members are all empty by these rules; the record identity is
|
|
30
|
-
* never empty.
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
32
|
+
* never empty. The target's `id` right after a reference is empty exactly when the reference is,
|
|
33
|
+
* including when the target is missing or hidden by its `find` scope. A path that continues inside an
|
|
34
|
+
* embedded list or a collection fails with `INVALID_FILTER_PATH`. With an adapter that declares
|
|
35
|
+
* `queryFeatures.referenceEmptiness: true`, a path may also cross single references and end at a
|
|
36
|
+
* reference or a collection, and an embedded object may have reference and collection members: a
|
|
37
|
+
* reference is empty when no record it points to exists that the target's `find` scope admits for
|
|
38
|
+
* the caller, a collection when no such child exists, and a hidden record on the way reads as
|
|
39
|
+
* missing; constructs that middleware and scopes add read stored data. With any other adapter they
|
|
40
|
+
* fail with `UNSUPPORTED_QUERY`. A term or group condition without a path tests the field itself.
|
|
35
41
|
*/
|
|
36
42
|
export type QueryOperator =
|
|
37
43
|
| 'EQ' | 'NE' | 'LT' | 'LTE' | 'GT' | 'GTE' | 'BTW' | 'IN' | 'NIN' | 'LIKE'
|
|
38
44
|
| 'ILIKE' | 'STARTS_WITH' | 'ISTARTS_WITH' | 'ENDS_WITH' | 'IENDS_WITH' | 'IEQ'
|
|
39
45
|
| 'IS_EMPTY' | 'IS_NOT_EMPTY';
|
|
40
46
|
|
|
47
|
+
/**
|
|
48
|
+
* A condition of a {@link FilterGroup}, as the `QLFilterCondition` input takes it: the field it
|
|
49
|
+
* tests, its {@link QueryOperator} (`EQ` when omitted) and its value. On an object field, `path`
|
|
50
|
+
* names the member it tests; `IS_EMPTY` and `IS_NOT_EMPTY` take no value and may omit `path` to test
|
|
51
|
+
* the field itself.
|
|
52
|
+
*/
|
|
53
|
+
export interface FilterCondition {
|
|
54
|
+
field: string;
|
|
55
|
+
operator?: QueryOperator | (string & {});
|
|
56
|
+
value?: any;
|
|
57
|
+
path?: string;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** A `QLFilterGroup` value: flat conditions plus nested `AND` and `OR` groups. */
|
|
61
|
+
export interface FilterGroup {
|
|
62
|
+
AND?: FilterGroup[];
|
|
63
|
+
OR?: FilterGroup[];
|
|
64
|
+
conditions?: FilterCondition[];
|
|
65
|
+
}
|
|
66
|
+
|
|
41
67
|
/**
|
|
42
68
|
* The query language features a {@link DatabaseAdapter} compiles. Declare it so that the runtime
|
|
43
69
|
* sends the adapter only conditions it compiles; later releases add members for later features.
|
|
@@ -48,6 +74,15 @@ export interface QueryFeatures {
|
|
|
48
74
|
* version of Simfinity does not know are ignored.
|
|
49
75
|
*/
|
|
50
76
|
operators: readonly (QueryOperator | (string & {}))[];
|
|
77
|
+
/**
|
|
78
|
+
* Whether the adapter compiles `IS_EMPTY` and `IS_NOT_EMPTY` through single references and at
|
|
79
|
+
* references and collections, including reference and collection members of embedded objects.
|
|
80
|
+
* Undefined or null means false; any other value that is not a boolean fails `createRuntime` with
|
|
81
|
+
* `INVALID_QUERY_FEATURES` (500). An adapter that declares it receives client constructs that carry
|
|
82
|
+
* a filled client scope marker, `__scope: { id, scopes }`, on their term or group condition, and
|
|
83
|
+
* must apply each target scope in `scopes`; constructs without a marker read stored data.
|
|
84
|
+
*/
|
|
85
|
+
referenceEmptiness?: boolean | null | undefined;
|
|
51
86
|
}
|
|
52
87
|
|
|
53
88
|
export interface ModelRegistration {
|
|
@@ -303,8 +338,56 @@ export interface CreateSchemaOptions {
|
|
|
303
338
|
includedCustomQueries?: string[] | null;
|
|
304
339
|
}
|
|
305
340
|
|
|
341
|
+
export interface QueryLimitsOptions {
|
|
342
|
+
/** Positive safe integer; defaults to 1000. Unpaged lists use min(100, maxPageSize). */
|
|
343
|
+
maxPageSize?: number;
|
|
344
|
+
/**
|
|
345
|
+
* Positive safe integer; defaults to 10. The most distinct scoped joins and frames that the
|
|
346
|
+
* client's reference and collection emptiness may need in one list, aggregate or collection-field
|
|
347
|
+
* call; more fail with `FILTER_DEPTH_EXCEEDED` (400).
|
|
348
|
+
*/
|
|
349
|
+
maxReferenceFrames?: number;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* The type of a view parameter: a value the client sends as a variable or a literal, so a scalar,
|
|
354
|
+
* an enum or a list of them, each possibly non-null, never an input object.
|
|
355
|
+
*/
|
|
356
|
+
export type ViewParamType =
|
|
357
|
+
| GraphQLScalarType
|
|
358
|
+
| GraphQLEnumType
|
|
359
|
+
| GraphQLList<ViewParamType>
|
|
360
|
+
| GraphQLNonNull<GraphQLScalarType | GraphQLEnumType | GraphQLList<ViewParamType>>;
|
|
361
|
+
|
|
362
|
+
/** A view parameter: an argument of the view's root field that only its `where` reads. */
|
|
363
|
+
export interface ViewParam {
|
|
364
|
+
type: ViewParamType;
|
|
365
|
+
description?: string;
|
|
366
|
+
defaultValue?: unknown;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/** The declaration of a named view (see {@link Runtime.registerView}). */
|
|
370
|
+
export interface ViewOptions {
|
|
371
|
+
/** The connected endpoint type whose list the view returns. */
|
|
372
|
+
type: GraphQLObjectType;
|
|
373
|
+
/** Non-empty; the root field's description. */
|
|
374
|
+
description: string;
|
|
375
|
+
/**
|
|
376
|
+
* Arguments of the root field besides the generated list arguments, named unlike them and unlike
|
|
377
|
+
* the fields of `type`. They never reach the adapter: only `where` receives them.
|
|
378
|
+
*/
|
|
379
|
+
params?: Record<string, ViewParam>;
|
|
380
|
+
/**
|
|
381
|
+
* Called on every read with the param values and the request context. The groups it returns are
|
|
382
|
+
* appended to the client's filters after middleware, so neither the client nor middleware can drop
|
|
383
|
+
* them; their paths may name fields that are not queryable.
|
|
384
|
+
*/
|
|
385
|
+
where: (input: { params: Record<string, any>; context: any }) => FilterGroup[] | Promise<FilterGroup[]>;
|
|
386
|
+
}
|
|
387
|
+
|
|
306
388
|
export interface Runtime<Model = any, Session = any> {
|
|
307
|
-
|
|
389
|
+
/** The same process-wide setting as the root `configureQueryLimits`. */
|
|
390
|
+
configureQueryLimits(options?: QueryLimitsOptions): void;
|
|
308
391
|
/**
|
|
309
392
|
* Process-wide; calling it without options restores the unlimited default. Invalid options,
|
|
310
393
|
* including unknown or misspelled option keys, throw `INVALID_MUTATION_LIMITS` (400) and keep
|
|
@@ -385,6 +468,17 @@ export interface Runtime<Model = any, Session = any> {
|
|
|
385
468
|
* (500) for a name already registered.
|
|
386
469
|
*/
|
|
387
470
|
registerCustomQuery(name: string, description: string, input: GraphQLInputObjectType | null | undefined, output: GraphQLOutputType, callback: (input: any, context: any) => any): void;
|
|
471
|
+
/**
|
|
472
|
+
* Register a named view: a root query field that returns the list of `options.type` with the
|
|
473
|
+
* generated list arguments plus `options.params`, and appends the groups of `options.where` after
|
|
474
|
+
* middleware. A schema includes the view exactly when it includes the type. Throws `INVALID_VIEW`
|
|
475
|
+
* (500) for an invalid declaration and `QUERY_NAME_CONFLICT` (500) for a name already registered as
|
|
476
|
+
* a view, keeping the first registration. `createSchema` throws `INVALID_VIEW` when the type is not
|
|
477
|
+
* connected in this runtime or a param is named like one of its fields or a generated list
|
|
478
|
+
* argument, and `QUERY_NAME_CONFLICT` when an included view is named like an included generated or
|
|
479
|
+
* custom query.
|
|
480
|
+
*/
|
|
481
|
+
registerView(name: string, options: ViewOptions): void;
|
|
388
482
|
/** Owns the full workflow transaction unless an active caller session is supplied. */
|
|
389
483
|
saveObject(typeName: string, args: Record<string, any>, session?: Session, context?: any): Promise<any>;
|
|
390
484
|
preventCreatingCollection(prevent: boolean): void;
|
|
@@ -395,15 +489,16 @@ export interface DatabaseAdapter<Model = any, Session = any> {
|
|
|
395
489
|
/**
|
|
396
490
|
* Optional. The filter operators this adapter compiles. `createRuntime` reads it once and throws
|
|
397
491
|
* `INVALID_QUERY_FEATURES` (500) unless it is undefined, null or an object whose `operators` is an
|
|
398
|
-
* array of strings
|
|
399
|
-
* `LTE`, `GT`, `GTE`, `BTW`, `IN`, `NIN`
|
|
492
|
+
* array of strings and whose `referenceEmptiness`, if present, is a boolean or null. Without it the
|
|
493
|
+
* adapter supports the operators of 3.7.0 (`EQ`, `NE`, `LT`, `LTE`, `GT`, `GTE`, `BTW`, `IN`, `NIN`
|
|
494
|
+
* and `LIKE`) and no reference emptiness. A list, aggregate or collection read, or a
|
|
400
495
|
* by-ID read through a `get_by_id` scope, whose arguments use a `QLOperator` value the adapter
|
|
401
496
|
* does not support fails with `UNSUPPORTED_QUERY` (400), before middleware runs for the client's
|
|
402
497
|
* arguments and before `find`, `count`, `aggregate`, `findChildren` or `findChildrenBatch` for
|
|
403
498
|
* conditions that middleware, scopes and joined scopes added. Operators outside `QLOperator` still
|
|
404
499
|
* 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;
|
|
406
|
-
*
|
|
500
|
+
* compiling their semantics on the record's own fields; emptiness through references and
|
|
501
|
+
* collections also needs `referenceEmptiness: true`, and fails with `UNSUPPORTED_QUERY` without it.
|
|
407
502
|
*/
|
|
408
503
|
queryFeatures?: QueryFeatures | null | undefined;
|
|
409
504
|
bind?(runtime: Pick<Runtime<Model, Session>, 'getModel' | 'getType' | 'getRegistrations'>): void;
|
|
@@ -527,9 +622,21 @@ export function createRuntime<Model = any, Session = any>(adapter: DatabaseAdapt
|
|
|
527
622
|
* `operator` is a {@link QueryOperator}; the value of a text operator is a validated string. For
|
|
528
623
|
* `IS_EMPTY` and `IS_NOT_EMPTY` the value is null, and `path` may end at an embedded object, an
|
|
529
624
|
* embedded list or a scalar list as well as at a scalar; `IS_NOT_EMPTY` is the exact complement,
|
|
530
|
-
* which the compiler builds.
|
|
625
|
+
* which the compiler builds. With `referenceEmptiness: true`, their `path` may also cross single
|
|
626
|
+
* references and end at a reference or a collection.
|
|
627
|
+
*
|
|
628
|
+
* `scopes` exists only on such an emptiness predicate from a client, and only for a compiler that
|
|
629
|
+
* declares `referenceEmptiness: true`. It has one key per dotted path from the queried record: each
|
|
630
|
+
* single reference the path crosses (a scoped join, which admits the target only where the scope
|
|
631
|
+
* admits it), the end when it is a reference or a collection, and each reference and collection
|
|
632
|
+
* member of an embedded object (frames, which keep only the records the scope admits). Each value is
|
|
633
|
+
* the target's `find` scope as a predicate in the target's own frame, or null when the scope added
|
|
634
|
+
* nothing; after a scoped crossing, a null hop still correlates through that scoped join. Without
|
|
635
|
+
* `scopes` the predicate reads stored data, through plain joins.
|
|
531
636
|
*/
|
|
532
|
-
export type QueryPredicate =
|
|
637
|
+
export type QueryPredicate =
|
|
638
|
+
| { kind: 'predicate'; path: string[]; operator: string; value: any; scopes?: Record<string, QueryPredicate | null> }
|
|
639
|
+
| { kind: 'and' | 'or'; terms: QueryPredicate[] };
|
|
533
640
|
export interface QueryPlan {
|
|
534
641
|
entity: string;
|
|
535
642
|
where: QueryPredicate | null;
|
|
@@ -556,11 +663,26 @@ export interface QueryPlan {
|
|
|
556
663
|
* the field itself. For other operators, a filter path of more than one segment that ends at an
|
|
557
664
|
* object fails with `INVALID_FILTER_PATH`, as on MongoDB; a bare object field keeps
|
|
558
665
|
* `MISSING_FILTER_PATH`.
|
|
666
|
+
*
|
|
667
|
+
* `referenceEmptiness` defaults to true when `operators` is omitted and to false when it is given.
|
|
668
|
+
* When it is false, an emptiness condition that crosses or ends at a reference or a collection, or
|
|
669
|
+
* reads a reference or collection member, fails with `UNSUPPORTED_QUERY` where the order above has
|
|
670
|
+
* `UNSUPPORTED_QUERY` for references; when it is true, such a condition plans with stored semantics,
|
|
671
|
+
* unless its term or group condition carries a client scope marker that the runtime filled, which
|
|
672
|
+
* becomes the predicate's `scopes`. A marker that is not filled for every path of the condition fails
|
|
673
|
+
* with `DETACHED_CLIENT_FILTER` (500).
|
|
559
674
|
*/
|
|
560
|
-
export function createQueryPlan(models: ModelDescription, entityName: string, input?: Record<string, any>, options?: { mode?: QueryPlan['mode']; operators?: Iterable<string
|
|
675
|
+
export function createQueryPlan(models: ModelDescription, entityName: string, input?: Record<string, any>, options?: { mode?: QueryPlan['mode']; operators?: Iterable<string>; referenceEmptiness?: boolean }): QueryPlan;
|
|
561
676
|
export function resolveModelPath(models: ModelDescription, entityName: string, path: string | string[]): FieldDescription[];
|
|
562
677
|
|
|
563
|
-
|
|
678
|
+
/**
|
|
679
|
+
* Process-wide. `maxPageSize` (default 1000) caps page sizes. `maxReferenceFrames` (default 10) caps,
|
|
680
|
+
* per list, aggregate or collection-field call, the distinct scoped joins and frames that a client's
|
|
681
|
+
* reference and collection emptiness needs; more fail with `FILTER_DEPTH_EXCEEDED` (400). Each must be
|
|
682
|
+
* a positive safe integer, or the call throws `INVALID_QUERY_LIMITS` (400) and keeps both limits; an
|
|
683
|
+
* omitted option takes its default again.
|
|
684
|
+
*/
|
|
685
|
+
export function configureQueryLimits(options?: QueryLimitsOptions): void;
|
|
564
686
|
/**
|
|
565
687
|
* Same process-wide setting as `Runtime.configureMutationLimits`. Unknown or misspelled options
|
|
566
688
|
* throw `INVALID_MUTATION_LIMITS` (400) and keep the current limit.
|