@jacebenson/jsn 4.3.8-dev.55 → 4.3.8-dev.57

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.8-dev.55",
3
+ "version": "4.3.8-dev.57",
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": {
@@ -8,7 +8,7 @@
8
8
  },
9
9
  "main": "src/cli.js",
10
10
  "scripts": {
11
- "test": "node --experimental-test-module-mocks --test --test-concurrency=1 $(find test -name '*.test.js')",
11
+ "test": "node --experimental-test-module-mocks --test --test-concurrency=4 $(find test -name '*.test.js')",
12
12
  "lint": "npx eslint src/ bin/ test/",
13
13
  "start": "node bin/jsn.js",
14
14
  "docs": "npm --prefix docs run build",
@@ -560,25 +560,16 @@ export function updateSetsCmd(wrap) {
560
560
  throw errUsage(`Cannot set "${name}" as current — update set is ${stateRaw === 'ignore' ? 'ignored' : 'complete'}.\nView it instead: jsn updatesets show "${name}"`);
561
561
  }
562
562
  const userSysID = await requireCurrentUserSysId(app.sdk);
563
- await setCurrentUpdateSet(app.sdk, userSysID, sysID);
564
-
565
- // Like the platform update set picker: switching the set also
566
- // switches the application scope into the set's scope. The
567
- // application field is a sys_scope reference, so its raw value
568
- // IS the scope sys_id the apps.current_app preference stores.
563
+ // Scope selection can reset the session's capture set. Select the
564
+ // scope first, then the requested set, and verify both picker writes.
565
+ const scopeSysId = updateSetApp?.value || (typeof updateSetApp === 'string' ? updateSetApp : '');
569
566
  let scopeNote = '';
570
- try {
571
- const scopeSysId = updateSetApp?.value || (typeof updateSetApp === 'string' ? updateSetApp : '');
572
- if (scopeSysId) {
573
- await setCurrentApplication(app.sdk, userSysID, scopeSysId);
574
- const scopeName = updateSetApp?.display_value || updateSetApp;
575
- scopeNote = ` — scope switched to ${scopeName}`;
576
- }
577
- } catch {
578
- // scope switch failed — fall back to a warning
579
- const currentApp = await getCurrentApplication(app.sdk, userSysID).catch(() => null);
580
- scopeNote = scopeMismatchWarning(updateSetApp, currentApp);
567
+ if (scopeSysId) {
568
+ await setCurrentApplication(app.sdk, userSysID, scopeSysId);
569
+ const scopeName = updateSetApp?.display_value || updateSetApp;
570
+ scopeNote = ` — scope switched to ${scopeName}`;
581
571
  }
572
+ await setCurrentUpdateSet(app.sdk, userSysID, sysID);
582
573
 
583
574
  app.ok({ update_set: name, sys_id: sysID }, { summary: `Current update set: ${name}${scopeNote}` });
584
575
  }),
@@ -631,15 +622,18 @@ export function updateSetsCmd(wrap) {
631
622
 
632
623
  // Auto-set as current update set
633
624
  const sysID = record?.sys_id?.value || record?.sys_id;
625
+ let selectionNote = '';
634
626
  try {
635
627
  const userSysID = await requireCurrentUserSysId(app.sdk);
636
628
  await setCurrentUpdateSet(app.sdk, userSysID, sysID);
637
- } catch {
638
- // Non-fatal — auto-set is a convenience, not mandatory
629
+ } catch (error) {
630
+ // Creation succeeded. Report partial success without hiding a
631
+ // failed selection or inviting a duplicate create on retry.
632
+ selectionNote = `; not selected as current: ${error.message}. Run jsn updatesets set "${argv.name}"`;
639
633
  }
640
634
 
641
635
  app.ok(record, {
642
- summary: `Created update set: ${argv.name}`,
636
+ summary: `Created update set: ${argv.name}${selectionNote}`,
643
637
  breadcrumbs: [
644
638
  {
645
639
  action: 'set',
package/src/context.js CHANGED
@@ -63,25 +63,16 @@ export async function getCurrentUpdateSet(sdk, userSysID) {
63
63
  return { name: val, sys_id: val };
64
64
  }
65
65
 
66
- /**
67
- * Find-or-create a sys_user_preference and set its value. Shared by the
68
- * scope/update-set "set" commands and create auto-set (was copy-pasted 3x).
69
- */
70
- async function setUserPreference(sdk, userSysID, name, value) {
71
- const params = new URLSearchParams();
72
- params.set('sysparm_query', `user=${userSysID}^name=${name}`);
73
- params.set('sysparm_limit', '1');
74
- params.set('sysparm_fields', 'sys_id');
75
- const prefs = await sdk.list('sys_user_preference', params);
76
- if (prefs.length > 0) {
77
- await sdk.update('sys_user_preference', getStringField(prefs[0], 'sys_id'), { value });
78
- } else {
79
- await sdk.create('sys_user_preference', {
80
- user: userSysID,
81
- name,
82
- value,
83
- type: 'string',
84
- });
66
+ // Use the same selection contract as the platform picker. Table API writes to
67
+ // sys_user_preference do not update an already-warmed session's capture context.
68
+ async function selectContext(sdk, picker, data, expected) {
69
+ const endpoint = `${sdk.baseURL}/api/now/ui/concoursepicker/${picker}`;
70
+ await sdk.request(endpoint, { method: 'PUT', body: JSON.stringify(data) });
71
+ const response = await sdk.request(endpoint, { method: 'GET' });
72
+ const current = response?.result?.current;
73
+ const actual = picker === 'application' ? current : current?.sysId;
74
+ if (actual !== expected) {
75
+ throw new Error(`${picker === 'application' ? 'Application' : 'Update set'} selection was not applied. Expected ${expected}, received ${typeof actual === 'string' ? actual : 'an unreadable selection'}.`);
85
76
  }
86
77
  }
87
78
 
@@ -98,12 +89,12 @@ export async function requireCurrentUserSysId(sdk) {
98
89
  return getStringField(user[0], 'sys_id');
99
90
  }
100
91
 
101
- /** Set the current application scope via the apps.current_app preference. */
102
- export async function setCurrentApplication(sdk, userSysID, scopeSysID) {
103
- await setUserPreference(sdk, userSysID, 'apps.current_app', scopeSysID);
92
+ /** Select application scope in the authenticated session and verify it. */
93
+ export async function setCurrentApplication(sdk, _userSysID, scopeSysID) {
94
+ await selectContext(sdk, 'application', { app_id: scopeSysID }, scopeSysID);
104
95
  }
105
96
 
106
- /** Set the current update set via the sys_update_set preference. */
107
- export async function setCurrentUpdateSet(sdk, userSysID, updateSetSysID) {
108
- await setUserPreference(sdk, userSysID, 'sys_update_set', updateSetSysID);
97
+ /** Select the capture update set in the authenticated session and verify it. */
98
+ export async function setCurrentUpdateSet(sdk, _userSysID, updateSetSysID) {
99
+ await selectContext(sdk, 'updateset', { sysId: updateSetSysID }, updateSetSysID);
109
100
  }