@query-graph/core 1.0.0 → 1.1.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 CHANGED
@@ -496,6 +496,63 @@ JavaScript number и десятичная строка, а list-параметр
496
496
  типа. Эти проверки существуют только в декларациях TypeScript; сериализация
497
497
  definition, N-API вызов, runtime-валидация Rust и SQL compilation не меняются.
498
498
 
499
+ ### Тип результата проекции
500
+
501
+ DSL сохраняет scalar type и nullability каждого projection expression в compile-time metadata.
502
+ `ResultOf` строит из этих сведений вложенный тип результата по projection paths:
503
+
504
+ ```ts
505
+ import type { QueryOperation, ResultOf } from '@query-graph/core/dsl'
506
+
507
+ const operation = {
508
+ select: ['id', 'profile.displayName'],
509
+ parameters: { userId: 42 },
510
+ } as const satisfies QueryOperation<typeof definition>
511
+
512
+ type UserRow = ResultOf<typeof definition, typeof operation>
513
+ // {
514
+ // id: number | string
515
+ // profile: { displayName: string | null }
516
+ // }
517
+ ```
518
+
519
+ Если второй аргумент не передан, в результат входят только поля с `default: true`.
520
+ `ResultOf` также принимает тип, возвращенный `registerDefinition`, или mapped graph:
521
+
522
+ ```ts
523
+ type DefaultUserRow = ResultOf<typeof graph>
524
+ // { id: number | string }
525
+ ```
526
+
527
+ Nullability учитывает nullable-поле и всю цепочку optional relations до его source.
528
+ Required relation после optional relation не делает значение обязательным. Семантика
529
+ выражений тоже сохраняется: например, `coalesce(nullableField, 'fallback')` выводится
530
+ как non-null `string`.
531
+
532
+ Типы значений по умолчанию нейтральны к DB driver: `int64` и `decimal` представлены
533
+ как `number | string`, а `date`, `dateTime` и `binary` - как `string`. Адаптер
534
+ исполнения может передать третьим аргументом собственную полную scalar map:
535
+
536
+ ```ts
537
+ import type { DefaultScalarOutputTypeMap, ResultOf } from '@query-graph/core/dsl'
538
+
539
+ type DriverScalars = Omit<DefaultScalarOutputTypeMap, 'int64' | 'dateTime'> & {
540
+ int64: bigint
541
+ dateTime: Date
542
+ }
543
+
544
+ type DriverUserRow = ResultOf<typeof definition, typeof operation, DriverScalars>
545
+ ```
546
+
547
+ Для точного учета явного `select` operation должна сохранять literal tuple через
548
+ `as const` или `satisfies`. Это только TypeScript API: wire definition, N-API и
549
+ Rust planner не получают дополнительного runtime state.
550
+
551
+ `ResultOf` описывает логическую форму одной строки, восстановленную из путей
552
+ проекции. Он не выполняет запрос и не превращает relation `many` в массив.
553
+ Группировка строк, дедупликация и гидратация коллекций остаются ответственностью
554
+ потребителя модуля.
555
+
499
556
  ## Диагностика
500
557
 
501
558
  Ошибки N-API имеют имя `QueryGraphError` и сохраняют обычный `message` и stack.
package/definition.d.ts CHANGED
@@ -2,19 +2,27 @@ export * from './dsl.js'
2
2
 
3
3
  import type {
4
4
  ConstraintDefinition,
5
- DimensionDefinition,
6
5
  Expression,
7
6
  ExpressionInput,
7
+ ExpressionNullability,
8
+ ExpressionScalarType,
8
9
  FieldExpression,
10
+ FieldSpecIsNullable,
9
11
  FieldSpecMap,
12
+ FieldSpecScalarType,
10
13
  JoinProjectionPath,
11
- MeasureDefinition,
12
14
  ParameterRef,
13
- ProjectionFieldDefinition,
14
15
  RelationCardinality,
15
16
  RelationRef,
16
17
  RelationSelection,
18
+ ScalarType,
19
+ SourceFieldNullability,
20
+ SourceReferenceKey,
17
21
  SourceRef,
22
+ TypedExpression,
23
+ TypedDimensionDefinition,
24
+ TypedMeasureDefinition,
25
+ TypedProjectionField,
18
26
  } from './dsl.js'
19
27
 
20
28
  export const GRAPH_DEFINITION_VERSION: 9
@@ -23,25 +31,41 @@ export function field<
23
31
  const Key extends string,
24
32
  const Fields extends FieldSpecMap,
25
33
  const Name extends Extract<keyof Fields, string>,
26
- >(source: SourceRef<Key, Fields>, name: Name): FieldExpression<Key, Name>
34
+ >(
35
+ source: SourceRef<Key, Fields>,
36
+ name: Name,
37
+ ): TypedExpression<
38
+ FieldExpression<Key, Name>,
39
+ FieldSpecScalarType<Fields[Name]>,
40
+ SourceFieldNullability<Key, FieldSpecIsNullable<Fields[Name]>>
41
+ >
27
42
  export function field<const Source extends string, const Name extends string>(
28
43
  source: Source,
29
44
  name: Name,
30
- ): FieldExpression<Source, Name>
45
+ ): TypedExpression<FieldExpression<Source, Name>, ScalarType, SourceFieldNullability<Source, boolean>>
31
46
 
32
- export interface RelationOptions {
33
- required?: boolean
34
- cardinality?: RelationCardinality
47
+ export interface RelationOptions<
48
+ Required extends boolean = boolean,
49
+ Cardinality extends RelationCardinality = RelationCardinality,
50
+ > {
51
+ required?: Required
52
+ cardinality?: Cardinality
35
53
  selection?: RelationSelection
36
54
  }
37
55
 
38
- export function relation<const Name extends string>(
56
+ export function relation<
57
+ const Name extends string,
58
+ const From extends string | SourceRef,
59
+ const To extends string | SourceRef,
60
+ const Required extends boolean = false,
61
+ const Cardinality extends RelationCardinality = 'one',
62
+ >(
39
63
  name: Name,
40
- from: string | SourceRef,
41
- to: string | SourceRef,
64
+ from: From,
65
+ to: To,
42
66
  on: Expression,
43
- options?: RelationOptions,
44
- ): RelationRef<Name>
67
+ options?: RelationOptions<Required, Cardinality>,
68
+ ): RelationRef<Name, SourceReferenceKey<From>, SourceReferenceKey<To>, Required, Cardinality>
45
69
 
46
70
  export interface ConstraintOptions {
47
71
  when?: string | ParameterRef
@@ -49,39 +73,93 @@ export interface ConstraintOptions {
49
73
 
50
74
  export function constraint(predicate: Expression, options?: ConstraintOptions): ConstraintDefinition
51
75
 
52
- export interface ProjectionOptions {
53
- default?: boolean
76
+ export interface ProjectionOptions<SelectedByDefault extends boolean = boolean> {
77
+ default?: SelectedByDefault
54
78
  }
55
79
 
56
- export function project<const Path extends string>(
80
+ export function project<
81
+ const Path extends string,
82
+ const Value extends ExpressionInput,
83
+ const SelectedByDefault extends boolean = false,
84
+ >(
57
85
  path: Path,
58
- expression: ExpressionInput,
59
- options?: ProjectionOptions,
60
- ): ProjectionFieldDefinition<Path>
61
- export function project<const Path extends readonly string[]>(
86
+ expression: Value,
87
+ options?: ProjectionOptions<SelectedByDefault>,
88
+ ): TypedProjectionField<
89
+ Path,
90
+ Extract<ExpressionScalarType<Value>, ScalarType>,
91
+ ExpressionNullability<Value>,
92
+ SelectedByDefault
93
+ >
94
+ export function project<
95
+ const Path extends readonly string[],
96
+ const Value extends ExpressionInput,
97
+ const SelectedByDefault extends boolean = false,
98
+ >(
62
99
  path: Path,
63
- expression: ExpressionInput,
64
- options?: ProjectionOptions,
65
- ): ProjectionFieldDefinition<JoinProjectionPath<Path>>
100
+ expression: Value,
101
+ options?: ProjectionOptions<SelectedByDefault>,
102
+ ): TypedProjectionField<
103
+ JoinProjectionPath<Path>,
104
+ Extract<ExpressionScalarType<Value>, ScalarType>,
105
+ ExpressionNullability<Value>,
106
+ SelectedByDefault
107
+ >
66
108
 
67
- export function dimension<const Path extends string>(
109
+ export function dimension<
110
+ const Path extends string,
111
+ const Value extends ExpressionInput,
112
+ const SelectedByDefault extends boolean = false,
113
+ >(
68
114
  path: Path,
69
- expression: ExpressionInput,
70
- options?: ProjectionOptions,
71
- ): DimensionDefinition<Path>
72
- export function dimension<const Path extends readonly string[]>(
115
+ expression: Value,
116
+ options?: ProjectionOptions<SelectedByDefault>,
117
+ ): TypedDimensionDefinition<
118
+ Path,
119
+ Extract<ExpressionScalarType<Value>, ScalarType>,
120
+ ExpressionNullability<Value>,
121
+ SelectedByDefault
122
+ >
123
+ export function dimension<
124
+ const Path extends readonly string[],
125
+ const Value extends ExpressionInput,
126
+ const SelectedByDefault extends boolean = false,
127
+ >(
73
128
  path: Path,
74
- expression: ExpressionInput,
75
- options?: ProjectionOptions,
76
- ): DimensionDefinition<JoinProjectionPath<Path>>
129
+ expression: Value,
130
+ options?: ProjectionOptions<SelectedByDefault>,
131
+ ): TypedDimensionDefinition<
132
+ JoinProjectionPath<Path>,
133
+ Extract<ExpressionScalarType<Value>, ScalarType>,
134
+ ExpressionNullability<Value>,
135
+ SelectedByDefault
136
+ >
77
137
 
78
- export function measure<const Path extends string>(
138
+ export function measure<
139
+ const Path extends string,
140
+ const Value extends ExpressionInput,
141
+ const SelectedByDefault extends boolean = false,
142
+ >(
79
143
  path: Path,
80
- expression: ExpressionInput,
81
- options?: ProjectionOptions,
82
- ): MeasureDefinition<Path>
83
- export function measure<const Path extends readonly string[]>(
144
+ expression: Value,
145
+ options?: ProjectionOptions<SelectedByDefault>,
146
+ ): TypedMeasureDefinition<
147
+ Path,
148
+ Extract<ExpressionScalarType<Value>, ScalarType>,
149
+ ExpressionNullability<Value>,
150
+ SelectedByDefault
151
+ >
152
+ export function measure<
153
+ const Path extends readonly string[],
154
+ const Value extends ExpressionInput,
155
+ const SelectedByDefault extends boolean = false,
156
+ >(
84
157
  path: Path,
85
- expression: ExpressionInput,
86
- options?: ProjectionOptions,
87
- ): MeasureDefinition<JoinProjectionPath<Path>>
158
+ expression: Value,
159
+ options?: ProjectionOptions<SelectedByDefault>,
160
+ ): TypedMeasureDefinition<
161
+ JoinProjectionPath<Path>,
162
+ Extract<ExpressionScalarType<Value>, ScalarType>,
163
+ ExpressionNullability<Value>,
164
+ SelectedByDefault
165
+ >