@jacebenson/jsn 4.3.13-dev.84.1 → 4.3.13-dev.86.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jacebenson/jsn",
3
- "version": "4.3.13-dev.84.1",
3
+ "version": "4.3.13-dev.86.1",
4
4
  "description": "A command-line interface for ServiceNow that follows the Unix philosophy: simple, composable, and scriptable.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,3 +1,4 @@
1
+ import { enrichRelatedLists, relatedListOptions } from '../related-lists.js';
1
2
  // Generic table-based CRUD command builder
2
3
 
3
4
  import { formatRecordForDisplay, getStringField, buildRecordPayload, confirmDelete, interactiveList, canPrompt } from '../helpers.js';
@@ -182,7 +183,7 @@ export function buildDevCmd(name, table, aliases, defaultColumns, wrap, opts = {
182
183
  aliases: ['get'],
183
184
  describe: `Show ${vowelArticle(singular)} ${singular} by name or sys_id`,
184
185
  builder: y => {
185
- const command = recordOptions(y);
186
+ const command = relatedListOptions(recordOptions(y));
186
187
  return opts.relationships?.essential?.length ? command.option('essential', { type: 'boolean', default: true,
187
188
  describe: 'Fetch profile-declared essential children; --no-essential skips them. Supplementary lists remain lazy.' }) : command;
188
189
  },
@@ -204,6 +205,7 @@ export function buildDevCmd(name, table, aliases, defaultColumns, wrap, opts = {
204
205
  await opts.onShow(record, app, argv);
205
206
  }
206
207
 
208
+ await enrichRelatedLists(record, { table, argv, app });
207
209
  const summary = typeof showSummary === 'function' ? showSummary(record, id) : showSummary;
208
210
  const breadcrumbs = typeof showBreadcrumbs === 'function' ? showBreadcrumbs(record, id) : showBreadcrumbs;
209
211
 
@@ -1,3 +1,4 @@
1
+ import { enrichRelatedLists, relatedListOptions } from '../related-lists.js';
1
2
  // Generic command builder for CRUD operations on a ServiceNow table
2
3
  // Used by incidents, changes, requests, tasks, and most dev subcommands
3
4
 
@@ -127,13 +128,14 @@ export function buildTicketCommands(table, displayName, alias, defaultColumns, s
127
128
  aliases: ['get'],
128
129
  describe: `Show a specific ${displayName}`,
129
130
  builder: y => {
130
- const command = recordOptions(y);
131
+ const command = relatedListOptions(recordOptions(y));
131
132
  return opts.relationships?.essential?.length ? command.option('essential', { type: 'boolean', default: true, describe: 'Fetch declared essential children; use --no-essential to skip' }) : command;
132
133
  },
133
134
  handler: wrap(async (argv, app) => {
134
135
  const number = argv.number;
135
136
  const record = await getRecord(app.sdk, { table, identifier: number, identifierField: argv.identifierField, displayValues: argv.displayValues ?? opts.referenceMode, matchField: opts.identifierField || 'number', resource: displayName });
136
137
  if (opts.onShow) await opts.onShow(record, app, argv);
138
+ await enrichRelatedLists(record, { table, argv, app });
137
139
  record._context = {
138
140
  instance_url: app.getEffectiveInstance(),
139
141
  table,
@@ -1,3 +1,4 @@
1
+ import { enrichRelatedLists, relatedListOptions } from '../related-lists.js';
1
2
  import { getRecord, recordOptions } from '../records-engine.js';
2
3
  import { unwrapSysId } from '../resolve-record.js';
3
4
  import { buildProfileCommand } from '../record-profiles.js';
@@ -16,7 +17,7 @@ export function requestsCmd(wrap) {
16
17
  command: 'show <number>',
17
18
  aliases: ['get'],
18
19
  describe: 'Show a specific request with attachments and catalog variables',
19
- builder: y => recordOptions(y),
20
+ builder: y => relatedListOptions(recordOptions(y)),
20
21
  handler: wrap(enrichedShowHandler),
21
22
  });
22
23
  };
@@ -42,6 +43,7 @@ async function enrichedShowHandler(argv, app) {
42
43
  table: 'sc_req_item',
43
44
  };
44
45
 
46
+ await enrichRelatedLists(record, { table: 'sc_req_item', argv, app });
45
47
  app.ok(record, {
46
48
  summary: `Request ${number}`,
47
49
  breadcrumbs: [
@@ -1,3 +1,4 @@
1
+ import { enrichRelatedLists, relatedListOptions } from '../related-lists.js';
1
2
  import { formatRecordForDisplay, getStringField, buildQuerySuffix, resolveFieldsParam, buildRecordPayload, confirmDelete } from '../helpers.js';
2
3
  import { getRecord, listRecords, recordListParams, createRecord, updateRecord, deleteRecord, recordOptions } from '../records-engine.js';
3
4
  import { resolveSysId } from '../resolve-record.js';
@@ -59,9 +60,10 @@ export function ticketsCmd(wrap) {
59
60
  command: 'show <number>',
60
61
  aliases: ['get'],
61
62
  describe: 'Show a ticket',
62
- builder: y => recordOptions(y),
63
+ builder: y => relatedListOptions(recordOptions(y)),
63
64
  handler: wrap(async (argv, app) => {
64
65
  const record = await getRecord(app.sdk, { table: 'task', identifier: argv.number, identifierField: argv.identifierField, displayValues: argv.displayValues, matchField: 'number', resource: 'Ticket' });
66
+ await enrichRelatedLists(record, { table: 'task', argv, app });
65
67
  app.ok(record, { summary: `Ticket ${argv.number}` });
66
68
  }),
67
69
  })
@@ -237,7 +237,11 @@ function buildRelationships({ entries, dictionary, relationships, sourceTable, n
237
237
  const existing = result.get(id);
238
238
  const descriptor = existing || { id, kind: 'unresolved', table: '', reference_field: '', query_status: 'unavailable',
239
239
  reason: 'Configured related list metadata is absent, unsupported, or not visible' };
240
- result.set(id, { ...descriptor, configured: true, source_table: sourceTable, position: position(entry),
240
+ const filter = entry.filter;
241
+ const filterAvailable = Boolean(Object.hasOwn(entry, 'filter') && (filter === null || typeof filter === 'string'
242
+ || (filter && typeof filter === 'object' && Object.hasOwn(filter, 'value') && (filter.value === null || typeof filter.value === 'string'))));
243
+ if (!filterAvailable) warnings.push({ source: 'sys_ui_related_list_entry', reason: 'A configured filter is absent or not visible; do not assume an unfiltered list' });
244
+ result.set(id, { ...descriptor, configured: true, filter_available: filterAvailable, source_table: sourceTable, position: position(entry),
241
245
  filter: field(entry, 'filter'), order_by: field(entry, 'order_by') });
242
246
  if (!existing) warnings.push({ source: 'sys_ui_related_list_entry', reason: descriptor.reason });
243
247
  }
package/src/output.js CHANGED
@@ -529,6 +529,17 @@ export class OutputWriter {
529
529
  }
530
530
  }
531
531
 
532
+ if (data._related && typeof data._related === 'object') {
533
+ this.writer.write('─ Related lists ─\n');
534
+ for (const list of data._related.nonempty || []) {
535
+ this.writer.write(` ${list.label}: ${list.count}\n`);
536
+ if (list.command) this.writer.write(` Retrieve: ${list.command}\n`);
537
+ }
538
+ this.writer.write(` Coverage: ${data._related.coverage?.state || 'unknown'}; counts reflect aggregate API access and visible metadata.\n`);
539
+ for (const warning of data._related.coverage?.warnings || []) this.writer.write(` Unknown (${warning.source}): ${warning.reason}\n`);
540
+ this.writer.write('\n');
541
+ }
542
+
532
543
  // ── Attachments ──
533
544
  if (data._attachments && Array.isArray(data._attachments) && data._attachments.length > 0) {
534
545
  this.writer.write('─ Attachments ─\n');
@@ -145,3 +145,9 @@ export async function enrichRelatedLists(record, { table, argv = {}, app }) {
145
145
  instance: app.getEffectiveInstance?.() || '' });
146
146
  return record;
147
147
  }
148
+
149
+ export function relatedListOptions(y) {
150
+ return y.option('related', { type: 'boolean', default: false, describe: 'Count related lists under Aggregate API access; suggest commands without loading supplementary records' })
151
+ .option('view', { type: 'string', describe: 'Form view for --related metadata (default: Default view)' })
152
+ .option('related-limit', { type: 'number', describe: 'Max related-list descriptors to count with --related (default 50)' });
153
+ }