@query-graph/core 1.1.4 → 1.1.5
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 +95 -969
- package/definition.d.ts +11 -0
- package/definition.js +1 -2
- package/docs/authoring.md +161 -0
- package/docs/composition.md +100 -0
- package/docs/diagnostics.md +52 -0
- package/docs/sql.md +68 -0
- package/docs/type-system.md +86 -0
- package/dsl.d.ts +182 -1839
- package/dsl.js +57 -763
- package/index.d.ts +8 -100
- package/index.js +13 -568
- package/lib/authoring/definition.js +425 -0
- package/lib/authoring/primitives.js +433 -0
- package/lib/authoring/symbols.js +4 -0
- package/{composition.js → lib/composition.js} +9 -35
- package/lib/configuration.js +66 -0
- package/native.d.ts +98 -0
- package/native.js +714 -0
- package/package.json +16 -11
- package/types/authoring.d.ts +667 -0
- package/types/composition.d.ts +327 -0
- package/types/model.d.ts +425 -0
- package/types/result.d.ts +394 -0
- package/types/runtime.d.ts +168 -0
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { arrayOf, configurationOf, nonEmptyString, objectOf, optionalBoolean } = require('../configuration.js')
|
|
4
|
+
const { SUMMARY_FIELD_BRAND } = require('./symbols.js')
|
|
5
|
+
|
|
6
|
+
const SCALAR_TYPES = new Set([
|
|
7
|
+
'boolean',
|
|
8
|
+
'int32',
|
|
9
|
+
'int64',
|
|
10
|
+
'float64',
|
|
11
|
+
'decimal',
|
|
12
|
+
'string',
|
|
13
|
+
'date',
|
|
14
|
+
'dateTime',
|
|
15
|
+
'binary',
|
|
16
|
+
'json',
|
|
17
|
+
])
|
|
18
|
+
const FIELD_TYPE_OPTIONS_KEYS = new Set(['nullable', 'selectable'])
|
|
19
|
+
const FIELD_SPECIFICATION_KEYS = new Set(['scalarType', 'nullable', 'selectable'])
|
|
20
|
+
const EXISTS_CONFIGURATION_KEYS = new Set(['from'])
|
|
21
|
+
|
|
22
|
+
function fieldType(scalarType, options = {}) {
|
|
23
|
+
assertScalarType(scalarType)
|
|
24
|
+
const configuration = configurationOf('fieldType', options, FIELD_TYPE_OPTIONS_KEYS)
|
|
25
|
+
optionalBoolean(configuration.nullable, 'fieldType nullable')
|
|
26
|
+
optionalBoolean(configuration.selectable, 'fieldType selectable')
|
|
27
|
+
|
|
28
|
+
const definition = { scalarType }
|
|
29
|
+
if (configuration.nullable === true) {
|
|
30
|
+
definition.nullable = true
|
|
31
|
+
}
|
|
32
|
+
if (configuration.selectable === false) {
|
|
33
|
+
definition.selectable = false
|
|
34
|
+
}
|
|
35
|
+
return definition
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function nullable(specification) {
|
|
39
|
+
return {
|
|
40
|
+
...normalizeFieldSpec(specification),
|
|
41
|
+
nullable: true,
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function hidden(specification) {
|
|
46
|
+
return {
|
|
47
|
+
...normalizeFieldSpec(specification),
|
|
48
|
+
selectable: false,
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function source(key, fieldSpecifications) {
|
|
53
|
+
nonEmptyString(key, 'source key')
|
|
54
|
+
objectOf('source fields', fieldSpecifications)
|
|
55
|
+
|
|
56
|
+
const fields = Object.entries(fieldSpecifications).map(([name, specification]) => ({
|
|
57
|
+
name: nonEmptyString(name, 'source field name'),
|
|
58
|
+
...normalizeFieldSpec(specification),
|
|
59
|
+
}))
|
|
60
|
+
const sourceReference = { key, fields }
|
|
61
|
+
|
|
62
|
+
Object.defineProperty(sourceReference, 'field', {
|
|
63
|
+
enumerable: false,
|
|
64
|
+
value(name) {
|
|
65
|
+
return field(sourceReference, name)
|
|
66
|
+
},
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
return sourceReference
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function field(sourceReference, name) {
|
|
73
|
+
const sourceKey = referenceName(sourceReference, 'source')
|
|
74
|
+
nonEmptyString(name, 'field name')
|
|
75
|
+
|
|
76
|
+
if (typeof sourceReference !== 'string' && !sourceReference.fields.some((candidate) => candidate.name === name)) {
|
|
77
|
+
throw new TypeError(`Source ${sourceKey} does not define field ${name}`)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
kind: 'field',
|
|
82
|
+
source: sourceKey,
|
|
83
|
+
field: name,
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function requiredParameter(name, scalarType) {
|
|
88
|
+
return parameterDefinition(name, scalarType, true)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function optionalParameter(name, scalarType) {
|
|
92
|
+
return parameterDefinition(name, scalarType, false)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function requiredListParameter(name, scalarType) {
|
|
96
|
+
return parameterDefinition(name, scalarType, true, 'list')
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function optionalListParameter(name, scalarType) {
|
|
100
|
+
return parameterDefinition(name, scalarType, false, 'list')
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function param(parameterReference) {
|
|
104
|
+
if (typeof parameterReference !== 'string' && parameterReference?.shape === 'list') {
|
|
105
|
+
throw new TypeError(`List parameter ${parameterReference.name} cannot be used as a scalar expression`)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
kind: 'parameter',
|
|
110
|
+
name: referenceName(parameterReference, 'parameter'),
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function literal(value) {
|
|
115
|
+
if (value === null) {
|
|
116
|
+
return literalExpression({ kind: 'null' })
|
|
117
|
+
}
|
|
118
|
+
if (typeof value === 'boolean') {
|
|
119
|
+
return literalExpression({ kind: 'boolean', value })
|
|
120
|
+
}
|
|
121
|
+
if (typeof value === 'string') {
|
|
122
|
+
return literalExpression({ kind: 'string', value })
|
|
123
|
+
}
|
|
124
|
+
if (typeof value === 'number') {
|
|
125
|
+
return Number.isInteger(value) ? integer(value) : decimal(value)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
throw new TypeError('Literal must be null, boolean, string, or number')
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function integer(value) {
|
|
132
|
+
if (!Number.isSafeInteger(value)) {
|
|
133
|
+
throw new TypeError('Integer literal must be a safe JavaScript integer')
|
|
134
|
+
}
|
|
135
|
+
return literalExpression({ kind: 'integer', value })
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function decimal(value) {
|
|
139
|
+
const text = String(value)
|
|
140
|
+
if (!/^-?(?:\d+(?:\.\d*)?|\.\d+)$/.test(text)) {
|
|
141
|
+
throw new TypeError(`Invalid decimal literal ${text}`)
|
|
142
|
+
}
|
|
143
|
+
return literalExpression({ kind: 'decimal', value: text })
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function eq(left, right) {
|
|
147
|
+
return binaryExpression('eq', left, right)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function neq(left, right) {
|
|
151
|
+
return binaryExpression('notEq', left, right)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function lt(left, right) {
|
|
155
|
+
return binaryExpression('lessThan', left, right)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function lte(left, right) {
|
|
159
|
+
return binaryExpression('lessThanOrEqual', left, right)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function gt(left, right) {
|
|
163
|
+
return binaryExpression('greaterThan', left, right)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function gte(left, right) {
|
|
167
|
+
return binaryExpression('greaterThanOrEqual', left, right)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function like(expression, pattern) {
|
|
171
|
+
return {
|
|
172
|
+
kind: 'like',
|
|
173
|
+
expression: asExpression(expression),
|
|
174
|
+
pattern: asExpression(pattern),
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function inList(expression, values) {
|
|
179
|
+
return {
|
|
180
|
+
kind: 'in',
|
|
181
|
+
expression: asExpression(expression),
|
|
182
|
+
values: arrayOf(values, 'inList values').map(asExpression),
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function inParameter(expression, parameterReference) {
|
|
187
|
+
if (typeof parameterReference !== 'string' && parameterReference?.shape !== 'list') {
|
|
188
|
+
throw new TypeError(`Parameter ${parameterReference?.name ?? '<unknown>'} is not a list parameter`)
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return {
|
|
192
|
+
kind: 'inParameter',
|
|
193
|
+
expression: asExpression(expression),
|
|
194
|
+
parameter: referenceName(parameterReference, 'parameter'),
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function and(...expressions) {
|
|
199
|
+
return {
|
|
200
|
+
kind: 'and',
|
|
201
|
+
expressions: expressions.map(asExpression),
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function or(...expressions) {
|
|
206
|
+
return {
|
|
207
|
+
kind: 'or',
|
|
208
|
+
expressions: expressions.map(asExpression),
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function not(expression) {
|
|
213
|
+
return unaryExpression('not', expression)
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function isNull(expression) {
|
|
217
|
+
return unaryExpression('isNull', expression)
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function isNotNull(expression) {
|
|
221
|
+
return unaryExpression('isNotNull', expression)
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function exists(sourceReference, predicate, options) {
|
|
225
|
+
const expression = {
|
|
226
|
+
kind: 'exists',
|
|
227
|
+
source: referenceName(sourceReference, 'source'),
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (options !== undefined) {
|
|
231
|
+
const configuration = configurationOf('exists', options, EXISTS_CONFIGURATION_KEYS)
|
|
232
|
+
if (configuration.from === undefined) {
|
|
233
|
+
throw new TypeError('exists requires configuration field "from"')
|
|
234
|
+
}
|
|
235
|
+
expression.from = referenceName(configuration.from, 'source')
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (predicate !== undefined) {
|
|
239
|
+
expression.predicate = asExpression(predicate)
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return expression
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function call(name, ...arguments_) {
|
|
246
|
+
return {
|
|
247
|
+
kind: 'function',
|
|
248
|
+
name,
|
|
249
|
+
arguments: arguments_.map(asExpression),
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function lower(expression) {
|
|
254
|
+
return call('lower', expression)
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function upper(expression) {
|
|
258
|
+
return call('upper', expression)
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function coalesce(first, second, ...rest) {
|
|
262
|
+
return call('coalesce', first, second, ...rest)
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function concat(first, ...rest) {
|
|
266
|
+
return call('concat', first, ...rest)
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function aggregate(functionName, expression) {
|
|
270
|
+
const definition = {
|
|
271
|
+
kind: 'aggregate',
|
|
272
|
+
function: functionName,
|
|
273
|
+
}
|
|
274
|
+
if (expression !== undefined) {
|
|
275
|
+
definition.expression = asExpression(expression)
|
|
276
|
+
}
|
|
277
|
+
return definition
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function count(expression) {
|
|
281
|
+
return aggregate('count', expression)
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function countDistinct(expression) {
|
|
285
|
+
return aggregate('countDistinct', expression)
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function sum(expression) {
|
|
289
|
+
return aggregate('sum', expression)
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function average(expression) {
|
|
293
|
+
return aggregate('average', expression)
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function minimum(expression) {
|
|
297
|
+
return aggregate('minimum', expression)
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function maximum(expression) {
|
|
301
|
+
return aggregate('maximum', expression)
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function normalizeFieldSpec(specification) {
|
|
305
|
+
if (typeof specification === 'string') {
|
|
306
|
+
return fieldType(specification)
|
|
307
|
+
}
|
|
308
|
+
if (specification && typeof specification === 'object' && !Array.isArray(specification)) {
|
|
309
|
+
const configuration = configurationOf('field specification', specification, FIELD_SPECIFICATION_KEYS)
|
|
310
|
+
return fieldType(configuration.scalarType, {
|
|
311
|
+
nullable: configuration.nullable,
|
|
312
|
+
selectable: configuration.selectable,
|
|
313
|
+
})
|
|
314
|
+
}
|
|
315
|
+
throw new TypeError('Field specification must contain a scalar type')
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function parameterDefinition(name, scalarType, required, shape = 'scalar') {
|
|
319
|
+
nonEmptyString(name, 'parameter name')
|
|
320
|
+
assertScalarType(scalarType)
|
|
321
|
+
const definition = { name, scalarType }
|
|
322
|
+
if (required) {
|
|
323
|
+
definition.required = true
|
|
324
|
+
}
|
|
325
|
+
if (shape === 'list') {
|
|
326
|
+
definition.shape = 'list'
|
|
327
|
+
}
|
|
328
|
+
return definition
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function literalExpression(value) {
|
|
332
|
+
return {
|
|
333
|
+
kind: 'literal',
|
|
334
|
+
value,
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
function binaryExpression(kind, left, right) {
|
|
339
|
+
return {
|
|
340
|
+
kind,
|
|
341
|
+
left: asExpression(left),
|
|
342
|
+
right: asExpression(right),
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
function unaryExpression(kind, expression) {
|
|
347
|
+
return {
|
|
348
|
+
kind,
|
|
349
|
+
expression: asExpression(expression),
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
function asExpression(value) {
|
|
354
|
+
if (value?.[SUMMARY_FIELD_BRAND] === true) {
|
|
355
|
+
return value.expression
|
|
356
|
+
}
|
|
357
|
+
if (value && typeof value === 'object' && typeof value.kind === 'string') {
|
|
358
|
+
return value
|
|
359
|
+
}
|
|
360
|
+
return literal(value)
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
function referenceName(reference, type) {
|
|
364
|
+
if (typeof reference === 'string') {
|
|
365
|
+
return nonEmptyString(reference, `${type} name`)
|
|
366
|
+
}
|
|
367
|
+
if (type === 'source') {
|
|
368
|
+
if (reference && typeof reference.key === 'string') {
|
|
369
|
+
return nonEmptyString(reference.key, 'source key')
|
|
370
|
+
}
|
|
371
|
+
throw new TypeError('Invalid source reference')
|
|
372
|
+
}
|
|
373
|
+
if (reference && typeof reference.name === 'string') {
|
|
374
|
+
return nonEmptyString(reference.name, `${type} name`)
|
|
375
|
+
}
|
|
376
|
+
throw new TypeError(`Invalid ${type} reference`)
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
function copySource(sourceReference) {
|
|
380
|
+
return {
|
|
381
|
+
key: sourceReference.key,
|
|
382
|
+
fields: sourceReference.fields.map((fieldDefinition) => ({ ...fieldDefinition })),
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
function assertScalarType(scalarType) {
|
|
387
|
+
if (!SCALAR_TYPES.has(scalarType)) {
|
|
388
|
+
throw new TypeError(`Unknown scalar type ${scalarType}`)
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
module.exports = {
|
|
393
|
+
and,
|
|
394
|
+
asExpression,
|
|
395
|
+
average,
|
|
396
|
+
coalesce,
|
|
397
|
+
concat,
|
|
398
|
+
copySource,
|
|
399
|
+
count,
|
|
400
|
+
countDistinct,
|
|
401
|
+
decimal,
|
|
402
|
+
eq,
|
|
403
|
+
exists,
|
|
404
|
+
fieldType,
|
|
405
|
+
gt,
|
|
406
|
+
gte,
|
|
407
|
+
hidden,
|
|
408
|
+
inList,
|
|
409
|
+
inParameter,
|
|
410
|
+
integer,
|
|
411
|
+
isNotNull,
|
|
412
|
+
isNull,
|
|
413
|
+
like,
|
|
414
|
+
literal,
|
|
415
|
+
lower,
|
|
416
|
+
lt,
|
|
417
|
+
lte,
|
|
418
|
+
maximum,
|
|
419
|
+
minimum,
|
|
420
|
+
neq,
|
|
421
|
+
not,
|
|
422
|
+
nullable,
|
|
423
|
+
optionalListParameter,
|
|
424
|
+
optionalParameter,
|
|
425
|
+
or,
|
|
426
|
+
param,
|
|
427
|
+
referenceName,
|
|
428
|
+
requiredListParameter,
|
|
429
|
+
requiredParameter,
|
|
430
|
+
source,
|
|
431
|
+
sum,
|
|
432
|
+
upper,
|
|
433
|
+
}
|
|
@@ -1,34 +1,15 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const { arrayOf, configurationOf, nonEmptyString, objectOf, optionalEnum } = require('./configuration.js')
|
|
4
|
+
|
|
3
5
|
const BATCH_QUERY_KEYS = new Set(['graph', 'key'])
|
|
4
6
|
const BATCH_QUERY_KEY_KEYS = new Set(['path', 'parameter'])
|
|
5
7
|
const BATCH_RELATION_KEYS = new Set(['name', 'from', 'query', 'cardinality', 'parameters', 'ordering'])
|
|
6
8
|
const COMPOSE_KEYS = new Set(['root', 'relations'])
|
|
9
|
+
const BATCH_CARDINALITIES = new Set(['one', 'many'])
|
|
7
10
|
const batchQueryBrand = Symbol('BatchQuery')
|
|
8
11
|
const batchBrand = Symbol('BatchRelation')
|
|
9
12
|
|
|
10
|
-
function configurationOf(factory, value, allowedKeys) {
|
|
11
|
-
if (value === null || typeof value !== 'object' || Array.isArray(value)) {
|
|
12
|
-
throw new TypeError(`${factory} expects a configuration object`)
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const unknownKeys = Reflect.ownKeys(value).filter((key) => typeof key !== 'string' || !allowedKeys.has(key))
|
|
16
|
-
if (unknownKeys.length > 0) {
|
|
17
|
-
const label = unknownKeys.length === 1 ? 'field' : 'fields'
|
|
18
|
-
const keys = unknownKeys.map((key) => JSON.stringify(String(key))).join(', ')
|
|
19
|
-
throw new TypeError(`${factory} received unknown configuration ${label} ${keys}`)
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
return value
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function nonEmptyString(value, label) {
|
|
26
|
-
if (typeof value !== 'string' || value.trim() === '') {
|
|
27
|
-
throw new TypeError(`${label} must be a non-empty string`)
|
|
28
|
-
}
|
|
29
|
-
return value
|
|
30
|
-
}
|
|
31
|
-
|
|
32
13
|
function relationalGraph(value, label) {
|
|
33
14
|
if (value === null || typeof value !== 'object' || typeof value.compose !== 'function') {
|
|
34
15
|
throw new TypeError(`${label} must be a RelationalQueryGraph`)
|
|
@@ -72,17 +53,12 @@ function batchRelation(value) {
|
|
|
72
53
|
|
|
73
54
|
const from = nonEmptyString(configuration.from, 'batchRelation.from')
|
|
74
55
|
const query = batchQueryOf(configuration.query, 'batchRelation.query')
|
|
75
|
-
|
|
76
|
-
if (configuration.cardinality
|
|
56
|
+
optionalEnum(configuration.cardinality, BATCH_CARDINALITIES, 'batchRelation.cardinality')
|
|
57
|
+
if (configuration.cardinality === undefined) {
|
|
77
58
|
throw new TypeError("batchRelation.cardinality must be 'one' or 'many'")
|
|
78
59
|
}
|
|
79
|
-
if (
|
|
80
|
-
configuration.parameters
|
|
81
|
-
(configuration.parameters === null ||
|
|
82
|
-
typeof configuration.parameters !== 'object' ||
|
|
83
|
-
Array.isArray(configuration.parameters))
|
|
84
|
-
) {
|
|
85
|
-
throw new TypeError('batchRelation.parameters must be an object')
|
|
60
|
+
if (configuration.parameters !== undefined) {
|
|
61
|
+
objectOf('batchRelation.parameters', configuration.parameters)
|
|
86
62
|
}
|
|
87
63
|
if (configuration.ordering !== undefined) {
|
|
88
64
|
nonEmptyString(configuration.ordering, 'batchRelation.ordering')
|
|
@@ -102,11 +78,9 @@ function batchRelation(value) {
|
|
|
102
78
|
function composeGraph(value) {
|
|
103
79
|
const configuration = configurationOf('composeGraph', value, COMPOSE_KEYS)
|
|
104
80
|
const root = relationalGraph(configuration.root, 'composeGraph.root')
|
|
105
|
-
|
|
106
|
-
throw new TypeError('composeGraph.relations must be an array')
|
|
107
|
-
}
|
|
81
|
+
const relations = arrayOf(configuration.relations, 'composeGraph.relations')
|
|
108
82
|
|
|
109
|
-
return
|
|
83
|
+
return relations.reduce((graph, relation, index) => {
|
|
110
84
|
if (relation?.[batchBrand] !== true) {
|
|
111
85
|
throw new TypeError(`composeGraph.relations[${index}] must be created by batchRelation`)
|
|
112
86
|
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
function objectOf(label, value) {
|
|
4
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value)) {
|
|
5
|
+
throw new TypeError(`${label} must be an object`)
|
|
6
|
+
}
|
|
7
|
+
return value
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function configurationOf(factory, value, allowedKeys) {
|
|
11
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value)) {
|
|
12
|
+
throw new TypeError(`${factory} expects a configuration object`)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const unknownKeys = Reflect.ownKeys(value).filter((key) => typeof key !== 'string' || !allowedKeys.has(key))
|
|
16
|
+
if (unknownKeys.length > 0) {
|
|
17
|
+
const label = unknownKeys.length === 1 ? 'field' : 'fields'
|
|
18
|
+
const keys = unknownKeys.map((key) => JSON.stringify(String(key))).join(', ')
|
|
19
|
+
throw new TypeError(`${factory} received unknown configuration ${label} ${keys}`)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return value
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function nonEmptyString(value, label) {
|
|
26
|
+
if (typeof value !== 'string' || value.trim() === '') {
|
|
27
|
+
throw new TypeError(`${label} must be a non-empty string`)
|
|
28
|
+
}
|
|
29
|
+
return value
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function optionalBoolean(value, label) {
|
|
33
|
+
if (value !== undefined && typeof value !== 'boolean') {
|
|
34
|
+
throw new TypeError(`${label} must be a boolean`)
|
|
35
|
+
}
|
|
36
|
+
return value
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function optionalEnum(value, allowedValues, label) {
|
|
40
|
+
if (value !== undefined && !allowedValues.has(value)) {
|
|
41
|
+
const choices = [...allowedValues].map((choice) => JSON.stringify(choice)).join(' or ')
|
|
42
|
+
throw new TypeError(`${label} must be ${choices}`)
|
|
43
|
+
}
|
|
44
|
+
return value
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function arrayOf(value, label) {
|
|
48
|
+
if (!Array.isArray(value)) {
|
|
49
|
+
throw new TypeError(`${label} must be an array`)
|
|
50
|
+
}
|
|
51
|
+
return value
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function optionalArray(value, label) {
|
|
55
|
+
return value === undefined ? undefined : arrayOf(value, label)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
module.exports = {
|
|
59
|
+
arrayOf,
|
|
60
|
+
configurationOf,
|
|
61
|
+
nonEmptyString,
|
|
62
|
+
objectOf,
|
|
63
|
+
optionalArray,
|
|
64
|
+
optionalBoolean,
|
|
65
|
+
optionalEnum,
|
|
66
|
+
}
|
package/native.d.ts
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/* auto-generated by NAPI-RS */
|
|
2
|
+
/* eslint-disable */
|
|
3
|
+
export declare class CompiledQueryPlan {
|
|
4
|
+
get root(): CompiledSqlStatement
|
|
5
|
+
get batches(): Array<CompiledBatchStep>
|
|
6
|
+
compileBatch(name: string, keys: readonly unknown[]): CompiledSqlStatement
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export declare class ComposedQueryGraph {
|
|
10
|
+
get name(): string
|
|
11
|
+
withBatchRelation(graph: RelationalQueryGraph, relation: import('./dsl.js').BatchRelationWire): ComposedQueryGraph
|
|
12
|
+
compileSqlServerPlan(
|
|
13
|
+
operation: import('./dsl.js').QueryOperation,
|
|
14
|
+
options?: import('./dsl.js').SqlServerCompileOptions,
|
|
15
|
+
): CompiledQueryPlan
|
|
16
|
+
compileOraclePlan(
|
|
17
|
+
operation: import('./dsl.js').QueryOperation,
|
|
18
|
+
options?: import('./dsl.js').OracleCompileOptions,
|
|
19
|
+
): CompiledQueryPlan
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export declare class QueryGraph {
|
|
23
|
+
get name(): string
|
|
24
|
+
get root(): string
|
|
25
|
+
get sourceCount(): number
|
|
26
|
+
get relationCount(): number
|
|
27
|
+
hasSource(source: string): boolean
|
|
28
|
+
hasField(source: string, field: string): boolean
|
|
29
|
+
hasParameter(parameter: string): boolean
|
|
30
|
+
hasRelation(relation: string): boolean
|
|
31
|
+
selectableFields(): Array<string>
|
|
32
|
+
withRelationalMapping(mapping: import('./dsl.js').RelationalMapping): RelationalQueryGraph
|
|
33
|
+
withRelationalMappings(mappings: readonly import('./dsl.js').RelationalMapping[]): RelationalQueryGraph
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export declare class RelationalQueryGraph {
|
|
37
|
+
get name(): string
|
|
38
|
+
compose(): ComposedQueryGraph
|
|
39
|
+
compileSqlServer(
|
|
40
|
+
operation: import('./dsl.js').QueryOperation,
|
|
41
|
+
options?: import('./dsl.js').SqlServerCompileOptions,
|
|
42
|
+
): CompiledSqlStatement
|
|
43
|
+
compileOracle(
|
|
44
|
+
operation: import('./dsl.js').QueryOperation,
|
|
45
|
+
options?: import('./dsl.js').OracleCompileOptions,
|
|
46
|
+
): CompiledSqlStatement
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface CompiledBatchStep {
|
|
50
|
+
name: string
|
|
51
|
+
parentKey: string
|
|
52
|
+
childKey: string
|
|
53
|
+
keyParameter: string
|
|
54
|
+
parameters: Readonly<Record<string, unknown>>
|
|
55
|
+
cardinality: import('./dsl.js').BatchCardinality
|
|
56
|
+
parentKeyInjected: boolean
|
|
57
|
+
childKeyInjected: boolean
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface CompiledSqlColumn {
|
|
61
|
+
name: string
|
|
62
|
+
path: string
|
|
63
|
+
scalarType: import('./dsl.js').ScalarType
|
|
64
|
+
nullable: boolean
|
|
65
|
+
relations: Array<string>
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface CompiledSqlObject {
|
|
69
|
+
path: string
|
|
70
|
+
presenceColumn: string
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface CompiledSqlRelation {
|
|
74
|
+
name: string
|
|
75
|
+
from: string
|
|
76
|
+
to: string
|
|
77
|
+
cardinality: import('./dsl.js').RelationCardinality
|
|
78
|
+
required: boolean
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface CompiledSqlStatement {
|
|
82
|
+
sql: string
|
|
83
|
+
bindings: Array<SqlBinding>
|
|
84
|
+
columns: Array<CompiledSqlColumn>
|
|
85
|
+
objects: Array<CompiledSqlObject>
|
|
86
|
+
relations: Array<CompiledSqlRelation>
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export declare function registerDefinition<const Definition extends import('./dsl.js').GraphDefinitionInput>(
|
|
90
|
+
definition: import('./dsl.js').ExactGraphDefinitionInput<Definition>,
|
|
91
|
+
): import('./dsl.js').QueryGraph<Definition>
|
|
92
|
+
|
|
93
|
+
export interface SqlBinding {
|
|
94
|
+
name: string
|
|
95
|
+
parameter: string
|
|
96
|
+
scalarType: import('./dsl.js').ScalarType
|
|
97
|
+
index?: number
|
|
98
|
+
}
|