@uipath/identity-commands 1.202.1-preview.220 → 1.202.1-preview.239

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.
Files changed (2) hide show
  1. package/dist/index.js +224 -7
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -4745,7 +4745,165 @@ class FilterImplicitLimitError extends Error {
4745
4745
  this.instructions = "Pass --limit <n> to choose how many records the filter applies to. " + "To filter over all records, pass the command's maximum accepted --limit (see the option's description in --help).";
4746
4746
  }
4747
4747
  }
4748
- function applyFilter(data, filter) {
4748
+ function countRecords(data) {
4749
+ if (Array.isArray(data))
4750
+ return data.length;
4751
+ return data == null ? 0 : 1;
4752
+ }
4753
+ function availableFields(data) {
4754
+ const records = Array.isArray(data) ? data : [data];
4755
+ const keys = new Set;
4756
+ for (const record of records) {
4757
+ if (record && typeof record === "object" && !Array.isArray(record)) {
4758
+ for (const key of Object.keys(record))
4759
+ keys.add(key);
4760
+ }
4761
+ }
4762
+ return [...keys];
4763
+ }
4764
+ function decodeQuotedIdentifier(whole, inner) {
4765
+ const [error, decoded] = catchError(() => JSON.parse(whole));
4766
+ if (!error && typeof decoded === "string")
4767
+ return decoded;
4768
+ return inner.replace(/\\(.)/g, "$1");
4769
+ }
4770
+ function referencedFields(filter) {
4771
+ const top = new Set;
4772
+ const nested = new Set;
4773
+ const withoutValueLiterals = filter.replace(/'(?:\\.|[^'\\])*'/g, " ").replace(/`(?:\\.|[^`\\])*`/g, " ");
4774
+ const isPathTail = (text, index) => text[index - 1] === "." && /[A-Za-z0-9_"]/.test(text[index - 2] ?? "");
4775
+ const quoted = [];
4776
+ const withoutLiterals = withoutValueLiterals.replace(/"((?:\\.|[^"\\])*)"/g, (whole, name, index) => {
4777
+ const isAlias = /^\s*:/.test(withoutValueLiterals.slice(index + whole.length));
4778
+ if (!isAlias) {
4779
+ quoted.push({
4780
+ name: decodeQuotedIdentifier(whole, name),
4781
+ index,
4782
+ tail: isPathTail(withoutValueLiterals, index)
4783
+ });
4784
+ }
4785
+ return `"${" ".repeat(whole.length - 2)}"`;
4786
+ });
4787
+ const descent = /[A-Za-z0-9_"]\s*[.[]/.exec(withoutLiterals);
4788
+ const rootEnd = descent ? descent.index + 1 : Number.POSITIVE_INFINITY;
4789
+ const place = (name, index, tail) => {
4790
+ if (tail || index >= rootEnd)
4791
+ nested.add(name);
4792
+ else
4793
+ top.add(name);
4794
+ };
4795
+ for (const entry of quoted)
4796
+ place(entry.name, entry.index, entry.tail);
4797
+ const identifiers = withoutLiterals.matchAll(/([A-Za-z_][A-Za-z0-9_]*)(\s*[:(])?/g);
4798
+ const reserved = new Set(["true", "false", "null", "and", "or", "not"]);
4799
+ for (const match of identifiers) {
4800
+ const [, name, trailing] = match;
4801
+ if (trailing || reserved.has(name))
4802
+ continue;
4803
+ place(name, match.index, isPathTail(withoutLiterals, match.index));
4804
+ }
4805
+ return {
4806
+ top: [...top],
4807
+ nested: [...nested].filter((name) => !top.has(name)),
4808
+ hasPredicate: /\[\s*\?/.test(withoutValueLiterals)
4809
+ };
4810
+ }
4811
+ function appearsBelowTopLevel(data, name) {
4812
+ let budget = 2000;
4813
+ const visit = (value, depth) => {
4814
+ if (budget-- <= 0 || depth > 4)
4815
+ return false;
4816
+ if (value === null || typeof value !== "object")
4817
+ return false;
4818
+ if (Array.isArray(value))
4819
+ return value.some((entry) => visit(entry, depth));
4820
+ const record = value;
4821
+ if (depth > 0 && Object.hasOwn(record, name))
4822
+ return true;
4823
+ return Object.values(record).some((entry) => visit(entry, depth + 1));
4824
+ };
4825
+ return visit(data, 0);
4826
+ }
4827
+ function alwaysNullKeys(records) {
4828
+ const objects = records.filter((r) => r != null && typeof r === "object" && !Array.isArray(r));
4829
+ if (objects.length === 0 || objects.length !== records.length)
4830
+ return [];
4831
+ const keys = [...new Set(objects.flatMap((o) => Object.keys(o)))];
4832
+ if (keys.length === 0)
4833
+ return [];
4834
+ const nullKeys = keys.filter((key) => objects.every((record) => record[key] === null));
4835
+ return nullKeys.length === keys.length ? nullKeys : [];
4836
+ }
4837
+ function carriesOnlyNulls(value) {
4838
+ if (value === null)
4839
+ return true;
4840
+ if (Array.isArray(value)) {
4841
+ return value.length > 0 && value.every(carriesOnlyNulls);
4842
+ }
4843
+ if (typeof value === "object" && value !== undefined) {
4844
+ const values = Object.values(value);
4845
+ return values.length > 0 && values.every(carriesOnlyNulls);
4846
+ }
4847
+ return false;
4848
+ }
4849
+ function nameList(names, cap2 = 12) {
4850
+ const shown = names.slice(0, cap2).map((n) => `\`${n}\``);
4851
+ const rest = names.length - shown.length;
4852
+ return shown.join(", ") + (rest > 0 ? `, and ${rest} more` : "");
4853
+ }
4854
+ function buildFilterWarning(filter, data, outcome) {
4855
+ const present = availableFields(data);
4856
+ const refs = referencedFields(filter);
4857
+ const unknown = refs.top.filter((name) => !present.includes(name) && !appearsBelowTopLevel(data, name));
4858
+ const unchecked = refs.nested.filter((name) => !present.includes(name));
4859
+ const count = countRecords(data);
4860
+ const scope = `${count} record(s)`;
4861
+ const isNotAField = (names) => `${nameList(names)} ${names.length === 1 ? "is not a field" : "are not fields"} on the ${scope} it read`;
4862
+ 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" : "";
4863
+ const withSoundness = (base) => soundness ? `${base}, and ${soundness}` : base;
4864
+ let detail;
4865
+ let lede;
4866
+ if (outcome.kind === "null-elements") {
4867
+ lede = "returned only null values";
4868
+ const projected = `all ${outcome.count} of the elements it projected are null`;
4869
+ if (unknown.length > 0) {
4870
+ detail = `${projected}, because ${isNotAField(unknown)}`;
4871
+ } else if (unchecked.length > 0) {
4872
+ detail = withSoundness(projected);
4873
+ } else {
4874
+ 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`;
4875
+ }
4876
+ } else if (outcome.kind === "null-rows") {
4877
+ lede = "returned only null values";
4878
+ const projected = `every value it projected is null on all ${outcome.count} row(s)`;
4879
+ detail = unknown.length > 0 ? `${projected}, because ${isNotAField(unknown)}` : withSoundness(projected);
4880
+ } else if (outcome.kind === "null-values") {
4881
+ lede = "returned only null values";
4882
+ const projected = `every key it projected (${nameList(outcome.keys)}) is null`;
4883
+ const sameSet = unknown.length === outcome.keys.length && outcome.keys.every((k) => unknown.includes(k));
4884
+ if (sameSet) {
4885
+ detail = `${isNotAField(outcome.keys)}, so every key it projected is null`;
4886
+ } else if (unknown.length > 0) {
4887
+ detail = `${projected}, because ${isNotAField(unknown)}`;
4888
+ } else {
4889
+ detail = withSoundness(`${projected} on all ${scope}`);
4890
+ }
4891
+ } else {
4892
+ lede = "returned nothing";
4893
+ if (unknown.length > 0) {
4894
+ detail = isNotAField(unknown);
4895
+ } else {
4896
+ const selection = refs.hasPredicate ? "no record matched" : "the expression resolved to no value";
4897
+ detail = withSoundness(`${selection} — it read ${scope}`);
4898
+ }
4899
+ }
4900
+ const fields = present.length > 0 ? ` Available fields: ${nameList(present)}.` : "";
4901
+ return `--output-filter ${JSON.stringify(filter)} ${lede}: ${detail}.${fields}`;
4902
+ }
4903
+ function appendWarning(existing, note) {
4904
+ return existing ? `${existing} ${note}` : note;
4905
+ }
4906
+ function applyFilter(data, filter, onWarning) {
4749
4907
  const codec = requireJmespath();
4750
4908
  let result;
4751
4909
  try {
@@ -4753,9 +4911,34 @@ function applyFilter(data, filter) {
4753
4911
  } catch (err) {
4754
4912
  throw new FilterEvaluationError(filter, err);
4755
4913
  }
4756
- if (result == null)
4914
+ const inputCount = countRecords(data);
4915
+ const warn = (outcome) => {
4916
+ if (inputCount === 0)
4917
+ return;
4918
+ const note = buildFilterWarning(filter, data, outcome);
4919
+ getOutputSink().writeErr(`[WARN] ${note}
4920
+ `);
4921
+ onWarning?.(note);
4922
+ };
4923
+ const warnIfAllNull = (records) => {
4924
+ const keys = alwaysNullKeys(records);
4925
+ if (keys.length > 0)
4926
+ warn({ kind: "null-values", keys });
4927
+ };
4928
+ if (result == null) {
4929
+ warn({ kind: "empty" });
4757
4930
  return [];
4931
+ }
4758
4932
  if (Array.isArray(result)) {
4933
+ if (result.length === 0) {
4934
+ warn({ kind: "empty" });
4935
+ } else if (result.every((entry) => entry === null)) {
4936
+ warn({ kind: "null-elements", count: result.length });
4937
+ } else if (result.every((entry) => Array.isArray(entry)) && carriesOnlyNulls(result)) {
4938
+ warn({ kind: "null-rows", count: result.length });
4939
+ } else {
4940
+ warnIfAllNull(result);
4941
+ }
4759
4942
  if (result.length > 0 && typeof result[0] !== "object") {
4760
4943
  const fieldMatch = filter.match(/\.(\w+)$/);
4761
4944
  const key = fieldMatch ? fieldMatch[1] : "Value";
@@ -4763,10 +4946,30 @@ function applyFilter(data, filter) {
4763
4946
  }
4764
4947
  return result;
4765
4948
  }
4766
- if (typeof result === "object")
4949
+ if (typeof result === "object") {
4950
+ warnIfAllNull([result]);
4767
4951
  return result;
4952
+ }
4768
4953
  return { Value: result };
4769
4954
  }
4955
+ function filterFailureData(envelope) {
4956
+ const filter = getOutputFilter();
4957
+ const data = envelope.Data;
4958
+ if (!filter || data == null)
4959
+ return;
4960
+ let note;
4961
+ const [filterError, filtered] = catchError(() => applyFilter(data, filter, (n) => {
4962
+ note = n;
4963
+ }));
4964
+ if (filterError) {
4965
+ envelope.Warning = appendWarning(envelope.Warning, `--output-filter was not applied, so Data is shown in full: ${filterError.message}`);
4966
+ return;
4967
+ }
4968
+ envelope.Data = filtered;
4969
+ if (note) {
4970
+ envelope.Warning = appendWarning(envelope.Warning, note);
4971
+ }
4972
+ }
4770
4973
  function defaultErrorCodeForResult(result) {
4771
4974
  switch (result) {
4772
4975
  case RESULTS.AuthenticationError:
@@ -4849,8 +5052,14 @@ var OutputFormatter;
4849
5052
  }
4850
5053
  const filter = getOutputFilter();
4851
5054
  if (filter) {
4852
- const filtered = applyFilter(data.Data, filter);
5055
+ let note;
5056
+ const filtered = applyFilter(data.Data, filter, (n) => {
5057
+ note = n;
5058
+ });
4853
5059
  data.Data = normalize ? normalizeDataKeys(filtered, verbatim, subtrees && { refs: subtrees.refs, mode: "skip" }) : filtered;
5060
+ if (note) {
5061
+ data.Warning = appendWarning(data.Warning, note);
5062
+ }
4854
5063
  }
4855
5064
  logOutput(normalizeOutputKeys(data), getOutputFormat(), options?.tableRowStyle);
4856
5065
  }
@@ -4878,6 +5087,7 @@ var OutputFormatter;
4878
5087
  delete envelope.TelemetryErrorClass;
4879
5088
  delete envelope.TelemetryTerminalOutcome;
4880
5089
  delete envelope.TelemetryTerminalSignal;
5090
+ filterFailureData(envelope);
4881
5091
  if (!suppressTelemetry) {
4882
5092
  telemetry.trackEvent(CommonTelemetryEvents.Error, {
4883
5093
  result: data.Result,
@@ -4926,13 +5136,20 @@ var OutputFormatter;
4926
5136
  OutputFormatter.log = log;
4927
5137
  function formatToString(data) {
4928
5138
  const filter = getOutputFilter();
5139
+ const envelope = data;
4929
5140
  if ("Data" in data && data.Data != null) {
4930
5141
  data.Data = normalizeDataKeys(data.Data);
4931
5142
  if (filter) {
4932
- data.Data = normalizeDataKeys(applyFilter(data.Data, filter));
5143
+ let note;
5144
+ data.Data = normalizeDataKeys(applyFilter(data.Data, filter, (n) => {
5145
+ note = n;
5146
+ }));
5147
+ if (note) {
5148
+ envelope.Warning = appendWarning(envelope.Warning, note);
5149
+ }
4933
5150
  }
4934
5151
  }
4935
- const output = normalizeOutputKeys(data);
5152
+ const output = normalizeOutputKeys(envelope);
4936
5153
  const lines = [];
4937
5154
  const sink = getOutputSink();
4938
5155
  printOutput(output, getOutputFormat(), (msg) => {
@@ -12267,4 +12484,4 @@ export {
12267
12484
  registerCommands
12268
12485
  };
12269
12486
 
12270
- //# debugId=ABCBEDAD130ACE1F64756E2164756E21
12487
+ //# debugId=50DAB92527A489D464756E2164756E21
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@uipath/identity-commands",
3
3
  "license": "SEE LICENSE IN LICENSE.txt",
4
- "version": "1.202.1-preview.220",
4
+ "version": "1.202.1-preview.239",
5
5
  "description": "Admin CLI commands for Identity Server users, groups, robot accounts, and external apps. Library composed by admin-tool, not a standalone CLI tool.",
6
6
  "private": false,
7
7
  "repository": {
@@ -25,5 +25,5 @@
25
25
  "files": [
26
26
  "dist"
27
27
  ],
28
- "gitHead": "a09041733b628c3f6b658a1f58349ea0f40bb492"
28
+ "gitHead": "97545a7857bdff7e6db62bbe13c6097607cf38f1"
29
29
  }