@conduit-client/model 3.9.0 → 3.11.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/dist/v1/index.js CHANGED
@@ -7,6 +7,7 @@ import * as url from "url";
7
7
  import amf from "amf-client-js";
8
8
  import path from "path";
9
9
  import SwaggerParser from "@apidevtools/swagger-parser";
10
+ import { MissingPointerError, ResolverError } from "@apidevtools/json-schema-ref-parser";
10
11
  const BindingTypesEnum = ["wire", "imperative", "imperative-legacy", "mutation"];
11
12
  class BaseTypeRegistry extends Map {
12
13
  nameOf(t) {
@@ -13338,6 +13339,7 @@ const annotationSchema = z.object({
13338
13339
  });
13339
13340
  class SwaggerBaseType {
13340
13341
  constructor(api, schema2, schemaName, typeRegistry, factory, logger, fileParserLogger, jsonPath) {
13342
+ var _a;
13341
13343
  this.api = api;
13342
13344
  this.schema = schema2;
13343
13345
  this.schemaName = schemaName;
@@ -13347,13 +13349,18 @@ class SwaggerBaseType {
13347
13349
  this.fileParserLogger = fileParserLogger;
13348
13350
  this.resolved = false;
13349
13351
  this.parsedExtensions = { type: "unidentifiable" };
13352
+ const defaultPosition = {
13353
+ line: 0,
13354
+ column: 0,
13355
+ filePath: ((_a = api.sourceUrl) == null ? void 0 : _a.pathname) ?? ""
13356
+ };
13350
13357
  if (jsonPath) {
13351
- this.schemaPosition = api.getPosition(jsonPath) ?? { line: 0, column: 0 };
13358
+ this.schemaPosition = api.getPosition(jsonPath) ?? defaultPosition;
13352
13359
  } else if (schemaName) {
13353
13360
  const defaultPath = `components/schemas/${schemaName}`;
13354
- this.schemaPosition = api.getPosition(defaultPath) ?? { line: 0, column: 0 };
13361
+ this.schemaPosition = api.getPosition(defaultPath) ?? defaultPosition;
13355
13362
  } else {
13356
- this.schemaPosition = { line: 0, column: 0 };
13363
+ this.schemaPosition = defaultPosition;
13357
13364
  }
13358
13365
  }
13359
13366
  resolve() {
@@ -13516,6 +13523,12 @@ class SwaggerNilTypeImpl extends SwaggerBaseType {
13516
13523
  function isReferenceObject(obj) {
13517
13524
  return obj !== null && typeof obj === "object" && "$ref" in obj;
13518
13525
  }
13526
+ function isSchemaObject(obj) {
13527
+ return obj !== null && typeof obj === "object" && !("$ref" in obj) && ("type" in obj || "properties" in obj || "allOf" in obj || "anyOf" in obj || "oneOf" in obj || "items" in obj);
13528
+ }
13529
+ function isOpenAPISchema(obj) {
13530
+ return isSchemaObject(obj) || isReferenceObject(obj);
13531
+ }
13519
13532
  function isArraySchema(schema2) {
13520
13533
  return schema2.type === "array";
13521
13534
  }
@@ -13590,6 +13603,14 @@ function mapScalarType(schema2) {
13590
13603
  }
13591
13604
  }
13592
13605
  function getRefTypeName(ref) {
13606
+ const hashIndex = ref.lastIndexOf("#");
13607
+ if (hashIndex !== -1) {
13608
+ const fragment = ref.substring(hashIndex + 1);
13609
+ const parts2 = fragment.split("/").filter((p) => p !== "");
13610
+ if (parts2.length > 0) {
13611
+ return parts2[parts2.length - 1];
13612
+ }
13613
+ }
13593
13614
  const parts = ref.split("/");
13594
13615
  return parts[parts.length - 1];
13595
13616
  }
@@ -13607,6 +13628,28 @@ function getEnumValues(schema2, schemaName) {
13607
13628
  );
13608
13629
  });
13609
13630
  }
13631
+ const OPENAPI_DOCUMENT_PROPERTIES = /* @__PURE__ */ new Set([
13632
+ "openapi",
13633
+ "info",
13634
+ "servers",
13635
+ "paths",
13636
+ "components",
13637
+ "security",
13638
+ "tags",
13639
+ "externalDocs",
13640
+ "webhooks",
13641
+ "jsonSchemaDialect"
13642
+ ]);
13643
+ function extractRootLevelSchemas(file) {
13644
+ const schemas2 = {};
13645
+ for (const [key, value] of Object.entries(file)) {
13646
+ if (OPENAPI_DOCUMENT_PROPERTIES.has(key) || key.startsWith("x-")) continue;
13647
+ if (isOpenAPISchema(value)) {
13648
+ schemas2[key] = value;
13649
+ }
13650
+ }
13651
+ return schemas2;
13652
+ }
13610
13653
  class SwaggerEnumerableScalarType extends SwaggerBaseType {
13611
13654
  typeResolve() {
13612
13655
  if (this.schema.enum && this.schema.enum.length > 0) {
@@ -13894,6 +13937,12 @@ class SwaggerRefTypeImpl extends SwaggerBaseType {
13894
13937
  };
13895
13938
  this.resolved = false;
13896
13939
  this.refString = this.schema.$ref || "";
13940
+ if (this.schemaPosition.line === 0 && this.schemaPosition.column === 0 && this.refString && this.api) {
13941
+ const refPosition = this.api.getRefPosition(this.refString);
13942
+ if (refPosition) {
13943
+ this.schemaPosition = refPosition;
13944
+ }
13945
+ }
13897
13946
  }
13898
13947
  typeResolve() {
13899
13948
  if (this.refString) {
@@ -13905,7 +13954,7 @@ class SwaggerRefTypeImpl extends SwaggerBaseType {
13905
13954
  inheritedType.resolve();
13906
13955
  this.$ref = inheritedType;
13907
13956
  } else {
13908
- const errorMessage = `Failed to resolve $ref: ${typeName}`;
13957
+ const errorMessage = `Failed to resolve $ref: ${this.refString}`;
13909
13958
  this.fileParserLogger.error(this.schemaPosition, errorMessage);
13910
13959
  throw new Error(errorMessage);
13911
13960
  }
@@ -14133,7 +14182,7 @@ function swaggerTypeFactory(api, schema2, schemaName, typeRegistry, logger, file
14133
14182
  }
14134
14183
  class SwaggerBaseOperation {
14135
14184
  constructor(operation, methodStr, swaggerTypeFactory2, typeRegistry, logger, fileParserLogger, endpoint, server) {
14136
- var _a, _b;
14185
+ var _a, _b, _c;
14137
14186
  this.operation = operation;
14138
14187
  this.methodStr = methodStr;
14139
14188
  this.swaggerTypeFactory = swaggerTypeFactory2;
@@ -14145,7 +14194,12 @@ class SwaggerBaseOperation {
14145
14194
  this.defaults = endpoint.api.defaults;
14146
14195
  this.method = methodStr.toUpperCase();
14147
14196
  const jsonPath = `paths/${endpoint.path}/${methodStr}`;
14148
- this.position = ((_b = (_a = endpoint.api).getPosition) == null ? void 0 : _b.call(_a, jsonPath)) ?? { line: 0, column: 0 };
14197
+ const defaultPosition = {
14198
+ line: 0,
14199
+ column: 0,
14200
+ filePath: ((_a = endpoint.api.sourceUrl) == null ? void 0 : _a.pathname) ?? "Unknown File"
14201
+ };
14202
+ this.position = ((_c = (_b = endpoint.api).getPosition) == null ? void 0 : _c.call(_b, jsonPath)) ?? defaultPosition;
14149
14203
  const extensionsRaw = extractExtensions(operation);
14150
14204
  this.operationId = operation.operationId;
14151
14205
  this.operationSchemaBuilder = new OperationSchemaBuilder(
@@ -14192,10 +14246,14 @@ ${message}`);
14192
14246
  headers: {}
14193
14247
  };
14194
14248
  const parameters = this.operation.parameters || [];
14195
- for (const param of parameters) {
14196
- if (isReferenceObject(param)) continue;
14249
+ for (let param of parameters) {
14250
+ if (isReferenceObject(param)) {
14251
+ param = this.endpoint.api.resolveRefTargetPath(
14252
+ param.$ref
14253
+ );
14254
+ }
14197
14255
  const paramType = this.swaggerTypeFactory(
14198
- {},
14256
+ this.endpoint.api,
14199
14257
  param.schema || { type: "string" },
14200
14258
  void 0,
14201
14259
  this.typeRegistry,
@@ -14224,12 +14282,17 @@ ${message}`);
14224
14282
  }
14225
14283
  }
14226
14284
  if (this.operation.requestBody) {
14227
- const requestBody = isReferenceObject(this.operation.requestBody) ? void 0 : this.operation.requestBody;
14285
+ let requestBody = this.operation.requestBody;
14286
+ if (isReferenceObject(requestBody)) {
14287
+ requestBody = this.endpoint.api.resolveRefTargetPath(
14288
+ requestBody.$ref
14289
+ );
14290
+ }
14228
14291
  if (requestBody == null ? void 0 : requestBody.content) {
14229
14292
  for (const [mediaType, content] of Object.entries(requestBody.content)) {
14230
14293
  if (content.schema) {
14231
14294
  const dataType = this.swaggerTypeFactory(
14232
- {},
14295
+ this.endpoint.api,
14233
14296
  content.schema,
14234
14297
  void 0,
14235
14298
  this.typeRegistry,
@@ -14251,18 +14314,28 @@ ${message}`);
14251
14314
  buildResponses() {
14252
14315
  const responses = [];
14253
14316
  const opResponses = this.operation.responses || {};
14254
- for (const [statusCode, responseObj] of Object.entries(opResponses)) {
14255
- if (isReferenceObject(responseObj)) continue;
14317
+ for (const [statusCode, opResponse] of Object.entries(opResponses)) {
14318
+ let responseObj = opResponse;
14319
+ if (isReferenceObject(responseObj)) {
14320
+ responseObj = this.endpoint.api.resolveRefTargetPath(
14321
+ responseObj.$ref
14322
+ );
14323
+ }
14256
14324
  const response = {
14257
14325
  statusCode,
14258
14326
  payloads: [],
14259
14327
  headers: {}
14260
14328
  };
14261
14329
  if (responseObj.headers) {
14262
- for (const [headerName, headerObj] of Object.entries(responseObj.headers)) {
14263
- if (isReferenceObject(headerObj)) continue;
14330
+ for (const [headerName, headerValue] of Object.entries(responseObj.headers)) {
14331
+ let headerObj = headerValue;
14332
+ if (isReferenceObject(headerObj)) {
14333
+ headerObj = this.endpoint.api.resolveRefTargetPath(
14334
+ headerObj.$ref
14335
+ );
14336
+ }
14264
14337
  const headerType = this.swaggerTypeFactory(
14265
- {},
14338
+ this.endpoint.api,
14266
14339
  headerObj.schema || { type: "string" },
14267
14340
  void 0,
14268
14341
  this.typeRegistry,
@@ -14280,7 +14353,7 @@ ${message}`);
14280
14353
  for (const [mediaType, content] of Object.entries(responseObj.content)) {
14281
14354
  if (content.schema) {
14282
14355
  const dataType = this.swaggerTypeFactory(
14283
- {},
14356
+ this.endpoint.api,
14284
14357
  content.schema,
14285
14358
  void 0,
14286
14359
  this.typeRegistry,
@@ -14611,8 +14684,10 @@ class SwaggerBaseEndpoint {
14611
14684
  this.path = pathStr;
14612
14685
  this.uriParameters = {};
14613
14686
  const parameters = pathItem.parameters || [];
14614
- for (const param of parameters) {
14615
- if (isReferenceObject(param)) continue;
14687
+ for (let param of parameters) {
14688
+ if (isReferenceObject(param)) {
14689
+ param = api.resolveRefTargetPath(param.$ref);
14690
+ }
14616
14691
  if (param.in !== "path") continue;
14617
14692
  const paramType = swaggerTypeFactory2(
14618
14693
  api,
@@ -14687,46 +14762,42 @@ ${validationMessage}`);
14687
14762
  }
14688
14763
  function getSchemaPosition(subject, validationType) {
14689
14764
  if (validationType === ValidationType.Operation) {
14690
- return subject.position || { line: 0, column: 0 };
14765
+ return subject.position || { line: 0, column: 0, filePath: "" };
14691
14766
  }
14692
- return subject.schemaPosition || { line: 0, column: 0 };
14767
+ return subject.schemaPosition || { line: 0, column: 0, filePath: "" };
14693
14768
  }
14694
- function formatMessage(messages, lineFormatter2, level = 0) {
14695
- const padding = Array(4 * level).fill(" ").join("");
14769
+ function formatMessage(messages, level = 0) {
14770
+ const padding = " ".repeat(level);
14696
14771
  return messages.reduce((result, message) => {
14697
14772
  const position = getSchemaPosition(message.subject, message.validationType);
14698
- return `${result}${padding}- ${lineFormatter2(position, message.message)}
14699
- ${formatMessage(message.subValidationMessages ?? [], lineFormatter2, level + 1)}`;
14773
+ const formatted = lineFormatter(position, message.message, position.filePath);
14774
+ const subMessages = formatMessage(message.subValidationMessages ?? [], level + 1);
14775
+ return `${result}${padding}- ${formatted}
14776
+ ${subMessages}`;
14700
14777
  }, "");
14701
14778
  }
14702
14779
  function isValidSwaggerAPI(api, fileParserLogger) {
14703
- const formatter = (pos, msg) => {
14704
- return lineFormatter(pos, msg, fileParserLogger.filePath);
14705
- };
14780
+ const defaultFilePath = fileParserLogger.filePath;
14706
14781
  const apiValidator = buildApiValidatorFor(api);
14707
14782
  const result = apiValidator.validate(api);
14708
14783
  const isValid2 = result.isOk();
14709
14784
  const messages = isValid2 ? result.value : result.error;
14710
- for (const {
14711
- message,
14712
- subject,
14713
- severity,
14714
- validationType,
14715
- subValidationMessages: extraValidation
14716
- } of messages) {
14717
- let lineMessage = message;
14718
- if (extraValidation !== void 0) {
14719
- const formattedValidation = `${formatMessage(extraValidation, formatter, 1)}`;
14720
- lineMessage = `${lineMessage}
14721
- ${formattedValidation}`;
14722
- }
14785
+ for (const { message, subject, severity, validationType, subValidationMessages } of messages) {
14723
14786
  const position = getSchemaPosition(subject, validationType);
14787
+ let finalMessage = message;
14788
+ if (subValidationMessages !== void 0 && subValidationMessages.length > 0) {
14789
+ finalMessage += `
14790
+ ${formatMessage(subValidationMessages, 1)}`;
14791
+ }
14792
+ if (position.filePath !== defaultFilePath) {
14793
+ finalMessage = `[${position.filePath}:${position.line}:${position.column}] ${finalMessage}`;
14794
+ }
14724
14795
  if (severity === ValidationSeverity.Error) {
14725
- fileParserLogger.error(position, lineMessage);
14796
+ fileParserLogger.error(position, finalMessage);
14726
14797
  } else if (severity === ValidationSeverity.Warning) {
14727
- fileParserLogger.warn(position, lineMessage);
14798
+ fileParserLogger.warn(position, finalMessage);
14728
14799
  } else if (severity === ValidationSeverity.Info) {
14729
- fileParserLogger.info(position, lineMessage);
14800
+ fileParserLogger.info(position, finalMessage);
14730
14801
  }
14731
14802
  }
14732
14803
  return isValid2;
@@ -14753,12 +14824,13 @@ const oneStoreExtensionsSchema = z.object({
14753
14824
  }).strict()
14754
14825
  });
14755
14826
  class SwaggerAPI {
14756
- constructor(document, sourceUrl, services, fileParserLogger, positionMap) {
14827
+ constructor(document, sourceUrl, services, fileParserLogger, positionMap, $refs) {
14757
14828
  this.document = document;
14758
14829
  this.sourceUrl = sourceUrl;
14759
14830
  this.services = services;
14760
14831
  this.fileParserLogger = fileParserLogger;
14761
14832
  this.positionMap = positionMap;
14833
+ this.$refs = $refs;
14762
14834
  this.built = false;
14763
14835
  const urlPath = sourceUrl.pathname;
14764
14836
  this.defaultedNamespace = path.parse(urlPath).name;
@@ -14774,6 +14846,41 @@ class SwaggerAPI {
14774
14846
  getPosition(jsonPath) {
14775
14847
  return this.positionMap.get(jsonPath);
14776
14848
  }
14849
+ /**
14850
+ * Find the position of a $ref declaration by its target value.
14851
+ * @param refValue - The $ref target string (e.g., "./schemas/User.yaml#/TypeThatDoesNotExist")
14852
+ * @returns The position where the $ref is declared, if found
14853
+ */
14854
+ getRefPosition(refValue) {
14855
+ const result = this.positionMap.findRefPosition(refValue);
14856
+ return result == null ? void 0 : result.position;
14857
+ }
14858
+ /**
14859
+ * Resolve a $ref target path with respect to the directory of the source.
14860
+ * This is particularly necessary for relative paths since this.$refs.get() resolves
14861
+ * paths relative to the root file and not the ref source file.
14862
+ * @param refPath - The $ref target path (e.g., "./schemas/User.yaml#/User")
14863
+ * @returns The path from where the $ref is declared (e.g, "file:///path/to/source/./schemas/User.yaml#/User")
14864
+ */
14865
+ resolveRefTargetPath(refTarget) {
14866
+ var _a;
14867
+ const hashIndex = refTarget.indexOf("#");
14868
+ const refTargetFilePath = hashIndex === -1 ? refTarget : refTarget.substring(0, hashIndex);
14869
+ let resolvedPath;
14870
+ if (!refTargetFilePath) {
14871
+ resolvedPath = refTarget;
14872
+ } else if (/^(https?|file):\/\//.test(refTargetFilePath)) {
14873
+ resolvedPath = refTargetFilePath;
14874
+ } else {
14875
+ const refSourceFilePath = (_a = this.positionMap.getRef(refTarget)) == null ? void 0 : _a.filePath;
14876
+ if (!refSourceFilePath) {
14877
+ throw new Error(`Failed to resolve $ref: ${refTarget}`);
14878
+ }
14879
+ const refSourceBasePath = path.dirname(refSourceFilePath);
14880
+ resolvedPath = path.join(refSourceBasePath, refTarget);
14881
+ }
14882
+ return this.$refs.get(resolvedPath);
14883
+ }
14777
14884
  build() {
14778
14885
  if (!this.built) {
14779
14886
  this.built = true;
@@ -14784,9 +14891,14 @@ class SwaggerAPI {
14784
14891
  }
14785
14892
  }
14786
14893
  buildEndpoints() {
14787
- const paths = this.document.paths || {};
14788
- this._endpoints = Object.entries(paths).map(([pathStr, pathItem]) => {
14894
+ var _a;
14895
+ const paths = ((_a = this.$refs.values()[this.sourceUrl.toString()]) == null ? void 0 : _a.paths) || {};
14896
+ this._endpoints = Object.entries(paths).map(([pathStr, pathValue]) => {
14897
+ let pathItem = pathValue;
14789
14898
  if (!pathItem) return void 0;
14899
+ if (isReferenceObject(pathItem)) {
14900
+ pathItem = this.resolveRefTargetPath(pathItem.$ref);
14901
+ }
14790
14902
  return swaggerEndpointFactory(
14791
14903
  pathStr,
14792
14904
  pathItem,
@@ -14831,10 +14943,17 @@ servers:
14831
14943
  });
14832
14944
  }
14833
14945
  buildTypes() {
14834
- var _a;
14835
14946
  this.services.logger.debug("SwaggerAPI - Building the type registry");
14836
14947
  const typeRegistry = this.typeRegistry = new BaseTypeRegistry();
14837
- const schemas2 = ((_a = this.document.components) == null ? void 0 : _a.schemas) || {};
14948
+ const schemas2 = Object.values(this.$refs.values()).reduce(
14949
+ (acc, file) => {
14950
+ var _a;
14951
+ const componentSchemas = ((_a = file.components) == null ? void 0 : _a.schemas) ?? {};
14952
+ const rootSchemas = extractRootLevelSchemas(file);
14953
+ return { ...acc, ...componentSchemas, ...rootSchemas };
14954
+ },
14955
+ {}
14956
+ );
14838
14957
  for (const [name, schema2] of Object.entries(schemas2)) {
14839
14958
  this.services.logger.debug(`SwaggerAPI - Building Swagger Type for ${name}`);
14840
14959
  const swaggerType = swaggerTypeFactory(
@@ -14924,6 +15043,8 @@ servers:
14924
15043
  class SourcePositionMap {
14925
15044
  constructor() {
14926
15045
  this.positions = /* @__PURE__ */ new Map();
15046
+ this.fileUrls = /* @__PURE__ */ new Set();
15047
+ this.refTargets = /* @__PURE__ */ new Map();
14927
15048
  }
14928
15049
  /**
14929
15050
  * Get the position for a given JSON path
@@ -14932,6 +15053,13 @@ class SourcePositionMap {
14932
15053
  get(jsonPath) {
14933
15054
  return this.positions.get(jsonPath);
14934
15055
  }
15056
+ /**
15057
+ * Get the position for a given $ref path
15058
+ * @param refPath - The $ref path (e.g., "./schemas/User.yaml#/User")
15059
+ */
15060
+ getRef(refPath) {
15061
+ return this.refTargets.get(refPath);
15062
+ }
14935
15063
  /**
14936
15064
  * Set the position for a given JSON path
14937
15065
  */
@@ -14950,10 +15078,71 @@ class SourcePositionMap {
14950
15078
  keys() {
14951
15079
  return this.positions.keys();
14952
15080
  }
15081
+ /**
15082
+ * Get all entries as [jsonPath, position] pairs
15083
+ */
15084
+ entries() {
15085
+ return this.positions.entries();
15086
+ }
15087
+ /**
15088
+ * Get the number of files tracked in this map
15089
+ */
15090
+ get fileCount() {
15091
+ return this.fileUrls.size;
15092
+ }
15093
+ /**
15094
+ * Track a $ref target value and its position
15095
+ */
15096
+ setRefTarget(refValue, position) {
15097
+ this.refTargets.set(refValue, position);
15098
+ }
15099
+ /**
15100
+ * Find the position of a $ref that targets a file containing the given path,
15101
+ * or an internal ref containing the given token.
15102
+ */
15103
+ findRefPosition(targetPath, token) {
15104
+ const targetFileName = targetPath.split("/").pop() || targetPath;
15105
+ for (const [refValue, position] of this.refTargets) {
15106
+ if (refValue.includes(targetFileName)) {
15107
+ return { position, refValue };
15108
+ }
15109
+ if (token && refValue.startsWith("#/") && refValue.includes(token)) {
15110
+ return { position, refValue };
15111
+ }
15112
+ }
15113
+ }
15114
+ /**
15115
+ * Merge positions from another map, setting filePath on each position.
15116
+ * Used to aggregate positions from multiple files (e.g., external $refs).
15117
+ * @param other - The source position map to merge from
15118
+ * @param fileUrl - The file URL to associate with these positions
15119
+ */
15120
+ merge(other, fileUrl) {
15121
+ const filePath = extractFilePath(fileUrl);
15122
+ this.fileUrls.add(fileUrl);
15123
+ for (const [jsonPath, position] of other.entries()) {
15124
+ this.positions.set(jsonPath, {
15125
+ ...position,
15126
+ filePath
15127
+ });
15128
+ }
15129
+ for (const [refValue, position] of other.refTargets) {
15130
+ this.refTargets.set(refValue, { ...position, filePath });
15131
+ }
15132
+ }
15133
+ }
15134
+ function extractFilePath(fileUrl) {
15135
+ try {
15136
+ const url2 = new URL(fileUrl);
15137
+ return url2.pathname;
15138
+ } catch {
15139
+ return fileUrl;
15140
+ }
14953
15141
  }
14954
- function buildSourcePositionMap(yamlContent) {
15142
+ function buildSourcePositionMap(yamlContent, fileUrl) {
14955
15143
  const positionMap = new SourcePositionMap();
14956
15144
  const lineCounter = new LineCounter();
15145
+ const filePath = extractFilePath(fileUrl);
14957
15146
  const doc = parseDocument(yamlContent, { lineCounter });
14958
15147
  if (!doc.contents || !isMap(doc.contents)) {
14959
15148
  return positionMap;
@@ -14965,7 +15154,15 @@ function buildSourcePositionMap(yamlContent) {
14965
15154
  const range = pair.key.range;
14966
15155
  if (range) {
14967
15156
  const pos = lineCounter.linePos(range[0]);
14968
- positionMap.set(jsonPath, { line: pos.line, column: pos.col - 1 });
15157
+ positionMap.set(jsonPath, { line: pos.line, column: pos.col - 1, filePath });
15158
+ if (pair.key.value === "$ref" && pair.value && isScalar(pair.value)) {
15159
+ const refValue = String(pair.value.value);
15160
+ positionMap.setRefTarget(refValue, {
15161
+ line: pos.line,
15162
+ column: pos.col - 1,
15163
+ filePath
15164
+ });
15165
+ }
14969
15166
  }
14970
15167
  }
14971
15168
  }
@@ -14988,6 +15185,9 @@ function buildJsonPath(pair, path2) {
14988
15185
  if (segments.length >= 3 && segments[0] === "components" && segments[1] === "schemas") {
14989
15186
  return segments.join("/");
14990
15187
  }
15188
+ if (segments.length >= 1 && !["openapi", "info", "servers", "paths", "components", "x-onestore"].includes(segments[0])) {
15189
+ return segments.join("/");
15190
+ }
14991
15191
  return void 0;
14992
15192
  }
14993
15193
  const positionMapCache = /* @__PURE__ */ new Map();
@@ -14996,7 +15196,7 @@ const positionTrackingParser = {
14996
15196
  canParse: [".yaml", ".yml"],
14997
15197
  async parse(file) {
14998
15198
  const content = Buffer.isBuffer(file.data) ? file.data.toString() : file.data;
14999
- const positionMap = buildSourcePositionMap(content);
15199
+ const positionMap = buildSourcePositionMap(content, file.url);
15000
15200
  positionMapCache.set(file.url, positionMap);
15001
15201
  return parse(content);
15002
15202
  }
@@ -15012,12 +15212,25 @@ const PARSER_OPTIONS = {
15012
15212
  // Still validate $ref resolution
15013
15213
  }
15014
15214
  };
15215
+ function findRefPositionInCache(targetPath, token) {
15216
+ for (const positionMap of positionMapCache.values()) {
15217
+ const result = positionMap.findRefPosition(targetPath, token);
15218
+ if (result) {
15219
+ return result;
15220
+ }
15221
+ }
15222
+ }
15015
15223
  async function parseSwaggerDocument(source, logger) {
15016
15224
  const sourceUrl = source.toString();
15017
15225
  try {
15018
- const api = await SwaggerParser.bundle(sourceUrl, PARSER_OPTIONS);
15019
- const positionMap = positionMapCache.get(sourceUrl) ?? new SourcePositionMap();
15020
- positionMapCache.delete(sourceUrl);
15226
+ const parser = new SwaggerParser();
15227
+ const api = await parser.parse(sourceUrl, PARSER_OPTIONS);
15228
+ const $refs = await parser.resolve(sourceUrl, PARSER_OPTIONS);
15229
+ const positionMap = new SourcePositionMap();
15230
+ for (const [fileUrl, filePositionMap] of positionMapCache) {
15231
+ positionMap.merge(filePositionMap, fileUrl);
15232
+ }
15233
+ positionMapCache.clear();
15021
15234
  if (!("openapi" in api) || !api.openapi.startsWith("3.")) {
15022
15235
  throw new Error(
15023
15236
  `Only OpenAPI 3.x documents are supported. Found version: ${api.openapi || api.swagger || "unknown"}`
@@ -15025,10 +15238,50 @@ async function parseSwaggerDocument(source, logger) {
15025
15238
  }
15026
15239
  return {
15027
15240
  document: api,
15028
- positionMap
15241
+ positionMap,
15242
+ $refs
15029
15243
  };
15030
15244
  } catch (error) {
15031
- positionMapCache.delete(sourceUrl);
15245
+ if (error instanceof MissingPointerError) {
15246
+ const missingFile = error.source;
15247
+ const tokenMatch = error.message.match(/Token "([^"]+)"/);
15248
+ const token = (tokenMatch == null ? void 0 : tokenMatch[1]) ?? "unknown";
15249
+ const refInfo = findRefPositionInCache(missingFile, token);
15250
+ const filename = missingFile.split("/").pop() ?? missingFile;
15251
+ if (refInfo) {
15252
+ const { position, refValue } = refInfo;
15253
+ logger.error(
15254
+ position,
15255
+ `Cannot resolve reference '${refValue}': token '${token}' does not exist in '${filename}'`
15256
+ );
15257
+ } else {
15258
+ logger.error(
15259
+ { line: 0, column: 0 },
15260
+ `Cannot resolve reference: token '${token}' does not exist in '${filename}'`
15261
+ );
15262
+ }
15263
+ positionMapCache.clear();
15264
+ throw error;
15265
+ }
15266
+ if (error instanceof ResolverError) {
15267
+ const missingFile = error.source;
15268
+ const refInfo = findRefPositionInCache(missingFile);
15269
+ if (refInfo) {
15270
+ const { position, refValue } = refInfo;
15271
+ logger.error(
15272
+ position,
15273
+ `Cannot resolve external reference '${refValue}': file not found '${missingFile}'`
15274
+ );
15275
+ } else {
15276
+ logger.error(
15277
+ { line: 0, column: 0 },
15278
+ `Cannot resolve reference '${missingFile}': file not found`
15279
+ );
15280
+ }
15281
+ positionMapCache.clear();
15282
+ throw error;
15283
+ }
15284
+ positionMapCache.clear();
15032
15285
  const message = error instanceof Error ? error.message : String(error);
15033
15286
  logger.error(
15034
15287
  { line: 0, column: 0 },
@@ -15038,8 +15291,8 @@ async function parseSwaggerDocument(source, logger) {
15038
15291
  }
15039
15292
  }
15040
15293
  async function parseUrl(source, services, fileParserLogger) {
15041
- const { document, positionMap } = await parseSwaggerDocument(source, fileParserLogger);
15042
- const api = new SwaggerAPI(document, source, services, fileParserLogger, positionMap);
15294
+ const { document, positionMap, $refs } = await parseSwaggerDocument(source, fileParserLogger);
15295
+ const api = new SwaggerAPI(document, source, services, fileParserLogger, positionMap, $refs);
15043
15296
  api.validate();
15044
15297
  return api;
15045
15298
  }