@query-graph/core 1.1.6 → 1.1.7

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/definition.d.ts CHANGED
@@ -30,7 +30,7 @@ import type {
30
30
  } from './dsl.js'
31
31
 
32
32
  /** @deprecated Import authoring primitives from `@query-graph/core/dsl`. */
33
- export const GRAPH_DEFINITION_VERSION: 11
33
+ export const GRAPH_DEFINITION_VERSION: 12
34
34
 
35
35
  /** @deprecated Use `source.field(name)` from `@query-graph/core/dsl`. */
36
36
  export function field<
package/definition.js CHANGED
@@ -76,6 +76,11 @@ exports.requiredListParameter = objectDsl.requiredListParameter
76
76
  exports.requiredListParam = objectDsl.requiredListParam
77
77
  exports.optionalListParameter = objectDsl.optionalListParameter
78
78
  exports.optionalListParam = objectDsl.optionalListParam
79
+ exports.requiredGroupsParameter = objectDsl.requiredGroupsParameter
80
+ exports.requiredGroupsParam = objectDsl.requiredGroupsParam
81
+ exports.optionalGroupsParameter = objectDsl.optionalGroupsParameter
82
+ exports.optionalGroupsParam = objectDsl.optionalGroupsParam
83
+ exports.allGroups = objectDsl.allGroups
79
84
  exports.eq = objectDsl.eq
80
85
  exports.neq = objectDsl.neq
81
86
  exports.lt = objectDsl.lt
package/docs/authoring.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # Authoring DSL
2
2
 
3
3
  Authoring API находится в <code>@query-graph/core/dsl</code>. Он создает
4
- семантический definition версии 11, который не содержит таблиц, SQL aliases или
4
+ семантический definition версии 12, который не содержит таблиц, SQL aliases или
5
5
  синтаксиса диалекта.
6
6
 
7
7
  ## Sources и поля
@@ -36,6 +36,76 @@ Scalar parameter передается в expression напрямую, list param
36
36
  тех же factories. Optional parameter не активирует constraint с
37
37
  <code>when</code>, пока значение отсутствует.
38
38
 
39
+ ### Группированные параметры
40
+
41
+ Grouped list parameter описывает массив групп <code>{ key, values }</code>.
42
+ Каждая группа содержит один ключ и список значений:
43
+
44
+ ```ts
45
+ import { allGroups, and, constraint, eq, inParam, optionalGroupsParam, source } from '@query-graph/core/dsl'
46
+
47
+ const attributes = source('attributes', { name: 'string', valueId: 'int64' })
48
+ const filters = optionalGroupsParam('filters', { key: 'string', value: 'int64' })
49
+ const attributeFilter = constraint({
50
+ predicate: allGroups(filters, ({ key, values }) =>
51
+ and(eq(attributes.field('name'), key), inParam(attributes.field('valueId'), values)),
52
+ ),
53
+ })
54
+
55
+ // Operation parameters:
56
+ const parameters = {
57
+ filters: [
58
+ { key: 'color', values: [1, 2] },
59
+ { key: 'size', values: [3] },
60
+ ],
61
+ }
62
+ ```
63
+
64
+ Добавьте <code>filters</code> в <code>parameters</code> definition, а
65
+ <code>attributeFilter</code> в <code>constraints</code>. Если разные группы
66
+ должны совпасть с разными связанными строками, поместите <code>exists</code>
67
+ внутрь callback и объявите соответствующую relation.
68
+
69
+ <code>requiredGroupsParam</code> требует присутствия массива в operation.
70
+ Полные имена <code>optionalGroupsParameter</code> и
71
+ <code>requiredGroupsParameter</code> являются алиасами тех же factories.
72
+ Неизвестные поля конфигурации, scalar types и некорректные аргументы factories
73
+ отклоняются сразу при authoring.
74
+
75
+ Callback <code>allGroups</code> вызывается ровно один раз при построении AST,
76
+ а не для каждой runtime-группы. <code>key</code> является типизированным scalar
77
+ expression, <code>values</code> является отдельной scoped list reference только
78
+ для <code>inParam</code>/<code>inParameter</code>. Ни grouped parameter, ни
79
+ <code>values</code> нельзя передать в <code>param</code> или использовать как
80
+ обычное scalar expression. <code>values</code> также нельзя объявить root parameter.
81
+
82
+ Каждый binder получает UUID scope через <code>node:crypto.randomUUID()</code>,
83
+ независимо от загрузки отдельных экземпляров или копий DSL. Вложенный callback
84
+ может захватить <code>key</code> и <code>values</code> внешнего callback;
85
+ одноименные параметры не смешивают их scopes. Ссылки за пределами своего binder,
86
+ в том числе захваченные из соседнего callback, отклоняются Rust при регистрации.
87
+ Scope является внутренним идентификатором, его конкретное значение не гарантируется.
88
+
89
+ Семантика компиляции:
90
+
91
+ - отсутствующий optional parameter и пустой массив групп дают <code>true</code>;
92
+ - неактивное тело не требует optional scalar parameters или JOIN, используемых
93
+ только внутри него;
94
+ - predicates всех групп объединяются через <code>AND</code>;
95
+ - повторяющиеся ключи остаются независимыми группами, их values не объединяются;
96
+ - пустой <code>values</code> дает <code>false</code> в <code>inParam</code>;
97
+ - nullable predicate сохраняет обычную трехзначную SQL-семантику.
98
+
99
+ Rust разрешает <code>allGroups</code> только в constraint predicates, включая
100
+ вложенные boolean expressions и <code>exists</code>. Projection, ordering,
101
+ relation predicates и aggregate bodies не поддерживают binder.
102
+
103
+ Wire definition сохраняет тип элементов в <code>scalarType</code> и добавляет
104
+ <code>shape: 'groupedList'</code> и <code>keyType</code>. AST использует
105
+ <code>{ kind: 'allGroups', parameter, scope, predicate }</code>,
106
+ <code>{ kind: 'groupKey', scope }</code> и
107
+ <code>{ kind: 'inGroupValues', expression, scope }</code>.
108
+
39
109
  ## Expressions
40
110
 
41
111
  DSL предоставляет сравнения, boolean groups, null tests, semantic functions,
@@ -45,8 +45,32 @@ Rust выполняет semantic validation:
45
45
  Такое разделение дает ранние ошибки опечаток, не дублируя planner rules в
46
46
  JavaScript.
47
47
 
48
+ Циклические зависимости relation predicates через <code>exists</code> отклоняются
49
+ при регистрации definition: <code>invalidExistsContext</code> указывает на
50
+ <code>relations[i].on</code>, замыкающий цикл. Параметры, необходимые вложенным
51
+ <code>exists</code>, проверяются в фазе <code>operation</code>; отсутствующий
52
+ параметр получает <code>missingParameter</code> с location
53
+ <code>parameters.&lt;name&gt;</code>.
54
+
55
+ Статические параметры batch relation проверяются при <code>composeGraph</code>:
56
+ ошибки относятся к фазе <code>composition</code>, в том числе для имен параметров
57
+ с квадратными скобками. Параметр batch key исключается из этой проверки по точному
58
+ имени; значения ключей проверяются позже, при <code>compileBatch</code>, в фазе
59
+ <code>operation</code>.
60
+
48
61
  ## Совместимость diagnostics
49
62
 
50
- Wire schema остается версии 11. В совместимых выпусках сохраняются diagnostic
63
+ Wire schema версии 12 добавляет grouped-параметры и scoped-шаблоны `allGroups`.
64
+ Сохранённые raw definitions предыдущей версии требуют обновления; обычные
65
+ DSL-фабрики формируют актуальную версию автоматически. JS DSL и native binding
66
+ должны быть из одного выпуска пакета.
67
+
68
+ Невалидные данные группы получают `invalidParameterType` с точным путём,
69
+ например `parameters.characteristics[0].values[1]`. Ссылка вне своего шаблона
70
+ получает `unknownGroupScope`; повторное объявление активного scope получает
71
+ `shadowedGroupScope`. `allGroups` вне constraint predicate получает
72
+ `invalidAllGroupsContext`. Эти проверки выполняются и для raw wire definitions.
73
+
74
+ Для прежних ошибок сохраняются diagnostic
51
75
  codes и locations. Message предназначен человеку и не должен использоваться как
52
76
  machine-readable contract.
package/docs/sql.md CHANGED
@@ -45,6 +45,29 @@ Semantic functions рендерятся с одинаковым поведени
45
45
  SQL Server compiler принудительно переводит <code>divide</code> в дробную
46
46
  арифметику, чтобы два integer operands не дали integer division.
47
47
 
48
+ В SQL Server 2012 и новее <code>concat</code> с одним аргументом компилируется
49
+ с дополнительной пустой строкой <code>''</code>. Списки длиннее 254 аргументов
50
+ разделяются на вложенные вызовы с сохранением порядка. Промежуточные группы
51
+ используют <code>nvarchar(max)</code>, чтобы избежать обрезания строк при
52
+ объединении. <code>NULL</code> обрабатывается как пустая строка согласно
53
+ [семантике CONCAT в SQL Server](https://learn.microsoft.com/en-us/sql/t-sql/functions/concat-transact-sql).
54
+
55
+ ## Boolean expressions
56
+
57
+ Компилятор различает значения и условия. Boolean-поле в <code>WHERE</code>,
58
+ <code>ON</code> или логическом выражении превращается в сравнение с <code>1</code>.
59
+ Условие в <code>SELECT</code>, <code>GROUP BY</code> или аргументе функции
60
+ превращается в <code>CASE</code>: <code>TRUE</code> дает <code>1</code>,
61
+ <code>FALSE</code> — <code>0</code>, <code>UNKNOWN</code> — <code>NULL</code>.
62
+ Boolean metadata и TypeScript-типы сохраняются; преобразование значений драйвера
63
+ остается ответственностью executor.
64
+
65
+ В Oracle 23ai условия сохраняют нативный SQL-тип <code>BOOLEAN</code> в контексте
66
+ значения. В контексте условия скалярные значения оборачиваются в
67
+ <code>TO_BOOLEAN</code>, который принимает как числовое, так и boolean-представление.
68
+ Прямые проекции полей и существующие сравнения с boolean literals сохраняют
69
+ прежний SQL.
70
+
48
71
  ## Bindings
49
72
 
50
73
  Compiler никогда не вставляет operation values в SQL. Каждый binding содержит:
@@ -52,11 +75,64 @@ Compiler никогда не вставляет operation values в SQL. Каж
52
75
  - SQL placeholder name;
53
76
  - имя parameter;
54
77
  - scalar type;
55
- - index элемента для list parameter.
78
+ - index элемента для list parameter;
79
+ - path до вложенного значения для grouped parameter.
56
80
 
57
81
  Повторное использование parameter переиспользует binding. Пустой list
58
82
  компилируется в ложное условие без placeholders.
59
83
 
84
+ Для scalar отсутствуют и `index`, и `path`. Для обычного list сохраняется
85
+ `index`. Для grouped list используется только `path`, например:
86
+
87
+ ```ts
88
+ ;[
89
+ { name: 'p0', parameter: 'characteristics', scalarType: 'int64', path: [0, 'key'] },
90
+ { name: 'p1', parameter: 'characteristics', scalarType: 'int64', path: [0, 'values', 1] },
91
+ ]
92
+ ```
93
+
94
+ Executor извлекает значение из `operation.parameters[binding.parameter]`,
95
+ последовательно проходя по сегментам `path`. `index` и `path` взаимно исключаются;
96
+ отсутствующий сегмент должен приводить к ошибке, а не к подстановке `null`.
97
+ Тип binding соответствует конкретному ключу или элементу values, а не контейнеру.
98
+ Повторное обращение к тому же параметру и пути переиспользует placeholder.
99
+ Потребитель должен обновить resolver bindings до начала использования grouped-параметров.
100
+
101
+ `allGroups` разворачивает шаблон в условия, соединённые `AND`, только при
102
+ компиляции операции. Для отсутствующего optional-параметра и пустого списка
103
+ групп результат равен `TRUE`; зависимости тела шаблона не включаются в план.
104
+ Каждый `exists` остаётся коррелированным подзапросом по описанному пути relations,
105
+ включая `firstBy`. Definition не изменяется, но SQL зависит от количества групп
106
+ и значений. Это один SQL-запрос, а не отдельное выполнение на каждую группу.
107
+
108
+ На один statement допускается до 10 000 разворачиваний тела `allGroups`
109
+ суммарно по всем шаблонам, включая вложенные. Превышение возвращает
110
+ `groupExpansionLimitExceeded`: вложенность может увеличить SQL многократно,
111
+ даже когда все bindings переиспользуются. Лимит применяется отдельно к каждой
112
+ компиляции и не ограничивает каталог доступных групп в приложении.
113
+
114
+ Также ограничено суммарное число элементов параметров-массивов, развёрнутых
115
+ внутри этих шаблонов: до 100 000 на statement. Повторное использование
116
+ одинаковых placeholders считается повторно, поскольку увеличивает SQL.
117
+ Ограничение охватывает и `values` группы, и обычный list parameter внутри
118
+ `allGroups`; превышение даёт `groupValueExpansionLimitExceeded`.
119
+
120
+ SQL Server compiler отклоняет statement с более чем 2100 уникальными bindings
121
+ с диагностикой `tooManyParameters`. Считаются все scalar, list и grouped bindings
122
+ вместе; разбиение `IN` не уменьшает их количество. Для больших выборок нужна
123
+ другая стратегия передачи данных, которой текущий grouped DSL не подменяет
124
+ явную ошибку. Ограничение согласовано с
125
+ [бюджетом параметров SQL Server](https://learn.microsoft.com/sql/sql-server/maximum-capacity-specifications-for-sql-server).
126
+
127
+ В Oracle большие <code>inParam</code> (включая values группы) и <code>inList</code> разбиваются на
128
+ группы <code>IN</code>, объединённые <code>OR</code> в общих скобках внутри
129
+ одного SQL. Размер группы — до 1000 выражений в 11g–21c и до 65535 в 23ai
130
+ согласно [лимитам Oracle](https://python-oracledb.readthedocs.io/en/v2.5.1/user_guide/bind.html#binding-a-large-number-of-items-in-an-in-list).
131
+ <code>not</code> применяется ко всей группе и сохраняет SQL-семантику
132
+ <code>NULL</code>. Порядок элементов и индексы bindings сохраняются, включая
133
+ дубликаты. Пустой литеральный <code>inList</code> по-прежнему отклоняется при
134
+ валидации definition.
135
+
60
136
  ## Result metadata
61
137
 
62
138
  SQL columns получают технические aliases <code>c0</code>,
@@ -16,6 +16,10 @@ TypeScript помогает автору до регистрации, Rust ос
16
16
  <code>int64</code> принимает number только в безопасном диапазоне JavaScript.
17
17
  Decimal string проверяется как конечная десятичная запись.
18
18
 
19
+ <code>integer(value)</code> поддерживает весь диапазон от
20
+ <code>Number.MIN_SAFE_INTEGER</code> до <code>Number.MAX_SAFE_INTEGER</code>,
21
+ включая значения за пределами 32 бит.
22
+
19
23
  ## QueryOperation
20
24
 
21
25
  Definition передает свои параметры, projection paths и ordering names в
@@ -34,6 +38,49 @@ const operation = {
34
38
  Required parameters обязательны, optional параметры могут отсутствовать,
35
39
  list parameters принимают readonly arrays.
36
40
 
41
+ Grouped list parameters выводятся как:
42
+
43
+ ```ts
44
+ readonly {
45
+ key: ScalarParameterValue<KeyType>
46
+ values: readonly ScalarParameterValue<ValueType>[]
47
+ }[]
48
+ ```
49
+
50
+ Например, <code>requiredGroupsParam('filters', { key: 'string', value: 'int64' })</code>
51
+ требует <code>parameters.filters</code> типа
52
+ <code>readonly { key: string; values: readonly (number | string)[] }[]</code>.
53
+ Optional grouped parameter может отсутствовать, но не заменяться на <code>null</code>.
54
+ Оба массива принимают readonly tuples, в том числе из <code>as const</code>.
55
+
56
+ Контейнер групп, отдельная группа и контейнер <code>values</code> не могут быть
57
+ <code>null</code>. Ключ и элементы списка следуют обычному scalar contract:
58
+ <code>null</code> запрещен для всех типов, кроме <code>json</code>. JSON сохраняет
59
+ существующую семантику JSON values, включая <code>null</code> и вложенные структуры.
60
+ Rust проверяет наличие <code>key</code>/<code>values</code>, неизвестные поля
61
+ группы и scalar types, включая ограничения числовых значений.
62
+
63
+ Grouped parameters сохраняют эти типы при композиции GraphModule и могут быть
64
+ статическими параметрами дочернего <code>batchRelation</code>. Ключ
65
+ <code>batchQuery</code> по-прежнему требует обычный list parameter, не grouped list.
66
+
67
+ В callback <code>allGroups</code> ключ имеет scalar type <code>KeyType</code>,
68
+ а <code>GroupValuesRef&lt;ValueType&gt;</code> принимается только как второй аргумент
69
+ <code>inParameter</code>/<code>inParam</code>. TypeScript исключает эту reference
70
+ из scalar expressions и root parameters; Rust проверяет lexical scope и
71
+ совместимость типов сравнения и membership.
72
+
73
+ Для grouped bindings <code>path</code> указывает на значение внутри operation
74
+ parameter: <code>[groupIndex, 'key']</code> или
75
+ <code>[groupIndex, 'values', valueIndex]</code>. Scalar bindings остаются без
76
+ <code>index</code>/<code>path</code>, обычные list bindings сохраняют
77
+ <code>index</code>. Поля <code>index</code> и <code>path</code> взаимоисключающие.
78
+ Это же правило действует для результатов <code>compileBatch</code>.
79
+
80
+ В Node API <code>offset</code> и <code>limit</code> принимают безопасные целые
81
+ JavaScript, включая значения больше <code>2 \*\* 32 - 1</code>.
82
+ <code>offset</code> должен быть неотрицательным, <code>limit</code> — положительным.
83
+
37
84
  ## Nullability
38
85
 
39
86
  Nullability выражения складывается из:
@@ -46,6 +93,20 @@ Nullability выражения складывается из:
46
93
  Required relation под optional ancestor остается nullable. Это свойство
47
94
  вычисляется из topology один раз при регистрации.
48
95
 
96
+ В <code>fieldType</code> флаг <code>nullable</code> типа <code>boolean</code>
97
+ означает, что поле может быть NULL. Литеральные <code>true</code> и
98
+ <code>false</code> позволяют вывести точную nullability.
99
+
100
+ Сравнения <code>eq</code>/<code>neq</code> с гарантированным <code>null</code>
101
+ (включая <code>literal(null)</code>) дают non-null результат. Тип операнда
102
+ <code>string | null</code> сам по себе не гарантирует сравнение именно с NULL:
103
+ nullable поле при сравнении со строкой может дать NULL.
104
+
105
+ <code>allGroups</code> возвращает boolean с nullability predicate шаблона.
106
+ Пустой набор групп дает <code>true</code>, но при наличии групп nullable
107
+ сравнение внутри callback может дать SQL UNKNOWN. Если callback возвращает
108
+ <code>exists</code>, результат остается non-null boolean.
109
+
49
110
  ## ResultOf
50
111
 
51
112
  ```ts
@@ -77,7 +138,8 @@ compilation не меняются.
77
138
  Rust проверяет:
78
139
 
79
140
  - ссылки на sources, fields и parameters;
80
- - scalar/list shape параметров;
141
+ - scalar/list/groupedList shape параметров и key type grouped parameters;
142
+ - lexical scopes ключей и списков групп, допустимость binder в constraint context;
81
143
  - совместимость сравнений и функций;
82
144
  - boolean тип predicates;
83
145
  - orderable и groupable expressions;
package/dsl.d.ts CHANGED
@@ -27,6 +27,9 @@ export type {
27
27
  LikeExpression,
28
28
  InExpression,
29
29
  InParameterExpression,
30
+ GroupKeyExpression,
31
+ InGroupValuesExpression,
32
+ AllGroupsExpression,
30
33
  ExpressionGroup,
31
34
  UnaryExpression,
32
35
  ExistsExpression,
@@ -94,6 +97,10 @@ export type {
94
97
  SourceRef,
95
98
  ScalarParameterRef,
96
99
  ListParameterRef,
100
+ GroupedListParameterRef,
101
+ GroupedListParameterTypes,
102
+ GroupValuesRef,
103
+ GroupScope,
97
104
  ParameterRef,
98
105
  RelationRef,
99
106
  LiteralInput,
@@ -127,6 +134,11 @@ export {
127
134
  requiredListParam,
128
135
  optionalListParameter,
129
136
  optionalListParam,
137
+ requiredGroupsParameter,
138
+ requiredGroupsParam,
139
+ optionalGroupsParameter,
140
+ optionalGroupsParam,
141
+ allGroups,
130
142
  param,
131
143
  literal,
132
144
  integer,
package/dsl.js CHANGED
@@ -20,6 +20,11 @@ exports.requiredListParameter = primitives.requiredListParameter
20
20
  exports.requiredListParam = primitives.requiredListParam
21
21
  exports.optionalListParameter = primitives.optionalListParameter
22
22
  exports.optionalListParam = primitives.optionalListParam
23
+ exports.requiredGroupsParameter = primitives.requiredGroupsParameter
24
+ exports.requiredGroupsParam = primitives.requiredGroupsParam
25
+ exports.optionalGroupsParameter = primitives.optionalGroupsParameter
26
+ exports.optionalGroupsParam = primitives.optionalGroupsParam
27
+ exports.allGroups = primitives.allGroups
23
28
  exports.eq = primitives.eq
24
29
  exports.neq = primitives.neq
25
30
  exports.lt = primitives.lt
@@ -11,7 +11,7 @@ const {
11
11
  const { asExpression, copySource, referenceName } = require('./primitives.js')
12
12
  const { GRAPH_MODULE_BRAND, SUMMARY_FIELD_BRAND } = require('./symbols.js')
13
13
 
14
- const GRAPH_DEFINITION_VERSION = 11
14
+ const GRAPH_DEFINITION_VERSION = 12
15
15
  const RELATION_CONFIGURATION_KEYS = new Set(['name', 'from', 'to', 'on', 'required', 'cardinality', 'selection'])
16
16
  const CONSTRAINT_CONFIGURATION_KEYS = new Set(['predicate', 'when'])
17
17
  const PROJECTION_CONFIGURATION_KEYS = new Set(['path', 'expression', 'default'])
@@ -1,7 +1,8 @@
1
1
  'use strict'
2
2
 
3
+ const { randomUUID } = require('node:crypto')
3
4
  const { arrayOf, configurationOf, nonEmptyString, objectOf, optionalBoolean } = require('../configuration.js')
4
- const { PARAMETER_REF_BRAND, SUMMARY_FIELD_BRAND } = require('./symbols.js')
5
+ const { GROUP_VALUES_REF_BRAND, PARAMETER_REF_BRAND, SUMMARY_FIELD_BRAND } = require('./symbols.js')
5
6
 
6
7
  const SCALAR_TYPES = new Set([
7
8
  'boolean',
@@ -18,6 +19,7 @@ const SCALAR_TYPES = new Set([
18
19
  const FIELD_TYPE_OPTIONS_KEYS = new Set(['nullable', 'selectable'])
19
20
  const FIELD_SPECIFICATION_KEYS = new Set(['scalarType', 'nullable', 'selectable'])
20
21
  const EXISTS_CONFIGURATION_KEYS = new Set(['from'])
22
+ const GROUPED_LIST_CONFIGURATION_KEYS = new Set(['key', 'value'])
21
23
 
22
24
  function fieldType(scalarType, options = {}) {
23
25
  assertScalarType(scalarType)
@@ -100,10 +102,32 @@ function optionalListParameter(name, scalarType) {
100
102
  return parameterDefinition(name, scalarType, false, 'list')
101
103
  }
102
104
 
105
+ function requiredGroupsParameter(name, types) {
106
+ return groupedListParameterDefinition(name, types, true)
107
+ }
108
+
109
+ function optionalGroupsParameter(name, types) {
110
+ return groupedListParameterDefinition(name, types, false)
111
+ }
112
+
113
+ function groupedListParameterDefinition(name, types, required) {
114
+ const configuration = configurationOf('grouped list parameter', types, GROUPED_LIST_CONFIGURATION_KEYS)
115
+ assertScalarType(configuration.key)
116
+ const definition = parameterDefinition(name, configuration.value, required, 'groupedList')
117
+ definition.keyType = configuration.key
118
+ return definition
119
+ }
120
+
103
121
  function param(parameterReference) {
104
122
  if (typeof parameterReference !== 'string' && parameterReference?.shape === 'list') {
105
123
  throw new TypeError(`List parameter ${parameterReference.name} cannot be used as a scalar expression`)
106
124
  }
125
+ if (parameterReference?.shape === 'groupedList') {
126
+ throw new TypeError(`Grouped list parameter ${parameterReference.name} cannot be used as a scalar expression`)
127
+ }
128
+ if (parameterReference?.[GROUP_VALUES_REF_BRAND] === true) {
129
+ throw new TypeError('Group values cannot be used as a scalar expression')
130
+ }
107
131
 
108
132
  return {
109
133
  kind: 'parameter',
@@ -184,6 +208,13 @@ function inList(expression, values) {
184
208
  }
185
209
 
186
210
  function inParameter(expression, parameterReference) {
211
+ if (parameterReference?.[GROUP_VALUES_REF_BRAND] === true) {
212
+ return {
213
+ kind: 'inGroupValues',
214
+ expression: asExpression(expression),
215
+ scope: parameterReference.scope,
216
+ }
217
+ }
187
218
  if (typeof parameterReference !== 'string' && parameterReference?.shape !== 'list') {
188
219
  throw new TypeError(`Parameter ${parameterReference?.name ?? '<unknown>'} is not a list parameter`)
189
220
  }
@@ -195,6 +226,31 @@ function inParameter(expression, parameterReference) {
195
226
  }
196
227
  }
197
228
 
229
+ function allGroups(parameterReference, predicate) {
230
+ if (parameterReference?.[PARAMETER_REF_BRAND] !== true || parameterReference.shape !== 'groupedList') {
231
+ throw new TypeError('allGroups requires a grouped list parameter')
232
+ }
233
+ const parameter = referenceName(parameterReference, 'parameter')
234
+ assertScalarType(parameterReference.keyType)
235
+ assertScalarType(parameterReference.scalarType)
236
+ if (typeof predicate !== 'function') {
237
+ throw new TypeError('allGroups predicate must be a function')
238
+ }
239
+
240
+ const scope = randomUUID()
241
+ const key = Object.freeze({ kind: 'groupKey', scope })
242
+ const values = { scope }
243
+ Object.defineProperty(values, GROUP_VALUES_REF_BRAND, { value: true })
244
+ Object.freeze(values)
245
+
246
+ return {
247
+ kind: 'allGroups',
248
+ parameter,
249
+ scope,
250
+ predicate: asExpression(predicate({ key, values })),
251
+ }
252
+ }
253
+
198
254
  function and(...expressions) {
199
255
  return {
200
256
  kind: 'and',
@@ -334,8 +390,8 @@ function parameterDefinition(name, scalarType, required, shape = 'scalar') {
334
390
  if (required) {
335
391
  definition.required = true
336
392
  }
337
- if (shape === 'list') {
338
- definition.shape = 'list'
393
+ if (shape !== 'scalar') {
394
+ definition.shape = shape
339
395
  }
340
396
  Object.defineProperty(definition, PARAMETER_REF_BRAND, { value: true })
341
397
  return definition
@@ -364,6 +420,9 @@ function unaryExpression(kind, expression) {
364
420
  }
365
421
 
366
422
  function asExpression(value) {
423
+ if (value?.[GROUP_VALUES_REF_BRAND] === true) {
424
+ throw new TypeError('Group values cannot be used as a scalar expression')
425
+ }
367
426
  if (value?.[SUMMARY_FIELD_BRAND] === true) {
368
427
  return value.expression
369
428
  }
@@ -406,6 +465,7 @@ function assertScalarType(scalarType) {
406
465
  }
407
466
 
408
467
  module.exports = {
468
+ allGroups,
409
469
  and,
410
470
  asExpression,
411
471
  average,
@@ -439,6 +499,8 @@ module.exports = {
439
499
  neq,
440
500
  not,
441
501
  nullable,
502
+ optionalGroupsParam: optionalGroupsParameter,
503
+ optionalGroupsParameter,
442
504
  optionalListParam: optionalListParameter,
443
505
  optionalListParameter,
444
506
  optionalParam: optionalParameter,
@@ -446,6 +508,8 @@ module.exports = {
446
508
  or,
447
509
  param,
448
510
  referenceName,
511
+ requiredGroupsParam: requiredGroupsParameter,
512
+ requiredGroupsParameter,
449
513
  requiredListParam: requiredListParameter,
450
514
  requiredListParameter,
451
515
  requiredParam: requiredParameter,
@@ -1,5 +1,6 @@
1
1
  'use strict'
2
2
 
3
3
  exports.GRAPH_MODULE_BRAND = Symbol('GraphModule')
4
+ exports.GROUP_VALUES_REF_BRAND = Symbol('GroupValuesRef')
4
5
  exports.PARAMETER_REF_BRAND = Symbol('ParameterRef')
5
6
  exports.SUMMARY_FIELD_BRAND = Symbol('SummaryField')
package/native.d.ts CHANGED
@@ -95,4 +95,5 @@ export interface SqlBinding {
95
95
  parameter: string
96
96
  scalarType: import('./dsl.js').ScalarType
97
97
  index?: number
98
+ path?: Array<number | string>
98
99
  }
package/native.js CHANGED
@@ -81,12 +81,12 @@ function requireNative() {
81
81
  const binding = require('@query-graph/core-android-arm64')
82
82
  const bindingPackageVersion = require('@query-graph/core-android-arm64/package.json').version
83
83
  if (
84
- bindingPackageVersion !== '1.1.6' &&
84
+ bindingPackageVersion !== '1.1.7' &&
85
85
  process.env.NAPI_RS_ENFORCE_VERSION_CHECK &&
86
86
  process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0'
87
87
  ) {
88
88
  throw new Error(
89
- `Native binding package version mismatch, expected 1.1.6 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`,
89
+ `Native binding package version mismatch, expected 1.1.7 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`,
90
90
  )
91
91
  }
92
92
  return binding
@@ -103,12 +103,12 @@ function requireNative() {
103
103
  const binding = require('@query-graph/core-android-arm-eabi')
104
104
  const bindingPackageVersion = require('@query-graph/core-android-arm-eabi/package.json').version
105
105
  if (
106
- bindingPackageVersion !== '1.1.6' &&
106
+ bindingPackageVersion !== '1.1.7' &&
107
107
  process.env.NAPI_RS_ENFORCE_VERSION_CHECK &&
108
108
  process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0'
109
109
  ) {
110
110
  throw new Error(
111
- `Native binding package version mismatch, expected 1.1.6 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`,
111
+ `Native binding package version mismatch, expected 1.1.7 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`,
112
112
  )
113
113
  }
114
114
  return binding
@@ -129,12 +129,12 @@ function requireNative() {
129
129
  const binding = require('@query-graph/core-win32-x64-msvc')
130
130
  const bindingPackageVersion = require('@query-graph/core-win32-x64-msvc/package.json').version
131
131
  if (
132
- bindingPackageVersion !== '1.1.6' &&
132
+ bindingPackageVersion !== '1.1.7' &&
133
133
  process.env.NAPI_RS_ENFORCE_VERSION_CHECK &&
134
134
  process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0'
135
135
  ) {
136
136
  throw new Error(
137
- `Native binding package version mismatch, expected 1.1.6 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`,
137
+ `Native binding package version mismatch, expected 1.1.7 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`,
138
138
  )
139
139
  }
140
140
  return binding
@@ -151,12 +151,12 @@ function requireNative() {
151
151
  const binding = require('@query-graph/core-win32-ia32-msvc')
152
152
  const bindingPackageVersion = require('@query-graph/core-win32-ia32-msvc/package.json').version
153
153
  if (
154
- bindingPackageVersion !== '1.1.6' &&
154
+ bindingPackageVersion !== '1.1.7' &&
155
155
  process.env.NAPI_RS_ENFORCE_VERSION_CHECK &&
156
156
  process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0'
157
157
  ) {
158
158
  throw new Error(
159
- `Native binding package version mismatch, expected 1.1.6 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`,
159
+ `Native binding package version mismatch, expected 1.1.7 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`,
160
160
  )
161
161
  }
162
162
  return binding
@@ -173,12 +173,12 @@ function requireNative() {
173
173
  const binding = require('@query-graph/core-win32-arm64-msvc')
174
174
  const bindingPackageVersion = require('@query-graph/core-win32-arm64-msvc/package.json').version
175
175
  if (
176
- bindingPackageVersion !== '1.1.6' &&
176
+ bindingPackageVersion !== '1.1.7' &&
177
177
  process.env.NAPI_RS_ENFORCE_VERSION_CHECK &&
178
178
  process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0'
179
179
  ) {
180
180
  throw new Error(
181
- `Native binding package version mismatch, expected 1.1.6 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`,
181
+ `Native binding package version mismatch, expected 1.1.7 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`,
182
182
  )
183
183
  }
184
184
  return binding
@@ -198,12 +198,12 @@ function requireNative() {
198
198
  const binding = require('@query-graph/core-darwin-universal')
199
199
  const bindingPackageVersion = require('@query-graph/core-darwin-universal/package.json').version
200
200
  if (
201
- bindingPackageVersion !== '1.1.6' &&
201
+ bindingPackageVersion !== '1.1.7' &&
202
202
  process.env.NAPI_RS_ENFORCE_VERSION_CHECK &&
203
203
  process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0'
204
204
  ) {
205
205
  throw new Error(
206
- `Native binding package version mismatch, expected 1.1.6 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`,
206
+ `Native binding package version mismatch, expected 1.1.7 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`,
207
207
  )
208
208
  }
209
209
  return binding
@@ -220,12 +220,12 @@ function requireNative() {
220
220
  const binding = require('@query-graph/core-darwin-x64')
221
221
  const bindingPackageVersion = require('@query-graph/core-darwin-x64/package.json').version
222
222
  if (
223
- bindingPackageVersion !== '1.1.6' &&
223
+ bindingPackageVersion !== '1.1.7' &&
224
224
  process.env.NAPI_RS_ENFORCE_VERSION_CHECK &&
225
225
  process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0'
226
226
  ) {
227
227
  throw new Error(
228
- `Native binding package version mismatch, expected 1.1.6 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`,
228
+ `Native binding package version mismatch, expected 1.1.7 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`,
229
229
  )
230
230
  }
231
231
  return binding
@@ -242,12 +242,12 @@ function requireNative() {
242
242
  const binding = require('@query-graph/core-darwin-arm64')
243
243
  const bindingPackageVersion = require('@query-graph/core-darwin-arm64/package.json').version
244
244
  if (
245
- bindingPackageVersion !== '1.1.6' &&
245
+ bindingPackageVersion !== '1.1.7' &&
246
246
  process.env.NAPI_RS_ENFORCE_VERSION_CHECK &&
247
247
  process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0'
248
248
  ) {
249
249
  throw new Error(
250
- `Native binding package version mismatch, expected 1.1.6 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`,
250
+ `Native binding package version mismatch, expected 1.1.7 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`,
251
251
  )
252
252
  }
253
253
  return binding
@@ -268,12 +268,12 @@ function requireNative() {
268
268
  const binding = require('@query-graph/core-freebsd-x64')
269
269
  const bindingPackageVersion = require('@query-graph/core-freebsd-x64/package.json').version
270
270
  if (
271
- bindingPackageVersion !== '1.1.6' &&
271
+ bindingPackageVersion !== '1.1.7' &&
272
272
  process.env.NAPI_RS_ENFORCE_VERSION_CHECK &&
273
273
  process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0'
274
274
  ) {
275
275
  throw new Error(
276
- `Native binding package version mismatch, expected 1.1.6 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`,
276
+ `Native binding package version mismatch, expected 1.1.7 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`,
277
277
  )
278
278
  }
279
279
  return binding
@@ -290,12 +290,12 @@ function requireNative() {
290
290
  const binding = require('@query-graph/core-freebsd-arm64')
291
291
  const bindingPackageVersion = require('@query-graph/core-freebsd-arm64/package.json').version
292
292
  if (
293
- bindingPackageVersion !== '1.1.6' &&
293
+ bindingPackageVersion !== '1.1.7' &&
294
294
  process.env.NAPI_RS_ENFORCE_VERSION_CHECK &&
295
295
  process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0'
296
296
  ) {
297
297
  throw new Error(
298
- `Native binding package version mismatch, expected 1.1.6 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`,
298
+ `Native binding package version mismatch, expected 1.1.7 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`,
299
299
  )
300
300
  }
301
301
  return binding
@@ -317,12 +317,12 @@ function requireNative() {
317
317
  const binding = require('@query-graph/core-linux-x64-musl')
318
318
  const bindingPackageVersion = require('@query-graph/core-linux-x64-musl/package.json').version
319
319
  if (
320
- bindingPackageVersion !== '1.1.6' &&
320
+ bindingPackageVersion !== '1.1.7' &&
321
321
  process.env.NAPI_RS_ENFORCE_VERSION_CHECK &&
322
322
  process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0'
323
323
  ) {
324
324
  throw new Error(
325
- `Native binding package version mismatch, expected 1.1.6 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`,
325
+ `Native binding package version mismatch, expected 1.1.7 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`,
326
326
  )
327
327
  }
328
328
  return binding
@@ -339,12 +339,12 @@ function requireNative() {
339
339
  const binding = require('@query-graph/core-linux-x64-gnu')
340
340
  const bindingPackageVersion = require('@query-graph/core-linux-x64-gnu/package.json').version
341
341
  if (
342
- bindingPackageVersion !== '1.1.6' &&
342
+ bindingPackageVersion !== '1.1.7' &&
343
343
  process.env.NAPI_RS_ENFORCE_VERSION_CHECK &&
344
344
  process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0'
345
345
  ) {
346
346
  throw new Error(
347
- `Native binding package version mismatch, expected 1.1.6 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`,
347
+ `Native binding package version mismatch, expected 1.1.7 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`,
348
348
  )
349
349
  }
350
350
  return binding
@@ -363,12 +363,12 @@ function requireNative() {
363
363
  const binding = require('@query-graph/core-linux-arm64-musl')
364
364
  const bindingPackageVersion = require('@query-graph/core-linux-arm64-musl/package.json').version
365
365
  if (
366
- bindingPackageVersion !== '1.1.6' &&
366
+ bindingPackageVersion !== '1.1.7' &&
367
367
  process.env.NAPI_RS_ENFORCE_VERSION_CHECK &&
368
368
  process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0'
369
369
  ) {
370
370
  throw new Error(
371
- `Native binding package version mismatch, expected 1.1.6 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`,
371
+ `Native binding package version mismatch, expected 1.1.7 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`,
372
372
  )
373
373
  }
374
374
  return binding
@@ -385,12 +385,12 @@ function requireNative() {
385
385
  const binding = require('@query-graph/core-linux-arm64-gnu')
386
386
  const bindingPackageVersion = require('@query-graph/core-linux-arm64-gnu/package.json').version
387
387
  if (
388
- bindingPackageVersion !== '1.1.6' &&
388
+ bindingPackageVersion !== '1.1.7' &&
389
389
  process.env.NAPI_RS_ENFORCE_VERSION_CHECK &&
390
390
  process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0'
391
391
  ) {
392
392
  throw new Error(
393
- `Native binding package version mismatch, expected 1.1.6 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`,
393
+ `Native binding package version mismatch, expected 1.1.7 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`,
394
394
  )
395
395
  }
396
396
  return binding
@@ -409,12 +409,12 @@ function requireNative() {
409
409
  const binding = require('@query-graph/core-linux-arm-musleabihf')
410
410
  const bindingPackageVersion = require('@query-graph/core-linux-arm-musleabihf/package.json').version
411
411
  if (
412
- bindingPackageVersion !== '1.1.6' &&
412
+ bindingPackageVersion !== '1.1.7' &&
413
413
  process.env.NAPI_RS_ENFORCE_VERSION_CHECK &&
414
414
  process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0'
415
415
  ) {
416
416
  throw new Error(
417
- `Native binding package version mismatch, expected 1.1.6 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`,
417
+ `Native binding package version mismatch, expected 1.1.7 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`,
418
418
  )
419
419
  }
420
420
  return binding
@@ -431,12 +431,12 @@ function requireNative() {
431
431
  const binding = require('@query-graph/core-linux-arm-gnueabihf')
432
432
  const bindingPackageVersion = require('@query-graph/core-linux-arm-gnueabihf/package.json').version
433
433
  if (
434
- bindingPackageVersion !== '1.1.6' &&
434
+ bindingPackageVersion !== '1.1.7' &&
435
435
  process.env.NAPI_RS_ENFORCE_VERSION_CHECK &&
436
436
  process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0'
437
437
  ) {
438
438
  throw new Error(
439
- `Native binding package version mismatch, expected 1.1.6 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`,
439
+ `Native binding package version mismatch, expected 1.1.7 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`,
440
440
  )
441
441
  }
442
442
  return binding
@@ -455,12 +455,12 @@ function requireNative() {
455
455
  const binding = require('@query-graph/core-linux-loong64-musl')
456
456
  const bindingPackageVersion = require('@query-graph/core-linux-loong64-musl/package.json').version
457
457
  if (
458
- bindingPackageVersion !== '1.1.6' &&
458
+ bindingPackageVersion !== '1.1.7' &&
459
459
  process.env.NAPI_RS_ENFORCE_VERSION_CHECK &&
460
460
  process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0'
461
461
  ) {
462
462
  throw new Error(
463
- `Native binding package version mismatch, expected 1.1.6 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`,
463
+ `Native binding package version mismatch, expected 1.1.7 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`,
464
464
  )
465
465
  }
466
466
  return binding
@@ -477,12 +477,12 @@ function requireNative() {
477
477
  const binding = require('@query-graph/core-linux-loong64-gnu')
478
478
  const bindingPackageVersion = require('@query-graph/core-linux-loong64-gnu/package.json').version
479
479
  if (
480
- bindingPackageVersion !== '1.1.6' &&
480
+ bindingPackageVersion !== '1.1.7' &&
481
481
  process.env.NAPI_RS_ENFORCE_VERSION_CHECK &&
482
482
  process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0'
483
483
  ) {
484
484
  throw new Error(
485
- `Native binding package version mismatch, expected 1.1.6 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`,
485
+ `Native binding package version mismatch, expected 1.1.7 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`,
486
486
  )
487
487
  }
488
488
  return binding
@@ -501,12 +501,12 @@ function requireNative() {
501
501
  const binding = require('@query-graph/core-linux-riscv64-musl')
502
502
  const bindingPackageVersion = require('@query-graph/core-linux-riscv64-musl/package.json').version
503
503
  if (
504
- bindingPackageVersion !== '1.1.6' &&
504
+ bindingPackageVersion !== '1.1.7' &&
505
505
  process.env.NAPI_RS_ENFORCE_VERSION_CHECK &&
506
506
  process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0'
507
507
  ) {
508
508
  throw new Error(
509
- `Native binding package version mismatch, expected 1.1.6 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`,
509
+ `Native binding package version mismatch, expected 1.1.7 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`,
510
510
  )
511
511
  }
512
512
  return binding
@@ -523,12 +523,12 @@ function requireNative() {
523
523
  const binding = require('@query-graph/core-linux-riscv64-gnu')
524
524
  const bindingPackageVersion = require('@query-graph/core-linux-riscv64-gnu/package.json').version
525
525
  if (
526
- bindingPackageVersion !== '1.1.6' &&
526
+ bindingPackageVersion !== '1.1.7' &&
527
527
  process.env.NAPI_RS_ENFORCE_VERSION_CHECK &&
528
528
  process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0'
529
529
  ) {
530
530
  throw new Error(
531
- `Native binding package version mismatch, expected 1.1.6 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`,
531
+ `Native binding package version mismatch, expected 1.1.7 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`,
532
532
  )
533
533
  }
534
534
  return binding
@@ -546,12 +546,12 @@ function requireNative() {
546
546
  const binding = require('@query-graph/core-linux-ppc64-gnu')
547
547
  const bindingPackageVersion = require('@query-graph/core-linux-ppc64-gnu/package.json').version
548
548
  if (
549
- bindingPackageVersion !== '1.1.6' &&
549
+ bindingPackageVersion !== '1.1.7' &&
550
550
  process.env.NAPI_RS_ENFORCE_VERSION_CHECK &&
551
551
  process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0'
552
552
  ) {
553
553
  throw new Error(
554
- `Native binding package version mismatch, expected 1.1.6 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`,
554
+ `Native binding package version mismatch, expected 1.1.7 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`,
555
555
  )
556
556
  }
557
557
  return binding
@@ -568,12 +568,12 @@ function requireNative() {
568
568
  const binding = require('@query-graph/core-linux-s390x-gnu')
569
569
  const bindingPackageVersion = require('@query-graph/core-linux-s390x-gnu/package.json').version
570
570
  if (
571
- bindingPackageVersion !== '1.1.6' &&
571
+ bindingPackageVersion !== '1.1.7' &&
572
572
  process.env.NAPI_RS_ENFORCE_VERSION_CHECK &&
573
573
  process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0'
574
574
  ) {
575
575
  throw new Error(
576
- `Native binding package version mismatch, expected 1.1.6 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`,
576
+ `Native binding package version mismatch, expected 1.1.7 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`,
577
577
  )
578
578
  }
579
579
  return binding
@@ -594,12 +594,12 @@ function requireNative() {
594
594
  const binding = require('@query-graph/core-openharmony-arm64')
595
595
  const bindingPackageVersion = require('@query-graph/core-openharmony-arm64/package.json').version
596
596
  if (
597
- bindingPackageVersion !== '1.1.6' &&
597
+ bindingPackageVersion !== '1.1.7' &&
598
598
  process.env.NAPI_RS_ENFORCE_VERSION_CHECK &&
599
599
  process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0'
600
600
  ) {
601
601
  throw new Error(
602
- `Native binding package version mismatch, expected 1.1.6 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`,
602
+ `Native binding package version mismatch, expected 1.1.7 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`,
603
603
  )
604
604
  }
605
605
  return binding
@@ -616,12 +616,12 @@ function requireNative() {
616
616
  const binding = require('@query-graph/core-openharmony-x64')
617
617
  const bindingPackageVersion = require('@query-graph/core-openharmony-x64/package.json').version
618
618
  if (
619
- bindingPackageVersion !== '1.1.6' &&
619
+ bindingPackageVersion !== '1.1.7' &&
620
620
  process.env.NAPI_RS_ENFORCE_VERSION_CHECK &&
621
621
  process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0'
622
622
  ) {
623
623
  throw new Error(
624
- `Native binding package version mismatch, expected 1.1.6 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`,
624
+ `Native binding package version mismatch, expected 1.1.7 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`,
625
625
  )
626
626
  }
627
627
  return binding
@@ -638,12 +638,12 @@ function requireNative() {
638
638
  const binding = require('@query-graph/core-openharmony-arm')
639
639
  const bindingPackageVersion = require('@query-graph/core-openharmony-arm/package.json').version
640
640
  if (
641
- bindingPackageVersion !== '1.1.6' &&
641
+ bindingPackageVersion !== '1.1.7' &&
642
642
  process.env.NAPI_RS_ENFORCE_VERSION_CHECK &&
643
643
  process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0'
644
644
  ) {
645
645
  throw new Error(
646
- `Native binding package version mismatch, expected 1.1.6 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`,
646
+ `Native binding package version mismatch, expected 1.1.7 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`,
647
647
  )
648
648
  }
649
649
  return binding
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@query-graph/core",
3
- "version": "1.1.6",
3
+ "version": "1.1.7",
4
4
  "description": "Native query graph planner and SQL compiler for Node.js",
5
5
  "workspaces": [
6
6
  "packages/*"
@@ -141,9 +141,9 @@
141
141
  },
142
142
  "packageManager": "yarn@4.13.0",
143
143
  "optionalDependencies": {
144
- "@query-graph/core-win32-x64-msvc": "1.1.6",
145
- "@query-graph/core-darwin-x64": "1.1.6",
146
- "@query-graph/core-linux-x64-gnu": "1.1.6",
147
- "@query-graph/core-darwin-arm64": "1.1.6"
144
+ "@query-graph/core-win32-x64-msvc": "1.1.7",
145
+ "@query-graph/core-darwin-x64": "1.1.7",
146
+ "@query-graph/core-linux-x64-gnu": "1.1.7",
147
+ "@query-graph/core-darwin-arm64": "1.1.7"
148
148
  }
149
149
  }
@@ -1,5 +1,6 @@
1
1
  import type {
2
2
  AggregateExpression,
3
+ AllGroupsExpression,
3
4
  AllNullability,
4
5
  AnyNullability,
5
6
  BinaryExpression,
@@ -13,7 +14,9 @@ import type {
13
14
  FirstBySelection,
14
15
  FunctionExpression,
15
16
  GraphDefinition,
17
+ GroupKeyExpression,
16
18
  InExpression,
19
+ InGroupValuesExpression,
17
20
  InParameterExpression,
18
21
  JoinProjectionPath,
19
22
  LikeExpression,
@@ -110,11 +113,45 @@ export interface ListParameterRef<
110
113
  required?: Required
111
114
  }
112
115
 
116
+ export interface GroupedListParameterRef<
117
+ Name extends string = string,
118
+ Key extends ScalarType = ScalarType,
119
+ Value extends ScalarType = ScalarType,
120
+ Required extends boolean = boolean,
121
+ > extends ParameterDefinition {
122
+ readonly [parameterRefBrand]: true
123
+ name: Name
124
+ scalarType: Value
125
+ shape: 'groupedList'
126
+ keyType: Key
127
+ required?: Required
128
+ }
129
+
130
+ declare const groupValuesRefBrand: unique symbol
131
+
132
+ export interface GroupValuesRef<Type extends ScalarType = ScalarType> {
133
+ readonly [groupValuesRefBrand]: Type
134
+ readonly scope: string
135
+ }
136
+
137
+ export interface GroupedListParameterTypes<Key extends ScalarType = ScalarType, Value extends ScalarType = ScalarType> {
138
+ key: Key
139
+ value: Value
140
+ }
141
+
142
+ export interface GroupScope<Key extends ScalarType = ScalarType, Value extends ScalarType = ScalarType> {
143
+ key: TypedExpression<GroupKeyExpression, Key, false>
144
+ values: GroupValuesRef<Value>
145
+ }
146
+
113
147
  export type ParameterRef<
114
148
  Name extends string = string,
115
149
  Type extends ScalarType = ScalarType,
116
150
  Required extends boolean = boolean,
117
- > = ScalarParameterRef<Name, Type, Required> | ListParameterRef<Name, Type, Required>
151
+ > =
152
+ | ScalarParameterRef<Name, Type, Required>
153
+ | ListParameterRef<Name, Type, Required>
154
+ | GroupedListParameterRef<Name, ScalarType, Type, Required>
118
155
 
119
156
  export type RelationRef<
120
157
  Name extends string = string,
@@ -198,7 +235,13 @@ export interface FieldTypeOptions {
198
235
  selectable?: boolean
199
236
  }
200
237
 
201
- type OptionNullable<Options extends FieldTypeOptions> = Options extends { nullable: true } ? true : false
238
+ type OptionNullable<Options extends FieldTypeOptions> = Options extends unknown
239
+ ? Options['nullable'] extends infer Nullable
240
+ ? Nullable extends true
241
+ ? true
242
+ : false
243
+ : never
244
+ : never
202
245
  type OptionSelectable<Options extends FieldTypeOptions> = Options extends { selectable: false } ? false : true
203
246
 
204
247
  export function fieldType<const Type extends ScalarType, const Options extends FieldTypeOptions = Record<never, never>>(
@@ -237,6 +280,27 @@ export function optionalListParameter<const Name extends string, const Type exte
237
280
  scalarType: Type,
238
281
  ): ListParameterRef<Name, Type, false>
239
282
  export const optionalListParam: typeof optionalListParameter
283
+ export function requiredGroupsParameter<
284
+ const Name extends string,
285
+ const Key extends ScalarType,
286
+ const Value extends ScalarType,
287
+ >(name: Name, types: GroupedListParameterTypes<Key, Value>): GroupedListParameterRef<Name, Key, Value, true>
288
+ export const requiredGroupsParam: typeof requiredGroupsParameter
289
+ export function optionalGroupsParameter<
290
+ const Name extends string,
291
+ const Key extends ScalarType,
292
+ const Value extends ScalarType,
293
+ >(name: Name, types: GroupedListParameterTypes<Key, Value>): GroupedListParameterRef<Name, Key, Value, false>
294
+ export const optionalGroupsParam: typeof optionalGroupsParameter
295
+ export function allGroups<
296
+ const Name extends string,
297
+ const Key extends ScalarType,
298
+ const Value extends ScalarType,
299
+ const Predicate extends ExpressionInput,
300
+ >(
301
+ parameter: GroupedListParameterRef<Name, Key, Value>,
302
+ predicate: (group: GroupScope<Key, Value>) => Predicate,
303
+ ): TypedExpression<AllGroupsExpression<Name>, 'boolean', InputNullability<Predicate>>
240
304
  /** @deprecated Pass a scalar parameter reference directly to an expression factory. */
241
305
  export function param<const Name extends string, const Type extends ScalarType>(
242
306
  parameter: ScalarParameterRef<Name, Type>,
@@ -248,11 +312,20 @@ export function literal<const Value extends LiteralInput>(
248
312
  export function integer(value: number): TypedExpression<LiteralExpression, 'int64', false>
249
313
  export function decimal(value: string | number): TypedExpression<LiteralExpression, 'decimal', false>
250
314
 
251
- type EqualityNullability<Left extends ExpressionInput, Right extends ExpressionInput> = null extends
252
- | InputScalarType<Left>
253
- | InputScalarType<Right>
315
+ // A null literal becomes IS NULL / IS NOT NULL, so literals do not add equality nullability.
316
+ type EqualityOperandNullability<Input extends ExpressionInput> = Input extends LiteralExpression
254
317
  ? false
255
- : AnyInputNullability<readonly [Left, Right]>
318
+ : InputNullability<Input>
319
+
320
+ type EqualityNullability<Left extends ExpressionInput, Right extends ExpressionInput> = Left extends unknown
321
+ ? Right extends unknown
322
+ ? [InputScalarType<Left>] extends [null]
323
+ ? false
324
+ : [InputScalarType<Right>] extends [null]
325
+ ? false
326
+ : AnyNullability<readonly [EqualityOperandNullability<Left>, EqualityOperandNullability<Right>]>
327
+ : never
328
+ : never
256
329
 
257
330
  export function eq<const Left extends ExpressionInput, const Right extends ExpressionInput>(
258
331
  left: Left,
@@ -297,6 +370,10 @@ export function inParameter<
297
370
  expression: Value,
298
371
  parameter: ListParameterRef<Name, Type>,
299
372
  ): TypedExpression<InParameterExpression<Name>, 'boolean', InputNullability<Value>>
373
+ export function inParameter<const Value extends ExpressionInput, const Type extends ScalarType>(
374
+ expression: Value,
375
+ values: GroupValuesRef<Type>,
376
+ ): TypedExpression<InGroupValuesExpression, 'boolean', InputNullability<Value>>
300
377
  export const inParam: typeof inParameter
301
378
  export function or<const Expressions extends readonly ExpressionInput[]>(
302
379
  ...expressions: Expressions
@@ -12,35 +12,17 @@ import type {
12
12
  DefinitionParameter,
13
13
  DefinitionProjectionField,
14
14
  DefinitionProjectionPath,
15
- OperationParameters,
15
+ OperationParameterInput,
16
+ OptionalParameter,
16
17
  OracleCompileOptions,
17
18
  ParameterValue,
18
19
  QueryOperationBase,
19
20
  RelationalQueryGraph,
21
+ RequiredParameter,
20
22
  SqlServerCompileOptions,
21
23
  } from './runtime.js'
22
24
  import type { ListParameterRef } from './authoring.js'
23
25
 
24
- type RequiredParameter<Parameter extends ParameterDefinition> = Parameter extends unknown
25
- ? Parameter extends { required?: true }
26
- ? Parameter
27
- : never
28
- : never
29
-
30
- type OptionalParameter<Parameter extends ParameterDefinition> = Parameter extends unknown
31
- ? Parameter extends { required?: true }
32
- ? never
33
- : Parameter
34
- : never
35
-
36
- type OperationParameterInput<Definition extends GraphDefinitionInput> = [DefinitionParameter<Definition>] extends [
37
- never,
38
- ]
39
- ? { parameters?: never }
40
- : [RequiredParameter<DefinitionParameter<Definition>>] extends [never]
41
- ? { parameters?: OperationParameters<Definition> }
42
- : { parameters: OperationParameters<Definition> }
43
-
44
26
  type ProjectionFieldPath<Field> =
45
27
  Field extends ProjectionTypeMetadata<infer Path, infer _Type, infer _Nullability, infer _SelectedByDefault>
46
28
  ? Path
package/types/model.d.ts CHANGED
@@ -24,7 +24,7 @@ export type ScalarParameterValue<Type extends ScalarType> = Type extends 'boolea
24
24
  ? JsonValue
25
25
  : never
26
26
 
27
- export type ParameterShape = 'scalar' | 'list'
27
+ export type ParameterShape = 'scalar' | 'list' | 'groupedList'
28
28
  export type RelationCardinality = 'one' | 'many'
29
29
  export type OrderDirection = 'asc' | 'desc'
30
30
  export type NullsOrder = 'first' | 'last'
@@ -86,6 +86,7 @@ export interface ParameterDefinition {
86
86
  name: string
87
87
  scalarType: ScalarType
88
88
  shape?: ParameterShape
89
+ keyType?: ScalarType
89
90
  required?: boolean
90
91
  }
91
92
 
@@ -144,6 +145,24 @@ export interface InParameterExpression<Name extends string = string> {
144
145
  parameter: Name
145
146
  }
146
147
 
148
+ export interface GroupKeyExpression {
149
+ kind: 'groupKey'
150
+ scope: string
151
+ }
152
+
153
+ export interface InGroupValuesExpression {
154
+ kind: 'inGroupValues'
155
+ expression: Expression
156
+ scope: string
157
+ }
158
+
159
+ export interface AllGroupsExpression<Name extends string = string> {
160
+ kind: 'allGroups'
161
+ parameter: Name
162
+ scope: string
163
+ predicate: Expression
164
+ }
165
+
147
166
  export interface ExpressionGroup {
148
167
  kind: 'and' | 'or'
149
168
  expressions: Expression[]
@@ -181,6 +200,9 @@ export type Expression =
181
200
  | LikeExpression
182
201
  | InExpression
183
202
  | InParameterExpression
203
+ | GroupKeyExpression
204
+ | InGroupValuesExpression
205
+ | AllGroupsExpression
184
206
  | ExpressionGroup
185
207
  | UnaryExpression
186
208
  | ExistsExpression
@@ -376,7 +398,7 @@ export interface FirstBySelection {
376
398
  export type RelationSelection = FirstBySelection
377
399
 
378
400
  export interface GraphDefinitionInput {
379
- schemaVersion: 11
401
+ schemaVersion: 12
380
402
  name: string
381
403
  root: string
382
404
  sources: SourceDefinition[]
@@ -6,6 +6,7 @@ import type {
6
6
  ProjectionDefinitionInput,
7
7
  RelationalMapping,
8
8
  ScalarParameterValue,
9
+ ScalarType,
9
10
  } from './model.js'
10
11
 
11
12
  export interface QueryOperationBase<SelectPath extends string = string, OrderingName extends string = string> {
@@ -66,17 +67,22 @@ export type DefinitionRelation<Definition extends GraphDefinitionInput> = NonNul
66
67
 
67
68
  export type DefinitionRoot<Definition extends GraphDefinitionInput> = Definition['root']
68
69
 
69
- export type ParameterValue<Parameter extends ParameterDefinition> = Parameter extends { shape: 'list' }
70
- ? readonly ScalarParameterValue<Parameter['scalarType']>[]
71
- : ScalarParameterValue<Parameter['scalarType']>
70
+ export type ParameterValue<Parameter extends ParameterDefinition> = Parameter extends {
71
+ shape: 'groupedList'
72
+ keyType: infer Key extends ScalarType
73
+ }
74
+ ? readonly { key: ScalarParameterValue<Key>; values: readonly ScalarParameterValue<Parameter['scalarType']>[] }[]
75
+ : Parameter extends { shape: 'list' }
76
+ ? readonly ScalarParameterValue<Parameter['scalarType']>[]
77
+ : ScalarParameterValue<Parameter['scalarType']>
72
78
 
73
- type RequiredParameter<Parameter extends ParameterDefinition> = Parameter extends unknown
79
+ export type RequiredParameter<Parameter extends ParameterDefinition> = Parameter extends unknown
74
80
  ? Parameter extends { required?: true }
75
81
  ? Parameter
76
82
  : never
77
83
  : never
78
84
 
79
- type OptionalParameter<Parameter extends ParameterDefinition> = Parameter extends unknown
85
+ export type OptionalParameter<Parameter extends ParameterDefinition> = Parameter extends unknown
80
86
  ? Parameter extends { required?: true }
81
87
  ? never
82
88
  : Parameter
@@ -88,9 +94,9 @@ export type OperationParameters<Definition extends GraphDefinitionInput> = {
88
94
  [Parameter in OptionalParameter<DefinitionParameter<Definition>> as Parameter['name']]?: ParameterValue<Parameter>
89
95
  }
90
96
 
91
- type OperationParameterInput<Definition extends GraphDefinitionInput> = [DefinitionParameter<Definition>] extends [
92
- never,
93
- ]
97
+ export type OperationParameterInput<Definition extends GraphDefinitionInput> = [
98
+ DefinitionParameter<Definition>,
99
+ ] extends [never]
94
100
  ? { parameters?: never }
95
101
  : [RequiredParameter<DefinitionParameter<Definition>>] extends [never]
96
102
  ? { parameters?: OperationParameters<Definition> }