@simtlix/simfinity-core 3.9.0 → 3.11.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 -4
- package/package.json +1 -1
- package/src/auth/index.js +5 -3
- package/src/const/QLOperator.js +2 -0
- package/src/metadata.js +24 -8
- package/src/query-emptiness.js +297 -0
- package/src/query-limits.js +14 -2
- package/src/query-operators.js +21 -9
- package/src/query-plan.js +206 -35
- package/src/runtime.js +528 -77
- package/types/index.d.ts +119 -12
package/types/index.d.ts
CHANGED
|
@@ -21,10 +21,26 @@ export const QLValue: GraphQLScalarType<unknown, unknown>;
|
|
|
21
21
|
* and `IENDS_WITH` as a suffix, respecting or ignoring case, and `IEQ` as the whole value ignoring
|
|
22
22
|
* case. An empty value matches every non-null string, except for `IEQ`, where it matches only the
|
|
23
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. The target's `id` right after a reference is empty exactly when the reference is,
|
|
31
|
+
* including when the target is missing or hidden by its `find` scope. A path that continues inside an
|
|
32
|
+
* embedded list or a collection fails with `INVALID_FILTER_PATH`. With an adapter that declares
|
|
33
|
+
* `queryFeatures.referenceEmptiness: true`, a path may also cross single references and end at a
|
|
34
|
+
* reference or a collection, and an embedded object may have reference and collection members: a
|
|
35
|
+
* reference is empty when no record it points to exists that the target's `find` scope admits for
|
|
36
|
+
* the caller, a collection when no such child exists, and a hidden record on the way reads as
|
|
37
|
+
* missing; constructs that middleware and scopes add read stored data. With any other adapter they
|
|
38
|
+
* fail with `UNSUPPORTED_QUERY`. A term or group condition without a path tests the field itself.
|
|
24
39
|
*/
|
|
25
40
|
export type QueryOperator =
|
|
26
41
|
| 'EQ' | 'NE' | 'LT' | 'LTE' | 'GT' | 'GTE' | 'BTW' | 'IN' | 'NIN' | 'LIKE'
|
|
27
|
-
| 'ILIKE' | 'STARTS_WITH' | 'ISTARTS_WITH' | 'ENDS_WITH' | 'IENDS_WITH' | 'IEQ'
|
|
42
|
+
| 'ILIKE' | 'STARTS_WITH' | 'ISTARTS_WITH' | 'ENDS_WITH' | 'IENDS_WITH' | 'IEQ'
|
|
43
|
+
| 'IS_EMPTY' | 'IS_NOT_EMPTY';
|
|
28
44
|
|
|
29
45
|
/**
|
|
30
46
|
* The query language features a {@link DatabaseAdapter} compiles. Declare it so that the runtime
|
|
@@ -36,6 +52,15 @@ export interface QueryFeatures {
|
|
|
36
52
|
* version of Simfinity does not know are ignored.
|
|
37
53
|
*/
|
|
38
54
|
operators: readonly (QueryOperator | (string & {}))[];
|
|
55
|
+
/**
|
|
56
|
+
* Whether the adapter compiles `IS_EMPTY` and `IS_NOT_EMPTY` through single references and at
|
|
57
|
+
* references and collections, including reference and collection members of embedded objects.
|
|
58
|
+
* Undefined or null means false; any other value that is not a boolean fails `createRuntime` with
|
|
59
|
+
* `INVALID_QUERY_FEATURES` (500). An adapter that declares it receives client constructs that carry
|
|
60
|
+
* a filled client scope marker, `__scope: { id, scopes }`, on their term or group condition, and
|
|
61
|
+
* must apply each target scope in `scopes`; constructs without a marker read stored data.
|
|
62
|
+
*/
|
|
63
|
+
referenceEmptiness?: boolean | null | undefined;
|
|
39
64
|
}
|
|
40
65
|
|
|
41
66
|
export interface ModelRegistration {
|
|
@@ -279,8 +304,32 @@ export interface MutationLimitsOptions {
|
|
|
279
304
|
maxNestedOperations?: number | null;
|
|
280
305
|
}
|
|
281
306
|
|
|
307
|
+
/**
|
|
308
|
+
* The fourth `createSchema()` argument. Any other key, or a value that is not a plain object,
|
|
309
|
+
* throws `INVALID_SCHEMA_OPTIONS` (500) before models are prepared.
|
|
310
|
+
*/
|
|
311
|
+
export interface CreateSchemaOptions {
|
|
312
|
+
/**
|
|
313
|
+
* Custom queries to include, by name. `null` or omission includes every registered custom query,
|
|
314
|
+
* `[]` includes none, and names that are not registered are ignored.
|
|
315
|
+
*/
|
|
316
|
+
includedCustomQueries?: string[] | null;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
export interface QueryLimitsOptions {
|
|
320
|
+
/** Positive safe integer; defaults to 1000. Unpaged lists use min(100, maxPageSize). */
|
|
321
|
+
maxPageSize?: number;
|
|
322
|
+
/**
|
|
323
|
+
* Positive safe integer; defaults to 10. The most distinct scoped joins and frames that the
|
|
324
|
+
* client's reference and collection emptiness may need in one list, aggregate or collection-field
|
|
325
|
+
* call; more fail with `FILTER_DEPTH_EXCEEDED` (400).
|
|
326
|
+
*/
|
|
327
|
+
maxReferenceFrames?: number;
|
|
328
|
+
}
|
|
329
|
+
|
|
282
330
|
export interface Runtime<Model = any, Session = any> {
|
|
283
|
-
|
|
331
|
+
/** The same process-wide setting as the root `configureQueryLimits`. */
|
|
332
|
+
configureQueryLimits(options?: QueryLimitsOptions): void;
|
|
284
333
|
/**
|
|
285
334
|
* Process-wide; calling it without options restores the unlimited default. Invalid options,
|
|
286
335
|
* including unknown or misspelled option keys, throw `INVALID_MUTATION_LIMITS` (400) and keep
|
|
@@ -312,7 +361,7 @@ export interface Runtime<Model = any, Session = any> {
|
|
|
312
361
|
* not virtual, and is not a reference or collection, logs a `Configuration issue` warning once. The
|
|
313
362
|
* first schema binds the types whose relation resolvers it generates to this runtime and
|
|
314
363
|
* reserves reachable types that still have a
|
|
315
|
-
* non-embedded relation field without a resolver, such as an unregistered custom
|
|
364
|
+
* non-embedded relation field without a resolver, such as an unregistered custom operation result.
|
|
316
365
|
* Every entity type resolves `id` to `_id ?? id`, whatever the allowlists include.
|
|
317
366
|
*
|
|
318
367
|
* A generated input that would have no fields is left out, with a `Configuration issue`
|
|
@@ -326,12 +375,16 @@ export interface Runtime<Model = any, Session = any> {
|
|
|
326
375
|
* metadata types `SimfinityFieldExtensionsType` and `SimfinityRelationType` (numbered when
|
|
327
376
|
* taken) for every later schema and logs a warning; once those names are fixed, a type with one
|
|
328
377
|
* of them throws `RESERVED_TYPE_NAME` (500), before `adapter.prepare()` when it is an included
|
|
329
|
-
* endpoint type, a registered
|
|
378
|
+
* endpoint type, a registered custom operation's input or output type, an output type they reach or a
|
|
330
379
|
* field argument's type, and when the schema is constructed otherwise, such as for a type inside
|
|
331
380
|
* an input object's fields. The check before `adapter.prepare()` never reads an input object's
|
|
332
381
|
* fields, so a registered input's `fields` function may call `getInputType()`.
|
|
382
|
+
*
|
|
383
|
+
* `options.includedCustomQueries` selects custom queries by name (see {@link CreateSchemaOptions});
|
|
384
|
+
* an included custom query named like an included generated query throws `QUERY_NAME_CONFLICT`
|
|
385
|
+
* (500). A schema without mutation fields has no `Mutation` root.
|
|
333
386
|
*/
|
|
334
|
-
createSchema(includedQueryTypes?: GraphQLObjectType[] | null, includedMutationTypes?: GraphQLObjectType[] | null, includedCustomMutations?: string[] | null): GraphQLSchema;
|
|
387
|
+
createSchema(includedQueryTypes?: GraphQLObjectType[] | null, includedMutationTypes?: GraphQLObjectType[] | null, includedCustomMutations?: string[] | null, options?: CreateSchemaOptions): GraphQLSchema;
|
|
335
388
|
getModel(type: GraphQLObjectType | { name: string }): Model | null | undefined;
|
|
336
389
|
getType(name: string | { name: string }): GraphQLObjectType | null | undefined;
|
|
337
390
|
/**
|
|
@@ -350,6 +403,13 @@ export interface Runtime<Model = any, Session = any> {
|
|
|
350
403
|
*/
|
|
351
404
|
use(middleware: (params: MiddlewareContext, next: () => Promise<void>) => void | Promise<void>): void;
|
|
352
405
|
registerMutation(name: string, description: string, input: GraphQLInputObjectType | null | undefined, output: GraphQLOutputType, callback: (args: any, session: Session, context: any) => any): void;
|
|
406
|
+
/**
|
|
407
|
+
* Register a custom query on `RootQueryType`, for a result that is not a stored record. The
|
|
408
|
+
* callback receives the input and the request context; no transaction or session wraps it.
|
|
409
|
+
* Throws `INVALID_QUERY_REGISTRATION` (500) for invalid arguments and `QUERY_NAME_CONFLICT`
|
|
410
|
+
* (500) for a name already registered.
|
|
411
|
+
*/
|
|
412
|
+
registerCustomQuery(name: string, description: string, input: GraphQLInputObjectType | null | undefined, output: GraphQLOutputType, callback: (input: any, context: any) => any): void;
|
|
353
413
|
/** Owns the full workflow transaction unless an active caller session is supplied. */
|
|
354
414
|
saveObject(typeName: string, args: Record<string, any>, session?: Session, context?: any): Promise<any>;
|
|
355
415
|
preventCreatingCollection(prevent: boolean): void;
|
|
@@ -360,13 +420,16 @@ export interface DatabaseAdapter<Model = any, Session = any> {
|
|
|
360
420
|
/**
|
|
361
421
|
* Optional. The filter operators this adapter compiles. `createRuntime` reads it once and throws
|
|
362
422
|
* `INVALID_QUERY_FEATURES` (500) unless it is undefined, null or an object whose `operators` is an
|
|
363
|
-
* array of strings
|
|
364
|
-
* `LTE`, `GT`, `GTE`, `BTW`, `IN`, `NIN`
|
|
423
|
+
* array of strings and whose `referenceEmptiness`, if present, is a boolean or null. Without it the
|
|
424
|
+
* adapter supports the operators of 3.7.0 (`EQ`, `NE`, `LT`, `LTE`, `GT`, `GTE`, `BTW`, `IN`, `NIN`
|
|
425
|
+
* and `LIKE`) and no reference emptiness. A list, aggregate or collection read, or a
|
|
365
426
|
* by-ID read through a `get_by_id` scope, whose arguments use a `QLOperator` value the adapter
|
|
366
427
|
* does not support fails with `UNSUPPORTED_QUERY` (400), before middleware runs for the client's
|
|
367
428
|
* arguments and before `find`, `count`, `aggregate`, `findChildren` or `findChildrenBatch` for
|
|
368
429
|
* conditions that middleware, scopes and joined scopes added. Operators outside `QLOperator` still
|
|
369
|
-
* reach the adapter, which reports them as before.
|
|
430
|
+
* reach the adapter, which reports them as before. Declaring `IS_EMPTY` and `IS_NOT_EMPTY` means
|
|
431
|
+
* compiling their semantics on the record's own fields; emptiness through references and
|
|
432
|
+
* collections also needs `referenceEmptiness: true`, and fails with `UNSUPPORTED_QUERY` without it.
|
|
370
433
|
*/
|
|
371
434
|
queryFeatures?: QueryFeatures | null | undefined;
|
|
372
435
|
bind?(runtime: Pick<Runtime<Model, Session>, 'getModel' | 'getType' | 'getRegistrations'>): void;
|
|
@@ -486,8 +549,25 @@ export interface DatabaseAdapter<Model = any, Session = any> {
|
|
|
486
549
|
}
|
|
487
550
|
export function createRuntime<Model = any, Session = any>(adapter: DatabaseAdapter<Model, Session>): Runtime<Model, Session>;
|
|
488
551
|
|
|
489
|
-
/**
|
|
490
|
-
|
|
552
|
+
/**
|
|
553
|
+
* `operator` is a {@link QueryOperator}; the value of a text operator is a validated string. For
|
|
554
|
+
* `IS_EMPTY` and `IS_NOT_EMPTY` the value is null, and `path` may end at an embedded object, an
|
|
555
|
+
* embedded list or a scalar list as well as at a scalar; `IS_NOT_EMPTY` is the exact complement,
|
|
556
|
+
* which the compiler builds. With `referenceEmptiness: true`, their `path` may also cross single
|
|
557
|
+
* references and end at a reference or a collection.
|
|
558
|
+
*
|
|
559
|
+
* `scopes` exists only on such an emptiness predicate from a client, and only for a compiler that
|
|
560
|
+
* declares `referenceEmptiness: true`. It has one key per dotted path from the queried record: each
|
|
561
|
+
* single reference the path crosses (a scoped join, which admits the target only where the scope
|
|
562
|
+
* admits it), the end when it is a reference or a collection, and each reference and collection
|
|
563
|
+
* member of an embedded object (frames, which keep only the records the scope admits). Each value is
|
|
564
|
+
* the target's `find` scope as a predicate in the target's own frame, or null when the scope added
|
|
565
|
+
* nothing; after a scoped crossing, a null hop still correlates through that scoped join. Without
|
|
566
|
+
* `scopes` the predicate reads stored data, through plain joins.
|
|
567
|
+
*/
|
|
568
|
+
export type QueryPredicate =
|
|
569
|
+
| { kind: 'predicate'; path: string[]; operator: string; value: any; scopes?: Record<string, QueryPredicate | null> }
|
|
570
|
+
| { kind: 'and' | 'or'; terms: QueryPredicate[] };
|
|
491
571
|
export interface QueryPlan {
|
|
492
572
|
entity: string;
|
|
493
573
|
where: QueryPredicate | null;
|
|
@@ -502,11 +582,38 @@ export interface QueryPlan {
|
|
|
502
582
|
* a string or contains U+0000. With `operators`, the names the caller compiles, any other
|
|
503
583
|
* `QLOperator` value fails with `UNSUPPORTED_QUERY` (400); unknown operators fail with
|
|
504
584
|
* `INVALID_FILTER_OPERATOR` (400) either way.
|
|
585
|
+
*
|
|
586
|
+
* An emptiness condition is checked in this order: its form, path and group depth
|
|
587
|
+
* (`MISSING_FILTER_PATH` for `IS_EMPTY` or `IS_NOT_EMPTY` on an object filter without terms or beside
|
|
588
|
+
* terms, `AMBIGUOUS_FILTER_FIELD`, `INVALID_FILTER_PATH`, `INVALID_FILTER_FIELD`,
|
|
589
|
+
* `FILTER_DEPTH_EXCEEDED`), then `operators` (`UNSUPPORTED_QUERY`), then `INVALID_FILTER_PATH` for a
|
|
590
|
+
* value inside an embedded list or a collection, `INVALID_FILTER_OPERATOR` for an embedded type that
|
|
591
|
+
* reaches itself through single embedded members, `INVALID_FILTER_VALUE` for a value that is not
|
|
592
|
+
* omitted or null, and `UNSUPPORTED_QUERY` for a reference or collection on the path or among the
|
|
593
|
+
* members. A term without a path (omitted, null or `""`) and a group condition without a path test
|
|
594
|
+
* the field itself. For other operators, a filter path of more than one segment that ends at an
|
|
595
|
+
* object fails with `INVALID_FILTER_PATH`, as on MongoDB; a bare object field keeps
|
|
596
|
+
* `MISSING_FILTER_PATH`.
|
|
597
|
+
*
|
|
598
|
+
* `referenceEmptiness` defaults to true when `operators` is omitted and to false when it is given.
|
|
599
|
+
* When it is false, an emptiness condition that crosses or ends at a reference or a collection, or
|
|
600
|
+
* reads a reference or collection member, fails with `UNSUPPORTED_QUERY` where the order above has
|
|
601
|
+
* `UNSUPPORTED_QUERY` for references; when it is true, such a condition plans with stored semantics,
|
|
602
|
+
* unless its term or group condition carries a client scope marker that the runtime filled, which
|
|
603
|
+
* becomes the predicate's `scopes`. A marker that is not filled for every path of the condition fails
|
|
604
|
+
* with `DETACHED_CLIENT_FILTER` (500).
|
|
505
605
|
*/
|
|
506
|
-
export function createQueryPlan(models: ModelDescription, entityName: string, input?: Record<string, any>, options?: { mode?: QueryPlan['mode']; operators?: Iterable<string
|
|
606
|
+
export function createQueryPlan(models: ModelDescription, entityName: string, input?: Record<string, any>, options?: { mode?: QueryPlan['mode']; operators?: Iterable<string>; referenceEmptiness?: boolean }): QueryPlan;
|
|
507
607
|
export function resolveModelPath(models: ModelDescription, entityName: string, path: string | string[]): FieldDescription[];
|
|
508
608
|
|
|
509
|
-
|
|
609
|
+
/**
|
|
610
|
+
* Process-wide. `maxPageSize` (default 1000) caps page sizes. `maxReferenceFrames` (default 10) caps,
|
|
611
|
+
* per list, aggregate or collection-field call, the distinct scoped joins and frames that a client's
|
|
612
|
+
* reference and collection emptiness needs; more fail with `FILTER_DEPTH_EXCEEDED` (400). Each must be
|
|
613
|
+
* a positive safe integer, or the call throws `INVALID_QUERY_LIMITS` (400) and keeps both limits; an
|
|
614
|
+
* omitted option takes its default again.
|
|
615
|
+
*/
|
|
616
|
+
export function configureQueryLimits(options?: QueryLimitsOptions): void;
|
|
510
617
|
/**
|
|
511
618
|
* Same process-wide setting as `Runtime.configureMutationLimits`. Unknown or misspelled options
|
|
512
619
|
* throw `INVALID_MUTATION_LIMITS` (400) and keep the current limit.
|