@jacebenson/jsn 4.0.4 → 4.2.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/README.md +18 -2
- package/package.json +4 -3
- package/src/app.js +48 -64
- package/src/auth.js +46 -11
- package/src/capabilities.js +128 -0
- package/src/cli.js +129 -65
- package/src/commands/_ticket.js +37 -93
- package/src/commands/approvals.js +350 -0
- package/src/commands/atf.js +635 -0
- package/src/commands/auth.js +93 -13
- package/src/commands/catalog/enrich.js +139 -0
- package/src/commands/catalog.js +25 -81
- package/src/commands/cmdb.js +613 -0
- package/src/commands/codesearch.js +103 -0
- package/src/commands/dev/_generic.js +63 -111
- package/src/commands/dev/_simple.js +16 -12
- package/src/commands/dev/b4rules.js +1 -1
- package/src/commands/dev/domains.js +4 -0
- package/src/commands/dev/eval.js +65 -7
- package/src/commands/dev/flows.js +3 -0
- package/src/commands/dev/logs.js +97 -0
- package/src/commands/dev/rest.js +6 -0
- package/src/commands/dev/restmessage.js +1 -0
- package/src/commands/dev/scopes.js +49 -25
- package/src/commands/dev/scrapi.js +5 -0
- package/src/commands/dev/soapmessages.js +1 -0
- package/src/commands/dev/updatesets.js +145 -15
- package/src/commands/dev.js +3 -2
- package/src/commands/docs/db.js +14 -0
- package/src/commands/docs/docs.js +9 -21
- package/src/commands/docs/search.js +33 -10
- package/src/commands/groups.js +3 -0
- package/src/commands/records.js +274 -12
- package/src/commands/setup.js +5 -0
- package/src/commands/skill.js +3 -0
- package/src/commands/snippets.js +155 -0
- package/src/commands/tickets.js +9 -29
- package/src/commands/users.js +9 -35
- package/src/commands/version.js +3 -0
- package/src/errors.js +107 -1
- package/src/help.js +1 -0
- package/src/helpers.js +131 -15
- package/src/mutations.js +36 -91
- package/src/output.js +195 -64
- package/src/resolve-record.js +71 -0
- package/src/sdk.js +108 -8
- package/src/session.js +214 -0
package/src/session.js
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
// Session resolver — one module owns the answer to "which instance am I
|
|
2
|
+
// talking to, as whom, with what profile flags?"
|
|
3
|
+
//
|
|
4
|
+
// Before this module existed, that composite answer was assembled across
|
|
5
|
+
// four files with shared mutable state:
|
|
6
|
+
// - cli.js middleware resolved --profile by mutating cfg.activeProfile as
|
|
7
|
+
// a side effect, and poked BOTH app._overrideInstance and
|
|
8
|
+
// argv._overrideInstance (the --instance branch duplicated across two
|
|
9
|
+
// adjacent middlewares);
|
|
10
|
+
// - app.js carried _overrideInstance + getEffectiveInstance +
|
|
11
|
+
// setEffectiveInstance, re-deriving the active profile three ways;
|
|
12
|
+
// - auth.js reached into configProvider.config.profiles to re-derive the
|
|
13
|
+
// username for credential keying;
|
|
14
|
+
// - config.js's getEffectiveInstance duplicated the
|
|
15
|
+
// activeProfile || defaultProfile incantation.
|
|
16
|
+
//
|
|
17
|
+
// Now: resolveSession({argv-ish}, config) → a session object (pure — no
|
|
18
|
+
// mutation of argv OR config), applySession(app, session) is the SINGLE
|
|
19
|
+
// point where a flag override changes app/config state, and App keeps the
|
|
20
|
+
// resolved session on app.session for anything that needs identity
|
|
21
|
+
// (AuthManager is handed a provider over it, so it no longer spelunks
|
|
22
|
+
// config internals).
|
|
23
|
+
//
|
|
24
|
+
// Precedence (pinned by test/session.test.js — preserve exactly):
|
|
25
|
+
// --instance flag > --profile reference > active profile >
|
|
26
|
+
// default profile > legacy bare instanceURL
|
|
27
|
+
//
|
|
28
|
+
// Deliberately NOT here: env var loading (loadConfig already folds
|
|
29
|
+
// SERVICENOW_INSTANCE_URL into cfg.instanceURL before the resolver runs)
|
|
30
|
+
// and credential storage I/O (auth.js owns the keyring/file stores).
|
|
31
|
+
|
|
32
|
+
import { normalizeInstanceURL } from './config.js';
|
|
33
|
+
import { extractProfileName } from './helpers.js';
|
|
34
|
+
import { errUsage } from './errors.js';
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Resolve the composite session from parsed argv flags + loaded config.
|
|
38
|
+
* Pure: never mutates argv or config.
|
|
39
|
+
*
|
|
40
|
+
* @param {object} [argv] — anything with optional { instance, profile }
|
|
41
|
+
* (a yargs argv, or a hand-built { instance, profile } shape)
|
|
42
|
+
* @param {object} config — the loaded config (from loadConfig())
|
|
43
|
+
* @returns {{
|
|
44
|
+
* instance: string, // effective instance URL ('' when unresolved)
|
|
45
|
+
* profileName: string|null,// active profile name after flags
|
|
46
|
+
* profile: object|null, // the profile entry (instance_url, flags, ...)
|
|
47
|
+
* username: string|null, // profile username for credential keying
|
|
48
|
+
* domain: string, // domain-separation sys_id ('' = no scoping)
|
|
49
|
+
* readOnly: boolean, // profile.read_only === true (the armed guard)
|
|
50
|
+
* override: string|null, // normalized flag override URL, null when none
|
|
51
|
+
* profileExplicit: boolean,// true when --profile named the active profile
|
|
52
|
+
* unknownProfile: string|null, // set when --profile named no known profile
|
|
53
|
+
* }}
|
|
54
|
+
*/
|
|
55
|
+
export function resolveSession(argv = {}, config) {
|
|
56
|
+
const cfg = config || {};
|
|
57
|
+
const profiles = cfg.profiles || {};
|
|
58
|
+
|
|
59
|
+
// --profile selects the active profile for this run. Unknown names are
|
|
60
|
+
// surfaced as data (unknownProfile) — the caller (cli.js middleware)
|
|
61
|
+
// decides how to render the guard exit; resolving must not throw because
|
|
62
|
+
// App is constructed before argv exists.
|
|
63
|
+
let unknownProfile = null;
|
|
64
|
+
let profileName = null;
|
|
65
|
+
if (argv.profile) {
|
|
66
|
+
if (profiles[argv.profile]) {
|
|
67
|
+
profileName = argv.profile;
|
|
68
|
+
} else {
|
|
69
|
+
unknownProfile = argv.profile;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (!profileName) {
|
|
73
|
+
const fallback = cfg.activeProfile || cfg.defaultProfile;
|
|
74
|
+
if (fallback && profiles[fallback]) profileName = fallback;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const profile = profileName ? profiles[profileName] : null;
|
|
78
|
+
|
|
79
|
+
// Instance: --instance flag > profile reference > active/default
|
|
80
|
+
// profile > legacy bare instanceURL. The override is normalized here so
|
|
81
|
+
// every downstream consumer (SDK client, context header links) sees the
|
|
82
|
+
// canonical form. --profile counts as an override too (the old code
|
|
83
|
+
// poked the profile's URL into app._overrideInstance and rebuilt the SDK
|
|
84
|
+
// via setEffectiveInstance's normalization) — session.instance is always
|
|
85
|
+
// the normalized effective URL either way.
|
|
86
|
+
let override = null;
|
|
87
|
+
if (argv.instance) {
|
|
88
|
+
override = normalizeInstanceURL(argv.instance) || null;
|
|
89
|
+
} else if (argv.profile && profile?.instance_url) {
|
|
90
|
+
override = normalizeInstanceURL(profile.instance_url) || null;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const instance = normalizeInstanceURL(override || (profile && profile.instance_url) || cfg.instanceURL || '');
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
instance,
|
|
97
|
+
profileName,
|
|
98
|
+
profile,
|
|
99
|
+
username: (profile && profile.username) || null,
|
|
100
|
+
domain: (profile && profile.domain) || '',
|
|
101
|
+
readOnly: profile?.read_only === true,
|
|
102
|
+
override,
|
|
103
|
+
profileExplicit: Boolean(argv.profile) && !unknownProfile,
|
|
104
|
+
unknownProfile,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Derive the legacy context block (profileName + username for the context
|
|
110
|
+
* header) from a session. Kept separate from the session itself because
|
|
111
|
+
* printContextHeader() enriches it with live instance data (scope, update
|
|
112
|
+
* set) — the session stays a snapshot of resolution-time truth.
|
|
113
|
+
*
|
|
114
|
+
* Legacy fallback preserved: with no active profile NAME, find the profile
|
|
115
|
+
* whose instance_url matches the effective instance (legacy configs that
|
|
116
|
+
* predate named profiles), else derive a display name from the URL host.
|
|
117
|
+
*/
|
|
118
|
+
export function contextFromSession(session, config) {
|
|
119
|
+
const context = { profileName: '', username: '', scope: '', updateSet: '' };
|
|
120
|
+
if (!session) return context;
|
|
121
|
+
|
|
122
|
+
if (session.profileName) {
|
|
123
|
+
context.profileName = session.profileName;
|
|
124
|
+
context.username = session.username || '';
|
|
125
|
+
return context;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (!session.instance) return context;
|
|
129
|
+
context.profileName = extractProfileName(session.instance);
|
|
130
|
+
for (const [name, profile] of Object.entries(config?.profiles || {})) {
|
|
131
|
+
if (profile.instance_url === session.instance) {
|
|
132
|
+
context.profileName = name;
|
|
133
|
+
context.username = profile.username || '';
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return context;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Apply a resolved session to the App — the ONLY place flag overrides
|
|
142
|
+
* change app/config state. Explicit, greppable, and called from exactly
|
|
143
|
+
* one middleware (cli.js). Replaces the old scattered trio:
|
|
144
|
+
* cfg.activeProfile = argv.profile (buried side effect)
|
|
145
|
+
* app._overrideInstance = url; argv._overrideInstance = url (mirrored)
|
|
146
|
+
*
|
|
147
|
+
* @param {import('./app.js').App} app
|
|
148
|
+
* @param {object} sessionOrArgv — a resolveSession() result, or a bare
|
|
149
|
+
* { instance, profile } argv shape (resolved here for convenience)
|
|
150
|
+
* @returns {object} the applied session
|
|
151
|
+
*/
|
|
152
|
+
export function applySession(app, sessionOrArgv) {
|
|
153
|
+
// A resolved session carries `override` (null when no flag); bare argv
|
|
154
|
+
// ({ instance, profile }) does not. (Object.hasOwn, not `in` — plain
|
|
155
|
+
// objects inherit Object.prototype, which has neither, but hasOwn is the
|
|
156
|
+
// intent-precise check.)
|
|
157
|
+
const isResolved = sessionOrArgv != null && Object.hasOwn(sessionOrArgv, 'override');
|
|
158
|
+
const session = isResolved
|
|
159
|
+
? sessionOrArgv
|
|
160
|
+
: resolveSession(sessionOrArgv, app.config);
|
|
161
|
+
|
|
162
|
+
// --profile: switch the active profile on the config so anything still
|
|
163
|
+
// reading config directly (legacy callers, help rendering) sees the same
|
|
164
|
+
// answer the session computed. Flag args are pinned on _sessionArgv so
|
|
165
|
+
// refreshSession() (after config edits) keeps the same flags in force.
|
|
166
|
+
if (session.profileExplicit) {
|
|
167
|
+
app.config.activeProfile = session.profileName;
|
|
168
|
+
app._sessionArgv = { ...(app._sessionArgv || {}), profile: session.profileName };
|
|
169
|
+
}
|
|
170
|
+
if (!isResolved && sessionOrArgv?.instance !== undefined) {
|
|
171
|
+
app._sessionArgv = { ...(app._sessionArgv || {}), instance: sessionOrArgv.instance };
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Flag overrides (--instance, --profile) pin the effective instance for
|
|
175
|
+
// this run and rebuild the SDK client against it. No flags → clear any
|
|
176
|
+
// stale override so the config-sourced instance is authoritative. (The
|
|
177
|
+
// SDK must be rebuilt even at the same URL: the domain scope comes from
|
|
178
|
+
// the newly-active profile.)
|
|
179
|
+
app._overrideInstance = session.override;
|
|
180
|
+
if (session.instance) {
|
|
181
|
+
app._buildSdk(session.instance, session);
|
|
182
|
+
} else {
|
|
183
|
+
app.sdk = null;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
app.session = session;
|
|
187
|
+
app.loadContext();
|
|
188
|
+
return session;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Re-resolve the session after config edits (profile switched, created,
|
|
193
|
+
* or removed by auth/setup commands). Explicit flag overrides are pinned
|
|
194
|
+
* by re-resolving against the pinned argv; without them the config is
|
|
195
|
+
* authoritative.
|
|
196
|
+
*
|
|
197
|
+
* @param {import('./app.js').App} app
|
|
198
|
+
* @returns {object} the refreshed session
|
|
199
|
+
*/
|
|
200
|
+
export function refreshSession(app) {
|
|
201
|
+
return applySession(app, app._sessionArgv || {});
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* The missing-instance guard, backed by the session. The message and code
|
|
206
|
+
* (usage → exit 2 via the unified renderer) are observable behavior pinned
|
|
207
|
+
* by test/core.test.js — do not reword.
|
|
208
|
+
*/
|
|
209
|
+
export function requireSessionInstance(session) {
|
|
210
|
+
if (!session || !session.instance) {
|
|
211
|
+
throw errUsage('Instance URL required. Set via --instance flag, SERVICENOW_INSTANCE_URL env, or config file.');
|
|
212
|
+
}
|
|
213
|
+
return session.instance;
|
|
214
|
+
}
|