@cqa-lib/cqa-ui 1.1.482 → 1.1.483

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.
@@ -14174,18 +14174,56 @@ class ApiStepComponent extends BaseStepComponent {
14174
14174
  }
14175
14175
  }
14176
14176
  /**
14177
- * Extracts JSON path from the clicked position in a formatted JSON string
14177
+ * Walks jsonData and builds a map from each line number (in the output of
14178
+ * JSON.stringify(jsonData, null, 2)) to the segment path that line represents.
14179
+ * Segments are strings for object keys and numbers for array indices.
14180
+ */
14181
+ buildLinePathMap(obj) {
14182
+ const map = new Map();
14183
+ const state = { line: 0 };
14184
+ const walk = (value, path) => {
14185
+ if (Array.isArray(value)) {
14186
+ if (value.length === 0)
14187
+ return;
14188
+ for (let i = 0; i < value.length; i++) {
14189
+ state.line += 1; // newline before element
14190
+ const elementPath = [...path, i];
14191
+ map.set(state.line, elementPath);
14192
+ walk(value[i], elementPath);
14193
+ }
14194
+ state.line += 1; // newline before closing ]
14195
+ return;
14196
+ }
14197
+ if (value && typeof value === 'object') {
14198
+ const keys = Object.keys(value);
14199
+ if (keys.length === 0)
14200
+ return;
14201
+ for (const key of keys) {
14202
+ state.line += 1; // newline before "key":
14203
+ const keyPath = [...path, key];
14204
+ map.set(state.line, keyPath);
14205
+ walk(value[key], keyPath);
14206
+ }
14207
+ state.line += 1; // newline before closing }
14208
+ return;
14209
+ }
14210
+ // primitive — no additional lines
14211
+ };
14212
+ // Root line (line 0) is the opening { or [ or primitive.
14213
+ walk(obj, []);
14214
+ return map;
14215
+ }
14216
+ /**
14217
+ * Extracts JSON path from the clicked position in a formatted JSON string.
14218
+ * Returns an array of segments (string keys or numeric array indices), or null.
14178
14219
  */
14179
14220
  getJsonPathFromClick(event, jsonData, prefix = '') {
14180
14221
  const target = event.target;
14181
- if (!target || !jsonData)
14222
+ if (!target || jsonData == null)
14182
14223
  return null;
14183
14224
  const preElement = target.closest('pre');
14184
14225
  if (!preElement)
14185
14226
  return null;
14186
- // Get the formatted JSON text
14187
- const formattedJson = this.formatJson(jsonData);
14188
- const lines = formattedJson.split('\n');
14189
14227
  // Create a range at the click position
14190
14228
  let range = null;
14191
14229
  if (document.caretRangeFromPoint) {
@@ -14208,58 +14246,44 @@ class ApiStepComponent extends BaseStepComponent {
14208
14246
  const textBeforeClick = preRange.toString();
14209
14247
  // Count newlines to find which line we're on
14210
14248
  const lineIndex = (textBeforeClick.match(/\n/g) || []).length;
14211
- // Get the clicked line
14212
- const clickedLine = lines[lineIndex] || '';
14213
- // Extract key from the line (pattern: "key":)
14214
- const keyMatch = clickedLine.match(/^\s*"([^"]+)":/);
14215
- if (!keyMatch)
14216
- return null;
14217
- const clickedKey = keyMatch[1];
14218
- const clickedIndent = (keyMatch[0].match(/^(\s+)/)?.[1]?.length || 0) / 2; // Divide by 2 since JSON.stringify uses 2 spaces
14219
- // Build path by finding parent keys based on indentation
14220
- const path = [];
14221
- // Traverse backwards from the clicked line to find all parent keys
14222
- let currentIndent = clickedIndent;
14223
- for (let i = lineIndex - 1; i >= 0; i--) {
14224
- const line = lines[i];
14225
- const lineKeyMatch = line.match(/^(\s+)"([^"]+)":/);
14226
- if (lineKeyMatch) {
14227
- const lineIndent = lineKeyMatch[1].length / 2; // Divide by 2 for 2-space indentation
14228
- const lineKey = lineKeyMatch[2];
14229
- // If this line has less indentation, it's a parent
14230
- if (lineIndent < currentIndent) {
14231
- path.unshift(lineKey);
14232
- currentIndent = lineIndent;
14233
- // Stop if we've reached the root level
14234
- if (lineIndent === 0)
14235
- break;
14236
- }
14237
- }
14238
- }
14239
- // Add the clicked key
14240
- path.push(clickedKey);
14241
- // Build the full path string
14242
- if (path.length === 0)
14249
+ const linePathMap = this.buildLinePathMap(jsonData);
14250
+ // Exact match first; otherwise fall back to the nearest preceding mapped line
14251
+ // (handles clicks on closing brackets or blank columns).
14252
+ let segments = linePathMap.get(lineIndex);
14253
+ if (!segments) {
14254
+ for (let i = lineIndex - 1; i >= 0; i--) {
14255
+ const candidate = linePathMap.get(i);
14256
+ if (candidate) {
14257
+ segments = candidate;
14258
+ break;
14259
+ }
14260
+ }
14261
+ }
14262
+ if (!segments || segments.length === 0)
14243
14263
  return null;
14244
- const fullPath = prefix ? `${prefix}.${path.join('.')}` : path.join('.');
14245
- return fullPath;
14264
+ return prefix ? [prefix, ...segments] : [...segments];
14246
14265
  }
14247
- normalizeJsonPath(path, prefix) {
14248
- if (!path || !prefix) {
14249
- return path;
14250
- }
14251
- const parts = path.split('.');
14252
- if (parts.length < 2) {
14253
- return path;
14266
+ formatSegmentsPath(segments) {
14267
+ return segments.reduce((acc, seg) => {
14268
+ if (typeof seg === 'number')
14269
+ return `${acc}[${seg}]`;
14270
+ return acc ? `${acc}.${seg}` : String(seg);
14271
+ }, '');
14272
+ }
14273
+ normalizeJsonPath(segments, prefix) {
14274
+ if (!segments || segments.length === 0)
14275
+ return '';
14276
+ if (!prefix || segments.length < 2) {
14277
+ return this.formatSegmentsPath(segments);
14254
14278
  }
14255
- const [, ...rest] = parts;
14279
+ const [, ...rest] = segments;
14256
14280
  if (prefix === 'requestHeaders' || prefix === 'responseHeaders') {
14257
- return rest.length ? `headers.${rest.join('.')}` : 'headers';
14281
+ return rest.length ? `headers${this.formatSegmentsPath(rest).startsWith('[') ? '' : '.'}${this.formatSegmentsPath(rest)}` : 'headers';
14258
14282
  }
14259
14283
  if (prefix === 'requestBody' || prefix === 'responseBody') {
14260
- return rest.length ? `body.${rest.join('.')}` : 'body';
14284
+ return rest.length ? `body${this.formatSegmentsPath(rest).startsWith('[') ? '' : '.'}${this.formatSegmentsPath(rest)}` : 'body';
14261
14285
  }
14262
- return path;
14286
+ return this.formatSegmentsPath(segments);
14263
14287
  }
14264
14288
  /**
14265
14289
  * Copy JSON path on double-click
@@ -14267,12 +14291,12 @@ class ApiStepComponent extends BaseStepComponent {
14267
14291
  copyJsonPath(event, jsonData, prefix = '') {
14268
14292
  if (!jsonData)
14269
14293
  return;
14270
- const path = this.getJsonPathFromClick(event, jsonData, prefix);
14271
- if (path) {
14294
+ const segments = this.getJsonPathFromClick(event, jsonData, prefix);
14295
+ if (segments && segments.length > 0) {
14272
14296
  const source = prefix;
14273
- const normalizedPath = this.normalizeJsonPath(path, prefix);
14297
+ const normalizedPath = this.normalizeJsonPath(segments, prefix);
14274
14298
  navigator.clipboard.writeText(normalizedPath).then(() => {
14275
- console.log('Copied to clipboard:', path);
14299
+ console.log('Copied to clipboard:', normalizedPath);
14276
14300
  // Call the handler on every double-click (even on success, for now showing error as per requirement)
14277
14301
  if (this.onJsonPathCopiedHandler) {
14278
14302
  console.log('Calling onJsonPathCopiedHandler on success with:', { path: normalizedPath, source });