@takeshape/schema 9.32.3 → 9.33.2

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/dist/validate.js CHANGED
@@ -3,13 +3,14 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.ensureValidLatestSchema = ensureValidLatestSchema;
6
+ exports.ensureValidLatestSchemaSyntax = ensureValidLatestSchemaSyntax;
7
7
  exports.ensureValidProjectSchemaImport = ensureValidProjectSchemaImport;
8
8
  exports.ensureValidRoleImport = ensureValidRoleImport;
9
9
  exports.formatError = formatError;
10
10
  exports.validateProjectSchemaImport = validateProjectSchemaImport;
11
11
  exports.validateRoleInput = validateRoleInput;
12
12
  exports.validateSchema = validateSchema;
13
+ exports.validateSchemaSyntax = validateSchemaSyntax;
13
14
 
14
15
  var _types = require("./types/types");
15
16
 
@@ -79,8 +80,14 @@ var _isEqual = _interopRequireDefault(require("lodash/isEqual"));
79
80
 
80
81
  var _pick = _interopRequireDefault(require("lodash/pick"));
81
82
 
83
+ var _pMap = _interopRequireDefault(require("p-map"));
84
+
85
+ var _lodash = require("lodash");
86
+
82
87
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
83
88
 
89
+ const builtInShapeNames = new Set([...Object.keys(_builtinSchema.builtInShapes), ..._scalars.scalars, 'object']);
90
+
84
91
  function findDuplicates(items) {
85
92
  const seen = {};
86
93
 
@@ -214,7 +221,24 @@ function getSemver(schemaVersion) {
214
221
  return (0, _coerce2.default)(schemaVersion) ?? _versions.LEGACY_SCHEMA_VERSION;
215
222
  }
216
223
 
217
- function validateResolver(projectSchema, basePath, resolver) {
224
+ function enumerateBasicResolvers(resolver, path) {
225
+ const results = [];
226
+
227
+ const visit = (resolver, path) => {
228
+ if ((0, _utils.isComposeResolver)(resolver)) {
229
+ resolver.compose.forEach((resolver, i) => {
230
+ visit(resolver, [...path, i]);
231
+ });
232
+ } else {
233
+ results.push([resolver, path]);
234
+ }
235
+ };
236
+
237
+ visit(resolver, path);
238
+ return results;
239
+ }
240
+
241
+ function validateResolver(projectSchema, basePath, baseResolver) {
218
242
  const errors = [];
219
243
  /**
220
244
  * V3.9 Resolver name enums are the set used for earlier v3 validations
@@ -258,14 +282,14 @@ function validateResolver(projectSchema, basePath, resolver) {
258
282
  const isLessThanV3_9_0 = (0, _lt.default)(getSemver(projectSchema.schemaVersion), '3.9.0');
259
283
  const getNamespace = (0, _refs.createGetNamespace)(projectSchema);
260
284
 
261
- const validateBasicResolver = resolver => {
285
+ for (const [resolver, path] of enumerateBasicResolvers(baseResolver, basePath)) {
262
286
  if ((0, _utils.isBasicResolver)(resolver)) {
263
287
  var _projectSchema$servic;
264
288
 
265
289
  if ('service' in resolver && resolver.service !== 'takeshape:local' && !((_projectSchema$servic = projectSchema.services) !== null && _projectSchema$servic !== void 0 && _projectSchema$servic[resolver.service])) {
266
290
  errors.push({
267
291
  type: 'notFound',
268
- path: basePath.concat(['service']),
292
+ path: path.concat(['service']),
269
293
  message: `Invalid service "${resolver.service}"`
270
294
  });
271
295
  }
@@ -273,7 +297,7 @@ function validateResolver(projectSchema, basePath, resolver) {
273
297
  if (isLessThanV3_9_0 && !isValidResolverNameV3_9_0(resolver.name)) {
274
298
  errors.push({
275
299
  type: 'notFound',
276
- path: basePath.concat(['name']),
300
+ path: path.concat(['name']),
277
301
  message: `Invalid resolver name "${resolver.name}"`
278
302
  });
279
303
  }
@@ -289,7 +313,7 @@ function validateResolver(projectSchema, basePath, resolver) {
289
313
  if (!isValidShapeName(shapeName)) {
290
314
  errors.push({
291
315
  type: 'notFound',
292
- path: basePath.concat(['options', 'model']),
316
+ path: path.concat(['options', 'model']),
293
317
  message: `Invalid Model Shape "${shapeName ?? ''}"`
294
318
  });
295
319
  }
@@ -304,7 +328,7 @@ function validateResolver(projectSchema, basePath, resolver) {
304
328
  if (!isValidShapeName(shapeName)) {
305
329
  errors.push({
306
330
  type: 'notFound',
307
- path: basePath.concat(['shapeName']),
331
+ path: path.concat(['shapeName']),
308
332
  message: `Invalid Model Shape "${shapeName ?? ''}"`
309
333
  });
310
334
  }
@@ -322,7 +346,7 @@ function validateResolver(projectSchema, basePath, resolver) {
322
346
  } else {
323
347
  errors.push({
324
348
  type: 'conflict',
325
- path: basePath.concat('to'),
349
+ path: path.concat('to'),
326
350
  message: `Unable to parse property ref "${resolver.to}"`
327
351
  });
328
352
  }
@@ -330,62 +354,115 @@ function validateResolver(projectSchema, basePath, resolver) {
330
354
  } else {
331
355
  errors.push({
332
356
  type: 'notFound',
333
- path: basePath,
357
+ path,
334
358
  message: `Invalid resolver`
335
359
  });
336
360
  }
337
- };
338
-
339
- if ((0, _utils.isComposeResolver)(resolver)) {
340
- resolver.compose.forEach(validateBasicResolver);
341
- } else {
342
- validateBasicResolver(resolver);
343
361
  }
344
362
 
345
363
  return errors;
346
- } // eslint-disable-next-line max-params
347
-
364
+ }
348
365
 
349
- function validateQueryConfig(projectSchema, validReturnShapes, query, operation, name) {
366
+ function validateLocalQueryConfig(projectSchema, query, operation, name) {
350
367
  const location = operation === 'query' ? 'queries' : 'mutations';
351
- const errors = validateResolver(projectSchema, [location, name, 'resolver'], query.resolver);
352
- const {
353
- template,
354
- shapeName
355
- } = (0, _refs.parseReturnShape)(projectSchema, query.shape);
368
+ return validateResolver(projectSchema, [location, name, 'resolver'], query.resolver);
369
+ }
356
370
 
357
- if (template && !(0, _templateShapes.isValidTemplate)(template)) {
358
- errors.push({
359
- type: 'notFound',
360
- path: [location, name, 'shape'],
361
- message: `Invalid template "${template}" for ${operation} "${name}"`
362
- });
371
+ const operationProps = [['query', 'queries'], ['mutation', 'mutations']];
372
+
373
+ function validateLocalQueryConfigs(projectSchema) {
374
+ const errors = [];
375
+
376
+ for (const [operation, prop] of operationProps) {
377
+ for (const name of Object.keys(projectSchema[prop])) {
378
+ errors.push(...validateLocalQueryConfig(projectSchema, projectSchema[prop][name], operation, name));
379
+ }
363
380
  }
364
381
 
365
- if (!validReturnShapes.has(shapeName)) {
366
- errors.push({
367
- type: 'notFound',
368
- path: [location, name, 'shape'],
369
- message: `Invalid shape "${shapeName}" for ${operation} "${name}"`
370
- });
382
+ return errors;
383
+ }
384
+
385
+ function allowDisconnected(context, status) {
386
+ return Boolean(context.allowDisconnectedLayers) && status !== 'notAvailable';
387
+ }
388
+
389
+ function isValidShapeReference(context, layerState, shapeName) {
390
+ if (layerState.status !== 'ok') {
391
+ return allowDisconnected(context, layerState.status);
371
392
  }
372
393
 
394
+ return Boolean(layerState.schema.shapes[shapeName]);
395
+ }
396
+
397
+ async function validateResolverReferences(context, projectSchema, basePath, baseResolver) {
398
+ const errors = [];
399
+ await (0, _pMap.default)(enumerateBasicResolvers(baseResolver, basePath), async ([resolver, path]) => {
400
+ if (resolver.name === 'graphql:query' || resolver.name === 'graphql:mutation') {
401
+ const {
402
+ service,
403
+ fieldName
404
+ } = resolver;
405
+ const prop = resolver.name === 'graphql:query' ? 'queries' : 'mutations';
406
+ const layerState = await context.resolveLayer(service);
407
+ const valid = layerState.status === 'ok' ? Boolean(layerState.schema[prop][fieldName]) : allowDisconnected(context, layerState.status);
408
+
409
+ if (!valid) {
410
+ const operation = resolver.name === 'graphql:query' ? 'query' : 'mutation';
411
+ errors.push({
412
+ type: 'notFound',
413
+ path: path.concat('fieldName'),
414
+ message: `Missing ${operation} "${resolver.fieldName}" in service layer "${service}"`
415
+ });
416
+ }
417
+ } else if (resolver.name === 'delegate') {
418
+ const ref = (0, _refs.parsePropertyRef)(resolver.to);
419
+
420
+ if (ref && ref.serviceId !== 'local') {
421
+ const layerState = await context.resolveLayer(ref.serviceId);
422
+ const valid = layerState.status === 'ok' ? Boolean((0, _get.default)(layerState.schema, (0, _refs.propertyRefItemToPath)((0, _util.value)(''), ref))) : allowDisconnected(context, layerState.status);
423
+
424
+ if (!valid) {
425
+ errors.push({
426
+ type: 'notFound',
427
+ path: path.concat('to'),
428
+ message: `Missing resolver config at property ref "${resolver.to}"`
429
+ });
430
+ }
431
+ }
432
+ }
433
+ });
373
434
  return errors;
374
435
  }
375
436
 
376
- function validateQueryConfigs(projectSchema, additionalShapeNames = builtInShapeNames) {
377
- const validReturnShapes = new Set(Object.keys(projectSchema.shapes).concat(additionalShapeNames));
378
- let errors = [];
437
+ async function validateQueryConfig(context, projectSchema, {
438
+ query,
439
+ name,
440
+ operation
441
+ }) {
442
+ const location = operation === 'query' ? 'queries' : 'mutations';
443
+ return validateResolverReferences(context, projectSchema, [location, name, 'resolver'], query.resolver);
444
+ }
379
445
 
380
- for (const name of Object.keys(projectSchema.queries)) {
381
- errors = errors.concat(validateQueryConfig(projectSchema, validReturnShapes, projectSchema.queries[name], 'query', name));
446
+ async function validateQueryConfigs(context, projectSchema) {
447
+ const isLessThanV3_9_0 = (0, _lt.default)(getSemver(projectSchema.schemaVersion), '3.9.0');
448
+
449
+ if (isLessThanV3_9_0) {
450
+ return [];
382
451
  }
383
452
 
384
- for (const name of Object.keys(projectSchema.mutations)) {
385
- errors = errors.concat(validateQueryConfig(projectSchema, validReturnShapes, projectSchema.mutations[name], 'mutation', name));
453
+ const promises = [];
454
+
455
+ for (const [operation, prop] of operationProps) {
456
+ for (const name of Object.keys(projectSchema[prop])) {
457
+ promises.push(validateQueryConfig(context, projectSchema, {
458
+ query: projectSchema[prop][name],
459
+ operation,
460
+ name
461
+ }));
462
+ }
386
463
  }
387
464
 
388
- return errors;
465
+ return (0, _lodash.flatten)(await Promise.all(promises));
389
466
  }
390
467
 
391
468
  function validateIndexedShapeConfig(projectSchema, shapeName, config) {
@@ -467,8 +544,6 @@ function validateIndexedShapes(projectSchema) {
467
544
  return errors;
468
545
  }
469
546
 
470
- const builtInShapeNames = [...Object.keys(_builtinSchema.builtInShapes), ..._scalars.scalars, 'object'];
471
-
472
547
  function getModelShapeIds(shapes) {
473
548
  return Object.values(shapes).filter(shape => shape.model).map(shape => shape.id);
474
549
  }
@@ -483,44 +558,94 @@ function isAllOfPath(path) {
483
558
  return index !== -1 && (0, _util.isIntegerLike)(path[index + 1]);
484
559
  }
485
560
 
486
- function validateRefs(projectSchema, additionalShapeNames = builtInShapeNames) {
561
+ function validateLocalRefs(projectSchema) {
487
562
  const errors = [];
488
- const shapeNames = new Set([...additionalShapeNames, ...Object.keys(projectSchema.shapes)]);
489
- const refs = (0, _schemaUtil.getAllRefsInShapes)(projectSchema);
563
+ const shapeNames = new Set([...builtInShapeNames, ...Object.keys(projectSchema.shapes)]);
564
+ const refs = (0, _schemaUtil.getAllRefs)(projectSchema).filter(item => !item.isForeign);
490
565
 
491
- if (refs.length > 0) {
492
- refs.forEach(item => {
493
- const shapeName = (0, _refs.refItemToShapeName)(item);
566
+ for (const item of refs) {
567
+ if (item.template && !(0, _templateShapes.isValidTemplate)(item.template)) {
568
+ errors.push({
569
+ type: 'notFound',
570
+ path: item.path,
571
+ message: `Invalid template "${item.template}"`
572
+ });
573
+ }
494
574
 
495
- if (!shapeNames.has(shapeName)) {
496
- errors.push({
497
- type: 'notFound',
498
- path: item.path,
499
- message: `Invalid ref "${(0, _refs.refItemToAtRef)(item)}"`
500
- });
501
- } // Make sure refs inside allOf don't refer to their own Shape
575
+ const shapeName = (0, _refs.refItemToShapeName)(item);
502
576
 
577
+ if (!shapeNames.has(shapeName)) {
578
+ errors.push({
579
+ type: 'notFound',
580
+ path: item.path,
581
+ message: `Invalid ref "${(0, _get.default)(projectSchema, item.path)}"`
582
+ });
583
+ } // Make sure refs inside allOf don't refer to their own Shape
503
584
 
504
- if (item.path[1] === shapeName && isAllOfPath(item.path)) {
505
- errors.push({
506
- type: 'conflict',
507
- path: item.path,
508
- message: `allOf cannot be self-referential`
509
- });
510
- }
511
585
 
512
- const parentPath = (0, _initial.default)(item.path);
513
- const parentSchema = (0, _get.default)(projectSchema, parentPath);
514
- const propName = (0, _last.default)(item.path);
586
+ if (item.path[1] === shapeName && isAllOfPath(item.path)) {
587
+ errors.push({
588
+ type: 'conflict',
589
+ path: item.path,
590
+ message: `allOf cannot be self-referential`
591
+ });
592
+ }
515
593
 
516
- if (propName === '@ref' && parentSchema.$ref) {
517
- errors.push({
518
- type: 'conflict',
519
- path: parentPath,
520
- message: `Ref cannot have both @ref and $ref`
521
- });
522
- }
523
- });
594
+ const parentPath = (0, _initial.default)(item.path);
595
+ const parentSchema = (0, _get.default)(projectSchema, parentPath);
596
+ const propName = (0, _last.default)(item.path);
597
+
598
+ if (propName === '@ref' && parentSchema.$ref) {
599
+ errors.push({
600
+ type: 'conflict',
601
+ path: parentPath,
602
+ message: `Ref cannot have both @ref and $ref`
603
+ });
604
+ }
605
+ }
606
+
607
+ return errors;
608
+ }
609
+
610
+ async function validateRefs(context, projectSchema) {
611
+ const {
612
+ resolveLayer
613
+ } = context;
614
+ const errors = [];
615
+ const refs = (0, _schemaUtil.getAllRefs)(projectSchema);
616
+ const layerIds = new Set();
617
+
618
+ for (const item of refs) {
619
+ if (item.serviceKey !== 'local') {
620
+ layerIds.add(item.serviceKey);
621
+ }
622
+ }
623
+
624
+ const layersById = Object.fromEntries(await (0, _pMap.default)(layerIds, async layerId => [layerId, await resolveLayer(layerId)]));
625
+
626
+ for (const item of refs) {
627
+ if (item.template && !(0, _templateShapes.isValidTemplate)(item.template)) {
628
+ errors.push({
629
+ type: 'notFound',
630
+ path: item.path,
631
+ message: `Invalid template "${item.path}"`
632
+ });
633
+ }
634
+
635
+ const {
636
+ serviceKey: layerId
637
+ } = item;
638
+ const shapeName = (0, _refs.refItemToShapeName)(item);
639
+ const localShapeExists = Boolean(projectSchema.shapes[shapeName]) || builtInShapeNames.has(shapeName);
640
+ const valid = layerId === 'local' ? localShapeExists : localShapeExists || isValidShapeReference(context, layersById[layerId], shapeName);
641
+
642
+ if (!valid) {
643
+ errors.push({
644
+ type: 'notFound',
645
+ path: item.path,
646
+ message: `Invalid ref "${(0, _get.default)(projectSchema, item.path)}"`
647
+ });
648
+ }
524
649
  }
525
650
 
526
651
  return errors;
@@ -788,9 +913,13 @@ function formatError(error) {
788
913
  };
789
914
  }
790
915
 
916
+ function isValidateReferencesContext(context) {
917
+ return Boolean(context.resolveLayer);
918
+ }
919
+
791
920
  const ajv = (0, _jsonSchema.createAjv)();
792
921
 
793
- function validateStructure(schemaVersion, schema, ref, options) {
922
+ function validateStructure(schemaVersion, context, schema, ref) {
794
923
  var _coerce, _ajv$errors;
795
924
 
796
925
  const versionStr = (_coerce = (0, _coerce2.default)(schemaVersion)) === null || _coerce === void 0 ? void 0 : _coerce.format();
@@ -805,7 +934,9 @@ function validateStructure(schemaVersion, schema, ref, options) {
805
934
 
806
935
  ajv.validate(`https://schema.takeshape.io/project-schema/v${versionStr}#${ref ?? ''}`, schema);
807
936
  let errors = ((_ajv$errors = ajv.errors) === null || _ajv$errors === void 0 ? void 0 : _ajv$errors.map(formatError)) ?? [];
808
- const suppressErrorPaths = options === null || options === void 0 ? void 0 : options.suppressErrorPaths;
937
+ const {
938
+ suppressErrorPaths
939
+ } = context;
809
940
 
810
941
  if (errors.length && suppressErrorPaths) {
811
942
  errors = errors.filter(error => {
@@ -828,20 +959,10 @@ function validateStructure(schemaVersion, schema, ref, options) {
828
959
  };
829
960
  }
830
961
 
831
- function validateV3X(version, obj, options) {
832
- const structuralValidation = validateStructure(version, obj, undefined, options);
833
-
834
- if (!structuralValidation.valid) {
835
- return structuralValidation;
836
- }
837
-
838
- const schema = obj;
839
- let errors = [];
840
- const namespaceShapes = (0, _schemaUtil.getAllNamespaceShapes)(schema);
841
- const additionalShapeNames = (options !== null && options !== void 0 && options.additionalShapeNames ? builtInShapeNames.concat(options.additionalShapeNames) : builtInShapeNames).concat(namespaceShapes);
842
- const additionalModelShapeIds = options !== null && options !== void 0 && options.additionalModelShapeIds ? builtInModelShapeIds.concat(options.additionalModelShapeIds) : builtInModelShapeIds;
843
- errors = errors.concat(checkShapeNames(schema.shapes)).concat(checkShapeIds(schema.shapes)).concat(validateWorkflowsV3(schema)).concat(validateQueryConfigs(schema, additionalShapeNames)).concat(validateRefs(schema, additionalShapeNames)).concat(validateDirectives(schema, additionalModelShapeIds)).concat(validateLocales(schema)).concat(checkWorkflowStepNames(schema.workflows)).concat(checkWorkflowStepKeys(schema.workflows)).concat(validateOneOfs(schema)).concat(validateIndexedShapes(schema)).concat(validateInterfaces(schema)).concat(validateInterfaceImplementations(schema));
844
- const suppressErrorPaths = options === null || options === void 0 ? void 0 : options.suppressErrorPaths;
962
+ function formatValidationResult(context, errors, schema) {
963
+ const {
964
+ suppressErrorPaths
965
+ } = context;
845
966
 
846
967
  if (suppressErrorPaths) {
847
968
  errors = errors.filter(error => {
@@ -860,133 +981,142 @@ function validateV3X(version, obj, options) {
860
981
  };
861
982
  }
862
983
 
863
- function validateV4X(version, obj, options) {
864
- const structuralValidation = validateStructure(version, obj);
865
-
866
- if (!structuralValidation.valid) {
867
- return structuralValidation;
868
- }
869
-
870
- const schema = obj;
871
-
872
- for (const [index, layerConfig] of Object.entries(schema.layers)) {
873
- var _options$resolveLayer;
874
-
875
- const layerId = typeof layerConfig === 'string' ? layerConfig : layerConfig.id;
876
- const layer = options === null || options === void 0 ? void 0 : (_options$resolveLayer = options.resolveLayer) === null || _options$resolveLayer === void 0 ? void 0 : _options$resolveLayer.call(options, layerId);
877
-
878
- if (layer) {
879
- const results = validateStructure(version, layer, '/definitions/layerSchema');
880
-
881
- if (!results.valid) {
882
- return results;
883
- }
884
- } else {
885
- return {
886
- valid: false,
887
- schema: undefined,
888
- errors: [{
889
- path: ['layers', index],
890
- type: 'undefined',
891
- message: `Layer with id is undefined`
892
- }]
893
- };
894
- }
895
- }
896
-
897
- return {
898
- valid: true,
899
- schema,
900
- errors: undefined
901
- };
902
- }
903
-
904
984
  const validators = [{
905
985
  range: '^1.0.0',
906
986
 
907
- validate(schemaVersion, obj) {
908
- const structuralValidation = validateStructure(schemaVersion, obj);
909
-
910
- if (!structuralValidation.valid) {
911
- return structuralValidation;
912
- }
913
-
987
+ validateSyntax(schemaVersion, context, obj) {
914
988
  const schemaV1 = obj;
915
989
  let errors = [];
916
990
  errors = errors.concat(checkContentTypeNames(schemaV1.contentTypes)).concat(validateWorkflowsV1(schemaV1)).concat(checkWorkflowStepNames(schemaV1.workflows)).concat(checkWorkflowStepKeys(schemaV1.workflows));
917
- return errors.length ? {
918
- valid: false,
919
- schema: undefined,
920
- errors
921
- } : {
922
- valid: true,
923
- schema: schemaV1,
924
- errors: undefined
925
- };
991
+ return formatValidationResult(context, errors, schemaV1);
926
992
  }
927
993
 
928
994
  }, {
929
995
  range: '^3.0.0',
930
- validate: validateV3X
931
- }, {
932
- range: '^4.0.0',
933
- validate: validateV4X
996
+
997
+ validateSyntax(schemaVersion, context, obj) {
998
+ const schema = obj;
999
+ let errors = [];
1000
+ errors = errors.concat(checkShapeNames(schema.shapes)).concat(checkShapeIds(schema.shapes)).concat(validateWorkflowsV3(schema)).concat(validateLocalQueryConfigs(schema)).concat(validateLocalRefs(schema)).concat(validateDirectives(schema)).concat(validateLocales(schema)).concat(checkWorkflowStepNames(schema.workflows)).concat(checkWorkflowStepKeys(schema.workflows)).concat(validateOneOfs(schema)).concat(validateIndexedShapes(schema)).concat(validateInterfaces(schema)).concat(validateInterfaceImplementations(schema));
1001
+ return formatValidationResult(context, errors, schema);
1002
+ },
1003
+
1004
+ async validateReferences(schemaVersion, context, obj) {
1005
+ const schema = obj;
1006
+ const errors = (0, _lodash.flatten)(await Promise.all([validateRefs(context, schema), validateQueryConfigs(context, schema)]));
1007
+ return formatValidationResult(context, errors, schema);
1008
+ }
1009
+
934
1010
  }];
935
1011
 
936
1012
  function findValidator(schemaVersion) {
937
- const normalizedSchemaVersion = schemaVersion ?? '1';
938
- const schemaSemVer = (0, _coerce2.default)(normalizedSchemaVersion);
1013
+ const schemaSemVer = (0, _coerce2.default)(schemaVersion);
939
1014
 
940
1015
  if (schemaSemVer) {
941
1016
  const validator = validators.find(v => (0, _satisfies.default)(schemaSemVer, v.range));
942
1017
 
943
1018
  if (validator) {
944
- return validator.validate.bind(null, normalizedSchemaVersion);
1019
+ const {
1020
+ validateSyntax,
1021
+ validateReferences
1022
+ } = validator;
1023
+ return {
1024
+ validateSyntax: validateSyntax.bind(null, schemaVersion),
1025
+ ...(validateReferences ? {
1026
+ validateReferences: validateReferences.bind(null, schemaVersion)
1027
+ } : {})
1028
+ };
945
1029
  }
946
1030
  }
947
1031
  }
1032
+
1033
+ const schemaUndefinedResult = {
1034
+ valid: false,
1035
+ schema: undefined,
1036
+ errors: [{
1037
+ path: [],
1038
+ type: 'undefined',
1039
+ message: 'Schema is undefined'
1040
+ }]
1041
+ };
1042
+ const invalidVersionResult = {
1043
+ valid: false,
1044
+ schema: undefined,
1045
+ errors: [{
1046
+ path: [],
1047
+ type: 'invalidVersion',
1048
+ message: 'Unknown schema version'
1049
+ }]
1050
+ };
1051
+
1052
+ function normalizeSchemaVersion(schema) {
1053
+ return schema.schemaVersion ?? '1';
1054
+ }
1055
+
1056
+ function validateSchemaSyntax(obj, options = {}) {
1057
+ if ((0, _isUndefined.default)(obj)) {
1058
+ return schemaUndefinedResult;
1059
+ }
1060
+
1061
+ const schema = obj;
1062
+ const schemaVersion = normalizeSchemaVersion(schema);
1063
+ const validator = findValidator(schemaVersion);
1064
+
1065
+ if (!validator) {
1066
+ return invalidVersionResult;
1067
+ }
1068
+
1069
+ const structuralValidation = validateStructure(schemaVersion, options, obj);
1070
+
1071
+ if (!structuralValidation.valid) {
1072
+ return structuralValidation;
1073
+ }
1074
+
1075
+ return validator.validateSyntax(options, schema);
1076
+ }
948
1077
  /**
949
1078
  * Validates a schema using a matching validation based on the `schemaVersion` property.
950
1079
  */
951
1080
 
952
1081
 
953
- function validateSchema(obj, options) {
1082
+ async function validateSchema(context, obj) {
954
1083
  if ((0, _isUndefined.default)(obj)) {
955
- return {
956
- valid: false,
957
- schema: undefined,
958
- errors: [{
959
- path: [],
960
- type: 'undefined',
961
- message: 'Schema is undefined'
962
- }]
963
- };
1084
+ return schemaUndefinedResult;
964
1085
  }
965
1086
 
1087
+ const contextWithDefaults = {
1088
+ allowDisconnectedLayers: true,
1089
+ ...context
1090
+ };
966
1091
  const schema = obj;
967
- const validator = findValidator(schema.schemaVersion);
1092
+ const schemaVersion = normalizeSchemaVersion(schema);
1093
+ const validator = findValidator(schemaVersion);
968
1094
 
969
1095
  if (!validator) {
970
- return {
971
- valid: false,
972
- schema: undefined,
973
- errors: [{
974
- path: [],
975
- type: 'invalidVersion',
976
- message: 'Unknown schema version'
977
- }]
978
- };
1096
+ return invalidVersionResult;
1097
+ }
1098
+
1099
+ const structuralValidation = validateStructure(schemaVersion, contextWithDefaults, obj);
1100
+
1101
+ if (!structuralValidation.valid) {
1102
+ return structuralValidation;
1103
+ }
1104
+
1105
+ const syntaxValidation = validator.validateSyntax(contextWithDefaults, schema);
1106
+
1107
+ if (!syntaxValidation.valid || !validator.validateReferences || !isValidateReferencesContext(contextWithDefaults)) {
1108
+ return syntaxValidation;
979
1109
  }
980
1110
 
981
- return validator(schema, options);
1111
+ return validator.validateReferences(contextWithDefaults, obj);
982
1112
  }
983
1113
 
984
- function ensureValidLatestSchema(obj) {
1114
+ function ensureValidLatestSchemaSyntax(obj) {
985
1115
  const {
986
1116
  valid,
987
1117
  schema,
988
1118
  errors
989
- } = validateSchema(obj);
1119
+ } = validateSchemaSyntax(obj);
990
1120
 
991
1121
  if (!valid && errors) {
992
1122
  throw new Error(`Invalid Schema "${errors[0].path.join(',')}": "${errors[0].message}"`);
@@ -1080,7 +1210,7 @@ function ensureValidRoleImport(maybeRoles) {
1080
1210
  * Only use when validating an imported schema! ignore fields optional when importing
1081
1211
  */
1082
1212
  function validateProjectSchemaImport(maybeSchema) {
1083
- return validateSchema(maybeSchema, {
1213
+ return validateSchemaSyntax(maybeSchema, {
1084
1214
  suppressErrorPaths: [..._types.projectSchemaImportOptionalProps, ..._types.legacyProjectSchemaImportOptionalProps]
1085
1215
  });
1086
1216
  }
package/es/schema-util.js CHANGED
@@ -746,7 +746,7 @@ export function getAllRefsInQuery(projectSchema, queryPath, query, predicate) {
746
746
  }
747
747
 
748
748
  if (query.args && typeof query.args === 'object') {
749
- visitSchemaProperties(dereferenceObjectSchema(projectSchema, query.args), queryPath, propertyVisitor);
749
+ visitSchemaProperties(dereferenceObjectSchema(projectSchema, query.args), [...queryPath, 'args'], propertyVisitor);
750
750
  }
751
751
 
752
752
  if (query.shape) {