bwx 0.8.1 → 0.10.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/src/cli/items.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import pc from "picocolors";
2
2
  import { summarizeItem, type ItemSummary } from "../bw/items.ts";
3
+ import { discoverItem, type ItemDiscovery } from "../bw/discovery.ts";
3
4
  import { BW_TYPE_FROM_NAME } from "../bw/types.ts";
4
5
  import { CliError, ExitCode } from "./errors.ts";
5
6
  import { emitData, emitWarn } from "./io.ts";
@@ -18,6 +19,8 @@ export interface ItemListOptions {
18
19
  limit?: number;
19
20
  /** Opt in to raw bw items, which carry passwords, notes, and custom fields. */
20
21
  fullItems?: boolean;
22
+ /** Metadata and consumable references only; never field values. */
23
+ fields?: boolean;
21
24
  }
22
25
 
23
26
  export interface FilteredItems {
@@ -67,6 +70,7 @@ export function filterItems(
67
70
  * the same reduced projection, so asking for machine-readable output is a
68
71
  * serialization choice and never widens data access. `--full-items` is the
69
72
  * explicit opt-in for callers that genuinely need the complete objects.
73
+ * `--fields` uses a separate allowlist for capabilities and consumable refs.
70
74
  *
71
75
  * A truncated listing reports what it dropped through every channel — a stderr
72
76
  * warning that survives `--json`, plus `meta` in the JSON envelope — because a
@@ -79,6 +83,8 @@ export function writeItems(
79
83
  ): void {
80
84
  const { items, total } = filtered;
81
85
  const omitted = total - items.length;
86
+ // Project the whole response before emitting anything, including warnings.
87
+ const discovery = options.fields ? items.map(discoverItem) : undefined;
82
88
 
83
89
  if (omitted > 0) {
84
90
  emitWarn(
@@ -88,13 +94,20 @@ export function writeItems(
88
94
  }
89
95
 
90
96
  if (opts.json) {
97
+ let data: unknown = items;
98
+ if (discovery) data = discovery;
99
+ else if (!options.fullItems) data = items.map(summarizeItem);
91
100
  emitData(
92
- options.fullItems ? items : items.map(summarizeItem),
101
+ data,
93
102
  opts,
94
103
  { total, shown: items.length, truncated: omitted > 0 },
95
104
  );
96
105
  return;
97
106
  }
107
+ if (discovery) {
108
+ writeLines(discovery, opts.plain ? plainDiscovery : humanDiscovery);
109
+ return;
110
+ }
98
111
 
99
112
  if (options.fullItems) {
100
113
  // Plain output stays line-oriented: one compact item per line (NDJSON).
@@ -106,6 +119,18 @@ export function writeItems(
106
119
  writeLines(items.map(summarizeItem), opts.plain ? plainLine : humanLine);
107
120
  }
108
121
 
122
+ function plainDiscovery(item: ItemDiscovery): string {
123
+ return `${item.id}\t${item.type}\t${JSON.stringify(item.name)}\t${JSON.stringify(item.fields)}`;
124
+ }
125
+
126
+ function humanDiscovery(item: ItemDiscovery): string {
127
+ const header = `${pc.dim(item.id)} ${pc.cyan(item.type)} ${JSON.stringify(item.name)}`;
128
+ const fields = item.fields.map((field) =>
129
+ ` ${field.kind} ${JSON.stringify(field.name)} [${field.type}] ${field.ref}`,
130
+ );
131
+ return [header, ...fields].join("\n");
132
+ }
133
+
109
134
  function plainLine(summary: ItemSummary): string {
110
135
  const totp = summary.hasTotp ? "totp" : "-";
111
136
  return `${summary.id}\t${summary.type}\t${summary.name}\t${summary.username ?? "-"}\t${totp}`;