@uipath/common 1.203.0-preview.235 → 1.203.0-preview.237

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.
@@ -103,6 +103,13 @@ export declare class SuccessOutput {
103
103
  Data: DataRecord | DataRecord[];
104
104
  Pagination?: Pagination;
105
105
  Instructions?: string;
106
+ /**
107
+ * A note for the caller that does not change the `Data` contract — e.g.
108
+ * why `--output-filter` returned nothing. Declared here because agents
109
+ * read the envelope and nothing else; `emitList` and the filter paths
110
+ * both set it.
111
+ */
112
+ Warning?: string;
106
113
  Log?: string;
107
114
  constructor(code: string, data: DataRecord | DataRecord[]);
108
115
  }
@@ -317,6 +324,9 @@ export declare namespace OutputFormatter {
317
324
  *
318
325
  * **Side-effect:** records the exit code through {@link setExitCode}, so a
319
326
  * printed failure exits non-zero even when no `context.exit()` follows.
327
+ *
328
+ * `--output-filter` projects `Data` here as it does in {@link success};
329
+ * see {@link filterFailureData}.
320
330
  */
321
331
  function error(data: FailureOutput): void;
322
332
  /** Emit a list result. Always uses `Result: "Success"` — an empty list is not an error. */
@@ -333,6 +343,11 @@ export declare namespace OutputFormatter {
333
343
  /**
334
344
  * Format structured output to a string without writing to console.
335
345
  * Useful when the caller needs the formatted string (e.g., Commander's formatHelp).
346
+ *
347
+ * This is the structured-help path, so it carries the filter note on the
348
+ * envelope like {@link success} does: a mistyped filter against `--help`
349
+ * is the same ambiguity as one against a list, and stdout is the only
350
+ * channel its reader has.
336
351
  */
337
352
  function formatToString(data: StructuredOutput): string;
338
353
  }
package/dist/index.js CHANGED
@@ -10922,6 +10922,7 @@ class SuccessOutput {
10922
10922
  Data;
10923
10923
  Pagination;
10924
10924
  Instructions;
10925
+ Warning;
10925
10926
  Log;
10926
10927
  constructor(code, data) {
10927
10928
  this.Code = code;
@@ -11563,7 +11564,165 @@ class ConfigurationError extends Error {
11563
11564
  function isConfigurationError(error) {
11564
11565
  return typeof error === "object" && error !== null && "__brand" in error && error.__brand === "ConfigurationError";
11565
11566
  }
11566
- function applyFilter(data, filter) {
11567
+ function countRecords(data) {
11568
+ if (Array.isArray(data))
11569
+ return data.length;
11570
+ return data == null ? 0 : 1;
11571
+ }
11572
+ function availableFields(data) {
11573
+ const records = Array.isArray(data) ? data : [data];
11574
+ const keys = new Set;
11575
+ for (const record of records) {
11576
+ if (record && typeof record === "object" && !Array.isArray(record)) {
11577
+ for (const key of Object.keys(record))
11578
+ keys.add(key);
11579
+ }
11580
+ }
11581
+ return [...keys];
11582
+ }
11583
+ function decodeQuotedIdentifier(whole, inner) {
11584
+ const [error, decoded] = catchError(() => JSON.parse(whole));
11585
+ if (!error && typeof decoded === "string")
11586
+ return decoded;
11587
+ return inner.replace(/\\(.)/g, "$1");
11588
+ }
11589
+ function referencedFields(filter) {
11590
+ const top = new Set;
11591
+ const nested = new Set;
11592
+ const withoutValueLiterals = filter.replace(/'(?:\\.|[^'\\])*'/g, " ").replace(/`(?:\\.|[^`\\])*`/g, " ");
11593
+ const isPathTail = (text, index) => text[index - 1] === "." && /[A-Za-z0-9_"]/.test(text[index - 2] ?? "");
11594
+ const quoted = [];
11595
+ const withoutLiterals = withoutValueLiterals.replace(/"((?:\\.|[^"\\])*)"/g, (whole, name, index) => {
11596
+ const isAlias = /^\s*:/.test(withoutValueLiterals.slice(index + whole.length));
11597
+ if (!isAlias) {
11598
+ quoted.push({
11599
+ name: decodeQuotedIdentifier(whole, name),
11600
+ index,
11601
+ tail: isPathTail(withoutValueLiterals, index)
11602
+ });
11603
+ }
11604
+ return `"${" ".repeat(whole.length - 2)}"`;
11605
+ });
11606
+ const descent = /[A-Za-z0-9_"]\s*[.[]/.exec(withoutLiterals);
11607
+ const rootEnd = descent ? descent.index + 1 : Number.POSITIVE_INFINITY;
11608
+ const place = (name, index, tail) => {
11609
+ if (tail || index >= rootEnd)
11610
+ nested.add(name);
11611
+ else
11612
+ top.add(name);
11613
+ };
11614
+ for (const entry of quoted)
11615
+ place(entry.name, entry.index, entry.tail);
11616
+ const identifiers = withoutLiterals.matchAll(/([A-Za-z_][A-Za-z0-9_]*)(\s*[:(])?/g);
11617
+ const reserved = new Set(["true", "false", "null", "and", "or", "not"]);
11618
+ for (const match of identifiers) {
11619
+ const [, name, trailing] = match;
11620
+ if (trailing || reserved.has(name))
11621
+ continue;
11622
+ place(name, match.index, isPathTail(withoutLiterals, match.index));
11623
+ }
11624
+ return {
11625
+ top: [...top],
11626
+ nested: [...nested].filter((name) => !top.has(name)),
11627
+ hasPredicate: /\[\s*\?/.test(withoutValueLiterals)
11628
+ };
11629
+ }
11630
+ function appearsBelowTopLevel(data, name) {
11631
+ let budget = 2000;
11632
+ const visit = (value, depth) => {
11633
+ if (budget-- <= 0 || depth > 4)
11634
+ return false;
11635
+ if (value === null || typeof value !== "object")
11636
+ return false;
11637
+ if (Array.isArray(value))
11638
+ return value.some((entry) => visit(entry, depth));
11639
+ const record = value;
11640
+ if (depth > 0 && Object.hasOwn(record, name))
11641
+ return true;
11642
+ return Object.values(record).some((entry) => visit(entry, depth + 1));
11643
+ };
11644
+ return visit(data, 0);
11645
+ }
11646
+ function alwaysNullKeys(records) {
11647
+ const objects = records.filter((r) => r != null && typeof r === "object" && !Array.isArray(r));
11648
+ if (objects.length === 0 || objects.length !== records.length)
11649
+ return [];
11650
+ const keys = [...new Set(objects.flatMap((o) => Object.keys(o)))];
11651
+ if (keys.length === 0)
11652
+ return [];
11653
+ const nullKeys = keys.filter((key) => objects.every((record) => record[key] === null));
11654
+ return nullKeys.length === keys.length ? nullKeys : [];
11655
+ }
11656
+ function carriesOnlyNulls(value) {
11657
+ if (value === null)
11658
+ return true;
11659
+ if (Array.isArray(value)) {
11660
+ return value.length > 0 && value.every(carriesOnlyNulls);
11661
+ }
11662
+ if (typeof value === "object" && value !== undefined) {
11663
+ const values = Object.values(value);
11664
+ return values.length > 0 && values.every(carriesOnlyNulls);
11665
+ }
11666
+ return false;
11667
+ }
11668
+ function nameList(names, cap2 = 12) {
11669
+ const shown = names.slice(0, cap2).map((n) => `\`${n}\``);
11670
+ const rest = names.length - shown.length;
11671
+ return shown.join(", ") + (rest > 0 ? `, and ${rest} more` : "");
11672
+ }
11673
+ function buildFilterWarning(filter, data, outcome) {
11674
+ const present = availableFields(data);
11675
+ const refs = referencedFields(filter);
11676
+ const unknown = refs.top.filter((name) => !present.includes(name) && !appearsBelowTopLevel(data, name));
11677
+ const unchecked = refs.nested.filter((name) => !present.includes(name));
11678
+ const count = countRecords(data);
11679
+ const scope = `${count} record(s)`;
11680
+ const isNotAField = (names) => `${nameList(names)} ${names.length === 1 ? "is not a field" : "are not fields"} on the ${scope} it read`;
11681
+ const soundness = unchecked.length > 0 ? `${nameList(unchecked)} ${unchecked.length === 1 ? "reads" : "read"} below the top level, so ${unchecked.length === 1 ? "it was" : "they were"} not checked against the record keys` : refs.top.length > 0 ? "every field it names exists" : "";
11682
+ const withSoundness = (base) => soundness ? `${base}, and ${soundness}` : base;
11683
+ let detail;
11684
+ let lede;
11685
+ if (outcome.kind === "null-elements") {
11686
+ lede = "returned only null values";
11687
+ const projected = `all ${outcome.count} of the elements it projected are null`;
11688
+ if (unknown.length > 0) {
11689
+ detail = `${projected}, because ${isNotAField(unknown)}`;
11690
+ } else if (unchecked.length > 0) {
11691
+ detail = withSoundness(projected);
11692
+ } else {
11693
+ detail = `${projected}, and every field it names exists — a multi-select list ` + `(\`[A, B]\`) reads each entry against \`Data\` itself, so on a list ` + `result use \`[].[A, B]\` or \`[].{a: A, b: B}\` to read the records`;
11694
+ }
11695
+ } else if (outcome.kind === "null-rows") {
11696
+ lede = "returned only null values";
11697
+ const projected = `every value it projected is null on all ${outcome.count} row(s)`;
11698
+ detail = unknown.length > 0 ? `${projected}, because ${isNotAField(unknown)}` : withSoundness(projected);
11699
+ } else if (outcome.kind === "null-values") {
11700
+ lede = "returned only null values";
11701
+ const projected = `every key it projected (${nameList(outcome.keys)}) is null`;
11702
+ const sameSet = unknown.length === outcome.keys.length && outcome.keys.every((k) => unknown.includes(k));
11703
+ if (sameSet) {
11704
+ detail = `${isNotAField(outcome.keys)}, so every key it projected is null`;
11705
+ } else if (unknown.length > 0) {
11706
+ detail = `${projected}, because ${isNotAField(unknown)}`;
11707
+ } else {
11708
+ detail = withSoundness(`${projected} on all ${scope}`);
11709
+ }
11710
+ } else {
11711
+ lede = "returned nothing";
11712
+ if (unknown.length > 0) {
11713
+ detail = isNotAField(unknown);
11714
+ } else {
11715
+ const selection = refs.hasPredicate ? "no record matched" : "the expression resolved to no value";
11716
+ detail = withSoundness(`${selection} — it read ${scope}`);
11717
+ }
11718
+ }
11719
+ const fields = present.length > 0 ? ` Available fields: ${nameList(present)}.` : "";
11720
+ return `--output-filter ${JSON.stringify(filter)} ${lede}: ${detail}.${fields}`;
11721
+ }
11722
+ function appendWarning(existing, note) {
11723
+ return existing ? `${existing} ${note}` : note;
11724
+ }
11725
+ function applyFilter(data, filter, onWarning) {
11567
11726
  const codec = requireJmespath();
11568
11727
  let result;
11569
11728
  try {
@@ -11571,9 +11730,34 @@ function applyFilter(data, filter) {
11571
11730
  } catch (err) {
11572
11731
  throw new FilterEvaluationError(filter, err);
11573
11732
  }
11574
- if (result == null)
11733
+ const inputCount = countRecords(data);
11734
+ const warn = (outcome) => {
11735
+ if (inputCount === 0)
11736
+ return;
11737
+ const note = buildFilterWarning(filter, data, outcome);
11738
+ getOutputSink().writeErr(`[WARN] ${note}
11739
+ `);
11740
+ onWarning?.(note);
11741
+ };
11742
+ const warnIfAllNull = (records) => {
11743
+ const keys = alwaysNullKeys(records);
11744
+ if (keys.length > 0)
11745
+ warn({ kind: "null-values", keys });
11746
+ };
11747
+ if (result == null) {
11748
+ warn({ kind: "empty" });
11575
11749
  return [];
11750
+ }
11576
11751
  if (Array.isArray(result)) {
11752
+ if (result.length === 0) {
11753
+ warn({ kind: "empty" });
11754
+ } else if (result.every((entry) => entry === null)) {
11755
+ warn({ kind: "null-elements", count: result.length });
11756
+ } else if (result.every((entry) => Array.isArray(entry)) && carriesOnlyNulls(result)) {
11757
+ warn({ kind: "null-rows", count: result.length });
11758
+ } else {
11759
+ warnIfAllNull(result);
11760
+ }
11577
11761
  if (result.length > 0 && typeof result[0] !== "object") {
11578
11762
  const fieldMatch = filter.match(/\.(\w+)$/);
11579
11763
  const key = fieldMatch ? fieldMatch[1] : "Value";
@@ -11581,10 +11765,30 @@ function applyFilter(data, filter) {
11581
11765
  }
11582
11766
  return result;
11583
11767
  }
11584
- if (typeof result === "object")
11768
+ if (typeof result === "object") {
11769
+ warnIfAllNull([result]);
11585
11770
  return result;
11771
+ }
11586
11772
  return { Value: result };
11587
11773
  }
11774
+ function filterFailureData(envelope) {
11775
+ const filter = getOutputFilter();
11776
+ const data = envelope.Data;
11777
+ if (!filter || data == null)
11778
+ return;
11779
+ let note;
11780
+ const [filterError, filtered] = catchError(() => applyFilter(data, filter, (n) => {
11781
+ note = n;
11782
+ }));
11783
+ if (filterError) {
11784
+ envelope.Warning = appendWarning(envelope.Warning, `--output-filter was not applied, so Data is shown in full: ${filterError.message}`);
11785
+ return;
11786
+ }
11787
+ envelope.Data = filtered;
11788
+ if (note) {
11789
+ envelope.Warning = appendWarning(envelope.Warning, note);
11790
+ }
11791
+ }
11588
11792
  function defaultErrorCodeForResult(result) {
11589
11793
  switch (result) {
11590
11794
  case RESULTS.AuthenticationError:
@@ -11667,8 +11871,14 @@ var OutputFormatter;
11667
11871
  }
11668
11872
  const filter = getOutputFilter();
11669
11873
  if (filter) {
11670
- const filtered = applyFilter(data.Data, filter);
11874
+ let note;
11875
+ const filtered = applyFilter(data.Data, filter, (n) => {
11876
+ note = n;
11877
+ });
11671
11878
  data.Data = normalize ? normalizeDataKeys(filtered, verbatim, subtrees && { refs: subtrees.refs, mode: "skip" }) : filtered;
11879
+ if (note) {
11880
+ data.Warning = appendWarning(data.Warning, note);
11881
+ }
11672
11882
  }
11673
11883
  logOutput(normalizeOutputKeys(data), getOutputFormat(), options?.tableRowStyle);
11674
11884
  }
@@ -11696,6 +11906,7 @@ var OutputFormatter;
11696
11906
  delete envelope.TelemetryErrorClass;
11697
11907
  delete envelope.TelemetryTerminalOutcome;
11698
11908
  delete envelope.TelemetryTerminalSignal;
11909
+ filterFailureData(envelope);
11699
11910
  if (!suppressTelemetry) {
11700
11911
  telemetry.trackEvent(CommonTelemetryEvents.Error, {
11701
11912
  result: data.Result,
@@ -11744,13 +11955,20 @@ var OutputFormatter;
11744
11955
  OutputFormatter.log = log;
11745
11956
  function formatToString(data) {
11746
11957
  const filter = getOutputFilter();
11958
+ const envelope = data;
11747
11959
  if ("Data" in data && data.Data != null) {
11748
11960
  data.Data = normalizeDataKeys(data.Data);
11749
11961
  if (filter) {
11750
- data.Data = normalizeDataKeys(applyFilter(data.Data, filter));
11962
+ let note;
11963
+ data.Data = normalizeDataKeys(applyFilter(data.Data, filter, (n) => {
11964
+ note = n;
11965
+ }));
11966
+ if (note) {
11967
+ envelope.Warning = appendWarning(envelope.Warning, note);
11968
+ }
11751
11969
  }
11752
11970
  }
11753
- const output = normalizeOutputKeys(data);
11971
+ const output = normalizeOutputKeys(envelope);
11754
11972
  const lines = [];
11755
11973
  const sink = getOutputSink();
11756
11974
  printOutput(output, getOutputFormat(), (msg) => {
@@ -14032,4 +14250,4 @@ export {
14032
14250
  writeTelemetrySpoolFile
14033
14251
  };
14034
14252
 
14035
- //# debugId=1C8485A93560E50864756E2164756E21
14253
+ //# debugId=C7396FAC1D7B6F8064756E2164756E21
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@uipath/common",
3
3
  "author": "UiPath",
4
4
  "license": "SEE LICENSE IN LICENSE.txt",
5
- "version": "1.203.0-preview.235",
5
+ "version": "1.203.0-preview.237",
6
6
  "description": "Common infrastructure needed by uip tools.",
7
7
  "repository": {
8
8
  "type": "git",
@@ -80,5 +80,5 @@
80
80
  "mihaigirleanu",
81
81
  "vlad-uipath"
82
82
  ],
83
- "gitHead": "4788b04c17a4429e5a994af2c3eba3898d1c4b13"
83
+ "gitHead": "4ad3e17a66f2c7fb7fe1f0e3e04a5b6175a0a9a3"
84
84
  }