@simtlix/simfinity-core 3.13.0 → 3.14.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
@@ -12,6 +12,8 @@ import type {
12
12
 
13
13
  /** The filter operator enum; its values are the {@link QueryOperator} names. */
14
14
  export const QLOperator: GraphQLEnumType;
15
+ /** The collection and reference quantifier enum. */
16
+ export const QLQuantifier: GraphQLEnumType;
15
17
  export const QLSort: GraphQLInputObjectType;
16
18
  export const QLValue: GraphQLScalarType<unknown, unknown>;
17
19
 
@@ -45,19 +47,57 @@ export const MAX_RECORD_VERSION: number;
45
47
  * embedded object or one whose stored members are all empty by these rules; the record identity is
46
48
  * never empty. The target's `id` right after a reference is empty exactly when the reference is,
47
49
  * including when the target is missing or hidden by its `find` scope. A path that continues inside an
48
- * embedded list or a collection fails with `INVALID_FILTER_PATH`. With an adapter that declares
50
+ * embedded list fails with `INVALID_FILTER_PATH`; shorthand through a collection excludes emptiness
51
+ * on a leaf, so use an explicit group. With an adapter that declares
49
52
  * `queryFeatures.referenceEmptiness: true`, a path may also cross single references and end at a
50
53
  * reference or a collection, and an embedded object may have reference and collection members: a
51
54
  * reference is empty when no record it points to exists that the target's `find` scope admits for
52
55
  * the caller, a collection when no such child exists, and a hidden record on the way reads as
53
56
  * missing; constructs that middleware and scopes add read stored data. With any other adapter they
54
- * fail with `UNSUPPORTED_QUERY`. A term or group condition without a path tests the field itself.
57
+ * fail with `UNSUPPORTED_QUERY`. A term or group condition without a path tests the field itself, except that every term
58
+ * of a quantified relation filter needs a non-empty element-relative path.
55
59
  */
56
60
  export type QueryOperator =
57
61
  | 'EQ' | 'NE' | 'LT' | 'LTE' | 'GT' | 'GTE' | 'BTW' | 'IN' | 'NIN' | 'LIKE'
58
62
  | 'ILIKE' | 'STARTS_WITH' | 'ISTARTS_WITH' | 'ENDS_WITH' | 'IENDS_WITH' | 'IEQ'
59
63
  | 'IS_EMPTY' | 'IS_NOT_EMPTY';
60
64
 
65
+ /** The quantifier applied to the elements visible in a collection or reference frame. */
66
+ export type QueryQuantifier = 'ANY' | 'ALL' | 'NONE';
67
+
68
+ /** A condition inside a filter group, with paths relative to the current frame. */
69
+ export interface QLFilterCondition {
70
+ field: string;
71
+ operator?: QueryOperator | null;
72
+ value?: any;
73
+ path?: string | null;
74
+ }
75
+
76
+ /** Recursive Boolean groups and quantified frames. */
77
+ export interface QLFilterGroup {
78
+ conditions?: Array<QLFilterCondition | null> | null;
79
+ AND?: Array<QLFilterGroup | null> | null;
80
+ OR?: Array<QLFilterGroup | null> | null;
81
+ ANY?: Array<QLQuantifiedGroup | null> | null;
82
+ ALL?: Array<QLQuantifiedGroup | null> | null;
83
+ NONE?: Array<QLQuantifiedGroup | null> | null;
84
+ }
85
+
86
+ /** A frame path ends at a collection or single reference without crossing a list; inner paths are element-relative. */
87
+ export interface QLQuantifiedGroup extends QLFilterGroup {
88
+ path: string;
89
+ }
90
+
91
+ /** A quantified field filter may omit terms; every supplied term needs a non-empty element-relative path. */
92
+ export interface QLTypeFilterExpression {
93
+ quantifier?: QueryQuantifier | null;
94
+ terms?: Array<{
95
+ operator?: QueryOperator | null;
96
+ value?: any;
97
+ path?: string | null;
98
+ } | null> | null;
99
+ }
100
+
61
101
  /**
62
102
  * A condition of a {@link FilterGroup}, as the `QLFilterCondition` input takes it: the field it
63
103
  * tests, its {@link QueryOperator} (`EQ` when omitted) and its value. On an object field, `path`
@@ -71,8 +111,11 @@ export interface FilterCondition {
71
111
  path?: string;
72
112
  }
73
113
 
74
- /** A `QLFilterGroup` value: flat conditions plus nested `AND` and `OR` groups. */
114
+ /** A `QLFilterGroup` value: flat conditions, Boolean groups and element-relative quantified groups. */
75
115
  export interface FilterGroup {
116
+ ANY?: QLQuantifiedGroup[];
117
+ ALL?: QLQuantifiedGroup[];
118
+ NONE?: QLQuantifiedGroup[];
76
119
  AND?: FilterGroup[];
77
120
  OR?: FilterGroup[];
78
121
  conditions?: FilterCondition[];
@@ -97,6 +140,15 @@ export interface QueryFeatures {
97
140
  * must apply each target scope in `scopes`; constructs without a marker read stored data.
98
141
  */
99
142
  referenceEmptiness?: boolean | null | undefined;
143
+ /**
144
+ * Whether the adapter compiles quantified frames and their scoped joins. Requires
145
+ * `referenceEmptiness: true`; undefined or null means false. Invalid declarations fail with
146
+ * `INVALID_QUERY_FEATURES` (500). Client markers have frame-relative scope keys; compilers must
147
+ * apply frame scopes before quantification and honour scoped joins/aggregate anchors. Without
148
+ * this flag explicit quantified forms fail with `UNSUPPORTED_QUERY`; collection filters keep
149
+ * legacy joins. All adapters reject list sorts through collections, including count mode.
150
+ */
151
+ quantifiers?: boolean | null | undefined;
100
152
  }
101
153
 
102
154
  export interface ModelRegistration {
@@ -383,11 +435,17 @@ export interface QueryLimitsOptions {
383
435
  /** Positive safe integer; defaults to 1000. Unpaged lists use min(100, maxPageSize). */
384
436
  maxPageSize?: number;
385
437
  /**
386
- * Positive safe integer; defaults to 10. The most distinct scoped joins and frames that the
387
- * client's reference and collection emptiness may need in one list, aggregate or collection-field
388
- * call; more fail with `FILTER_DEPTH_EXCEEDED` (400).
438
+ * Positive safe integer; defaults to 10. The most compiled frames and scoped joins that the
439
+ * client's conditions may need in one list, aggregate or collection-field call; more fail with
440
+ * `FILTER_DEPTH_EXCEEDED` (400), after every client 400 check and before middleware. Units count
441
+ * independently of markers: shorthand per group/prefix, explicit frames separately, scoped joins
442
+ * per containing frame/path/scope chain and emptiness frames per path/shape. Trusted units do
443
+ * not count toward this total. Named views check client arguments and their `where` groups
444
+ * against separate budgets of the same size; the `where` budget runs after middleware.
389
445
  */
390
446
  maxReferenceFrames?: number;
447
+ /** Positive safe integer; defaults to 3. Maximum client/trusted collection or reference frames on one chain. */
448
+ maxFrameNesting?: number;
391
449
  }
392
450
 
393
451
  /**
@@ -697,6 +755,9 @@ export interface DatabaseAdapter<Model = any, Session = any> {
697
755
  }
698
756
  export function createRuntime<Model = any, Session = any>(adapter: DatabaseAdapter<Model, Session>): Runtime<Model, Session>;
699
757
 
758
+ /** Scoped single-reference joins, keyed by a dotted prefix relative to the containing frame. */
759
+ export type QueryJoins = Record<string, QueryPredicate | null>;
760
+
700
761
  /**
701
762
  * `operator` is a {@link QueryOperator}; the value of a text operator is a validated string. For
702
763
  * `IS_EMPTY` and `IS_NOT_EMPTY` the value is null, and `path` may end at an embedded object, an
@@ -705,17 +766,31 @@ export function createRuntime<Model = any, Session = any>(adapter: DatabaseAdapt
705
766
  * references and end at a reference or a collection.
706
767
  *
707
768
  * `scopes` exists only on such an emptiness predicate from a client, and only for a compiler that
708
- * declares `referenceEmptiness: true`. It has one key per dotted path from the queried record: each
769
+ * declares `referenceEmptiness: true`. It has one key per dotted path relative to its frame: each
709
770
  * single reference the path crosses (a scoped join, which admits the target only where the scope
710
771
  * admits it), the end when it is a reference or a collection, and each reference and collection
711
772
  * member of an embedded object (frames, which keep only the records the scope admits). Each value is
712
773
  * the target's `find` scope as a predicate in the target's own frame, or null when the scope added
713
774
  * nothing; after a scoped crossing, a null hop still correlates through that scoped join. Without
714
- * `scopes` the predicate reads stored data, through plain joins.
775
+ * `scopes` the predicate reads stored data, through plain joins. `joins` carries the scopes of
776
+ * single references crossed inside a client frame, with keys relative to the containing frame.
777
+ * A `quantified` node selects elements with `ANY`, `ALL` or `NONE`, applying `scope` before `where`;
778
+ * both predicates use paths relative to the element, and null means no restriction. Empty elements
779
+ * give ANY false, ALL/NONE true; scope is never negated, and frames never multiply roots. `anchor` is
780
+ * an aggregate's legacy join chain; when present, `path` is relative to it and `joins` is absent.
715
781
  */
716
782
  export type QueryPredicate =
717
- | { kind: 'predicate'; path: string[]; operator: string; value: any; scopes?: Record<string, QueryPredicate | null> }
718
- | { kind: 'and' | 'or'; terms: QueryPredicate[] };
783
+ | { kind: 'predicate'; path: string[]; operator: string; value: any; scopes?: Record<string, QueryPredicate | null>; joins?: QueryJoins }
784
+ | { kind: 'and' | 'or'; terms: QueryPredicate[] }
785
+ | {
786
+ kind: 'quantified';
787
+ quantifier: QueryQuantifier;
788
+ path: string[];
789
+ where: QueryPredicate | null;
790
+ scope: QueryPredicate | null;
791
+ joins?: QueryJoins;
792
+ anchor?: string[];
793
+ };
719
794
  export interface QueryPlan {
720
795
  entity: string;
721
796
  where: QueryPredicate | null;
@@ -738,8 +813,8 @@ export interface QueryPlan {
738
813
  * value inside an embedded list or a collection, `INVALID_FILTER_OPERATOR` for an embedded type that
739
814
  * reaches itself through single embedded members, `INVALID_FILTER_VALUE` for a value that is not
740
815
  * omitted or null, and `UNSUPPORTED_QUERY` for a reference or collection on the path or among the
741
- * members. A term without a path (omitted, null or `""`) and a group condition without a path test
742
- * the field itself. For other operators, a filter path of more than one segment that ends at an
816
+ * members. A nonquantified emptiness term without a path (omitted, null or `""`) and a group
817
+ * condition without a path test the field itself; quantified filter terms always need a path. For other operators, a filter path of more than one segment that ends at an
743
818
  * object fails with `INVALID_FILTER_PATH`, as on MongoDB; a bare object field keeps
744
819
  * `MISSING_FILTER_PATH`.
745
820
  *
@@ -748,18 +823,22 @@ export interface QueryPlan {
748
823
  * reads a reference or collection member, fails with `UNSUPPORTED_QUERY` where the order above has
749
824
  * `UNSUPPORTED_QUERY` for references; when it is true, such a condition plans with stored semantics,
750
825
  * unless its term or group condition carries a client scope marker that the runtime filled, which
751
- * becomes the predicate's `scopes`. A marker that is not filled for every path of the condition fails
826
+ * becomes the predicate's `scopes`. `quantifiers` has the same default rule and requires reference
827
+ * emptiness; enabled collection filters emit quantified nodes, with per-group/prefix correlation,
828
+ * element-relative scopes/joins and aggregate anchors beyond legacy J joins. `__scope`, `__implicit`
829
+ * and `__anchor` are reserved internal input metadata. A marker not filled for every path fails
752
830
  * with `DETACHED_CLIENT_FILTER` (500).
753
831
  */
754
- export function createQueryPlan(models: ModelDescription, entityName: string, input?: Record<string, any>, options?: { mode?: QueryPlan['mode']; operators?: Iterable<string>; referenceEmptiness?: boolean }): QueryPlan;
832
+ export function createQueryPlan(models: ModelDescription, entityName: string, input?: Record<string, any>, options?: { mode?: QueryPlan['mode']; operators?: Iterable<string>; referenceEmptiness?: boolean; quantifiers?: boolean }): QueryPlan;
755
833
  export function resolveModelPath(models: ModelDescription, entityName: string, path: string | string[]): FieldDescription[];
756
834
 
757
835
  /**
758
836
  * Process-wide. `maxPageSize` (default 1000) caps page sizes. `maxReferenceFrames` (default 10) caps,
759
- * per list, aggregate or collection-field call, the distinct scoped joins and frames that a client's
760
- * reference and collection emptiness needs; more fail with `FILTER_DEPTH_EXCEEDED` (400). Each must be
761
- * a positive safe integer, or the call throws `INVALID_QUERY_LIMITS` (400) and keeps both limits; an
762
- * omitted option takes its default again.
837
+ * per list, aggregate or collection-field call, the compiled frames and scoped joins a client's
838
+ * conditions need. Named views check client arguments and `where` groups separately. `maxFrameNesting` (default 3) caps nested frames along one chain in client and
839
+ * trusted constructs. Exceeding either frame limit fails with `FILTER_DEPTH_EXCEEDED` (400). Each
840
+ * option must be a positive safe integer, or the call throws `INVALID_QUERY_LIMITS` (400) and keeps
841
+ * every current limit; an omitted option takes its default again.
763
842
  */
764
843
  export function configureQueryLimits(options?: QueryLimitsOptions): void;
765
844
  /**