@jacebenson/jsn 4.0.3 → 4.0.4

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.
@@ -15,6 +15,7 @@ import { formsCmd } from './dev/forms.js';
15
15
  import { listsCmd } from './dev/lists.js';
16
16
  import { updateSetsCmd } from './dev/updatesets.js';
17
17
  import { scopesCmd } from './dev/scopes.js';
18
+ import { domainsCmd } from './dev/domains.js';
18
19
  import { evalCmd } from './dev/eval.js';
19
20
  import { restCmd } from './dev/rest.js';
20
21
  import { logsCmd } from './dev/logs.js';
@@ -47,6 +48,7 @@ export function devCmd(wrap) {
47
48
  .command(rolesCmd(wrap))
48
49
  .command(updateSetsCmd(wrap))
49
50
  .command(scopesCmd(wrap))
51
+ .command(domainsCmd(wrap))
50
52
  .command(propertiesCmd(wrap))
51
53
  .command(relationshipsCmd(wrap))
52
54
  .command(appmodulesCmd(wrap))
@@ -61,6 +63,19 @@ export function devCmd(wrap) {
61
63
  .command(evalCmd(wrap))
62
64
  .command(restCmd(wrap));
63
65
  },
64
- handler: () => {},
66
+ handler: (argv) => {
67
+ if (argv._[1]) return; // a subcommand ran — its own handler handled it
68
+ console.log('Manage ServiceNow development artifacts.');
69
+ console.log('');
70
+ console.log('Most dev commands are also available at the top level:');
71
+ console.log(' flows, actions, rules, clientscripts, uiactions, uipolicies,');
72
+ console.log(' tables, columns, forms, lists, import, sppages, spwidgets,');
73
+ console.log(' uipages, appmenu, scrapi, acls, roles, properties,');
74
+ console.log(' relationships, appmodules, listcontrols, views, privileges,');
75
+ console.log(' uxscripts, aliases, catalogscripts, cataloguipolicies, logs,');
76
+ console.log(' updatesets, scopes, eval, rest');
77
+ console.log('');
78
+ console.log('Run "jsn dev <command> --help" or "jsn <command> --help" for details.');
79
+ },
65
80
  };
66
81
  }
@@ -21,7 +21,7 @@ function getDefaultColumns(table) {
21
21
 
22
22
  export function recordsCmd(wrap) {
23
23
  return {
24
- command: 'records <subcommand>',
24
+ command: 'records [subcommand]',
25
25
  describe: 'Query and manage records in any table (e.g. "records list --table incident --query priority=1")',
26
26
  builder: (yargs) => {
27
27
  return yargs
@@ -208,7 +208,8 @@ export function recordsCmd(wrap) {
208
208
  })
209
209
 
210
210
  },
211
- handler: () => {
211
+ handler: (argv) => {
212
+ if (argv._[1]) return; // a subcommand ran — its own handler handled it
212
213
  console.log('Query and manage records in any ServiceNow table.');
213
214
  console.log('');
214
215
  console.log('Available subcommands:');
package/src/context.js CHANGED
@@ -1,5 +1,7 @@
1
1
  // ServiceNow runtime context: current user, scope, update set
2
2
 
3
+ import { getStringField } from './helpers.js';
4
+
3
5
  export async function getCurrentUser(sdk) {
4
6
  const params = new URLSearchParams();
5
7
  params.set('sysparm_query', 'user_name=javascript:gs.getUserName()');
@@ -22,24 +24,24 @@ export async function getCurrentApplication(sdk, userSysID) {
22
24
  params.set('sysparm_limit', '1');
23
25
  params.set('sysparm_fields', 'value');
24
26
  const records = await sdk.list('sys_user_preference', params);
25
- if (records.length === 0) return { scope: 'global' };
27
+ if (records.length === 0) return { scope: 'global', appSysId: '' };
26
28
  const val = records[0].value?.value || records[0].value;
27
- if (!val) return { scope: 'global' };
29
+ if (!val) return { scope: 'global', appSysId: '' };
28
30
 
29
31
  // Try to resolve sys_id to scope name
30
32
  try {
31
33
  const app = await sdk.get('sys_scope', val);
32
- if (app) return { scope: app.scope || 'global' };
34
+ if (app) return { scope: app.scope || 'global', appSysId: val };
33
35
  } catch {
34
36
  // fallback
35
37
  }
36
38
  try {
37
39
  const app2 = await sdk.get('sys_app', val);
38
- if (app2) return { scope: app2.scope || 'global' };
40
+ if (app2) return { scope: app2.scope || 'global', appSysId: val };
39
41
  } catch {
40
42
  // fallback
41
43
  }
42
- return { scope: val };
44
+ return { scope: val, appSysId: val };
43
45
  }
44
46
 
45
47
  export async function getCurrentUpdateSet(sdk, userSysID) {
@@ -60,3 +62,48 @@ export async function getCurrentUpdateSet(sdk, userSysID) {
60
62
  }
61
63
  return { name: val, sys_id: val };
62
64
  }
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
+ });
85
+ }
86
+ }
87
+
88
+ /** Resolve the current user's sys_id (throws if it can't be determined). */
89
+ export async function requireCurrentUserSysId(sdk) {
90
+ const user = await sdk.list('sys_user', new URLSearchParams({
91
+ sysparm_query: 'user_name=javascript:gs.getUserName()',
92
+ sysparm_limit: '1',
93
+ sysparm_fields: 'sys_id',
94
+ }));
95
+ if (user.length === 0) {
96
+ throw new Error('Could not determine current user');
97
+ }
98
+ return getStringField(user[0], 'sys_id');
99
+ }
100
+
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);
104
+ }
105
+
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);
109
+ }
package/src/help.js CHANGED
@@ -111,6 +111,7 @@ const CONFIG_COMMANDS = [
111
111
  { name: 'auth', desc: 'Authentication (login, refresh, status)' },
112
112
  { name: 'updatesets', desc: 'Update sets (set, create, export)' },
113
113
  { name: 'scopes', desc: 'Application scopes' },
114
+ { name: 'domains', desc: 'Domains (domain separation)' },
114
115
  ];
115
116
 
116
117
  const DEVELOPER_COMMANDS = [
@@ -153,8 +154,21 @@ LEARN MORE
153
154
  Use "jsn <command> --help" for more information about a command.`;
154
155
  }
155
156
 
156
- export function renderHelp() {
157
- const sections = Object.entries(COMMAND_GROUPS).map(([name, cmds]) => renderGroup(name, cmds));
157
+ export function renderHelp(config) {
158
+ // Domain separation is an optional plugin — only advertise `domains`
159
+ // when the active profile's capability probe found it installed.
160
+ const activeName = config?.activeProfile || config?.defaultProfile;
161
+ const hasDS = !!(activeName && config?.profiles?.[activeName]?.domain_separation);
162
+ const configCmds = hasDS
163
+ ? CONFIG_COMMANDS
164
+ : CONFIG_COMMANDS.filter((c) => !c.name || c.name !== 'domains');
165
+
166
+ const COMMAND_GROUPS_LOCAL = {
167
+ ...COMMAND_GROUPS,
168
+ 'CONFIGURATION': configCmds,
169
+ };
170
+
171
+ const sections = Object.entries(COMMAND_GROUPS_LOCAL).map(([name, cmds]) => renderGroup(name, cmds));
158
172
 
159
173
  return [
160
174
  'Usage: jsn <command> [options]',
package/src/mutations.js CHANGED
@@ -75,11 +75,20 @@ export const MUTATION_COMMANDS = [
75
75
  ['scopes', 'set'],
76
76
  ['dev', 'scopes', 'create'],
77
77
  ['dev', 'scopes', 'set'],
78
- // Update sets: create + set (root + dev forms)
78
+ // Domains: set writes the profile config (root + dev forms)
79
+ ['domains', 'set'],
80
+ ['dev', 'domains', 'set'],
81
+ // Update sets: create, set, complete, ignore, parent (root + dev forms)
79
82
  ['updatesets', 'create'],
80
83
  ['updatesets', 'set'],
84
+ ['updatesets', 'complete'],
85
+ ['updatesets', 'ignore'],
86
+ ['updatesets', 'parent'],
81
87
  ['dev', 'updatesets', 'create'],
82
88
  ['dev', 'updatesets', 'set'],
89
+ ['dev', 'updatesets', 'complete'],
90
+ ['dev', 'updatesets', 'ignore'],
91
+ ['dev', 'updatesets', 'parent'],
83
92
  ];
84
93
 
85
94
  /**
package/src/sdk.js CHANGED
@@ -11,6 +11,7 @@ export class SDKClient {
11
11
  this.baseURL = baseURL.replace(/\/$/, '');
12
12
  this.authProvider = authProvider;
13
13
  this.timeout = opts.timeout || DEFAULT_TIMEOUT;
14
+ this.domain = opts.domain || '';
14
15
  }
15
16
 
16
17
  async _setAuth(req) {
@@ -53,6 +54,9 @@ export class SDKClient {
53
54
  if (opts.body && typeof opts.body === 'string') {
54
55
  req.headers.set('Content-Type', 'application/json');
55
56
  }
57
+ if (this.domain) {
58
+ req.headers.set('X-Now-Domain', this.domain);
59
+ }
56
60
  await this._setAuth(req);
57
61
 
58
62
  const resp = await fetch(req);