@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/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 mutation result.
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 mutation's input or output type, an output type they reach or a
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
- export function createQueryPlan(models: ModelDescription, entityName: string, input?: Record<string, any>, options?: { mode?: QueryPlan['mode'] }): QueryPlan;
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;