@tenonhq/dovetail-servicenow 0.0.35 → 0.0.36

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/dist/client.d.ts CHANGED
@@ -20,6 +20,15 @@
20
20
  * have SN_INSTANCE/SN_USER/SN_PASSWORD work without reconfiguration.
21
21
  */
22
22
  import type { ServiceNowClientConfig } from "./types";
23
+ /** Resolved auth mode: inbound API key (x-sn-apikey header) or basic auth. */
24
+ export type ResolvedAuth = {
25
+ mode: "apiKey";
26
+ apiKey: string;
27
+ } | {
28
+ mode: "basic";
29
+ user: string;
30
+ password: string;
31
+ };
23
32
  export interface TableQueryOptions {
24
33
  limit?: number;
25
34
  fields?: string[];
package/dist/client.js CHANGED
@@ -52,6 +52,15 @@ function resolveInstance(cfg) {
52
52
  return normalizeHost(raw);
53
53
  }
54
54
  function resolveAuth(cfg) {
55
+ // Explicit config wins over the environment, and within each layer an API
56
+ // key is the default over basic credentials. A cfg that names user/password
57
+ // (e.g. one resolved from an --env file) deliberately pins basic auth even
58
+ // when the surrounding process.env carries SN_API_KEY — an env-file retarget
59
+ // must be fully determined by its file.
60
+ if (cfg.apiKey) {
61
+ return { mode: "apiKey", apiKey: cfg.apiKey };
62
+ }
63
+ var cfgWantsBasic = Boolean(cfg.user || cfg.password);
55
64
  var user = cfg.user
56
65
  || process.env.SN_USER
57
66
  || process.env.SN_DEV_USERNAME
@@ -62,11 +71,20 @@ function resolveAuth(cfg) {
62
71
  || process.env.SN_DEV_PASSWORD
63
72
  || process.env.SN_PROD_PASSWORD
64
73
  || "";
74
+ if (!cfgWantsBasic) {
75
+ var envApiKey = process.env.SN_API_KEY
76
+ || process.env.SN_DEV_API_KEY
77
+ || process.env.SN_PROD_API_KEY
78
+ || "";
79
+ if (envApiKey) {
80
+ return { mode: "apiKey", apiKey: envApiKey };
81
+ }
82
+ }
65
83
  if (!user || !password) {
66
- throw new Error("ServiceNow credentials missing — set SN_USER/SN_PASSWORD (preferred) " +
67
- "or SN_DEV_USERNAME/SN_DEV_PASSWORD (or SN_PROD_*).");
84
+ throw new Error("ServiceNow credentials missing — set SN_API_KEY (inbound API key, preferred), " +
85
+ "or SN_USER/SN_PASSWORD, or SN_DEV_USERNAME/SN_DEV_PASSWORD (or SN_PROD_*).");
68
86
  }
69
- return { user: user, password: password };
87
+ return { mode: "basic", user: user, password: password };
70
88
  }
71
89
  function sleep(ms) {
72
90
  return new Promise(function (resolve) {
@@ -97,10 +115,19 @@ function createClient(config = {}) {
97
115
  var max5xx = config.maxRetries5xx != null
98
116
  ? config.maxRetries5xx
99
117
  : Number(process.env.SN_MAX_RETRIES_5XX) || 3;
118
+ var baseHeaders = {
119
+ accept: "application/json",
120
+ "content-type": "application/json"
121
+ };
122
+ if (creds.mode === "apiKey") {
123
+ baseHeaders["x-sn-apikey"] = creds.apiKey;
124
+ }
100
125
  var http = axios_1.default.create({
101
126
  baseURL: "https://" + host,
102
- auth: { username: creds.user, password: creds.password },
103
- headers: { accept: "application/json", "content-type": "application/json" },
127
+ auth: creds.mode === "basic"
128
+ ? { username: creds.user, password: creds.password }
129
+ : undefined,
130
+ headers: baseHeaders,
104
131
  validateStatus: function () { return true; }
105
132
  });
106
133
  var lastAt = 0;
@@ -17,8 +17,9 @@ import type { ServiceNowClientConfig } from "./types";
17
17
  * the surrounding process.env, so the file fully determines the target.
18
18
  *
19
19
  * Throws if the file cannot be read, or if it does not define an instance and
20
- * a complete credential pair — failing loudly rather than silently falling
21
- * back to whatever instance the host process happens to be pointed at.
20
+ * credentials (an inbound API key, or a complete basic-auth pair) — failing
21
+ * loudly rather than silently falling back to whatever instance the host
22
+ * process happens to be pointed at.
22
23
  */
23
24
  export declare function resolveConfigFromEnvFile(envPath: string): ServiceNowClientConfig;
24
25
  /**
@@ -25,8 +25,9 @@ const client_1 = require("./client");
25
25
  * the surrounding process.env, so the file fully determines the target.
26
26
  *
27
27
  * Throws if the file cannot be read, or if it does not define an instance and
28
- * a complete credential pair — failing loudly rather than silently falling
29
- * back to whatever instance the host process happens to be pointed at.
28
+ * credentials (an inbound API key, or a complete basic-auth pair) — failing
29
+ * loudly rather than silently falling back to whatever instance the host
30
+ * process happens to be pointed at.
30
31
  */
31
32
  function resolveConfigFromEnvFile(envPath) {
32
33
  if (typeof envPath !== "string" || envPath.length === 0) {
@@ -41,15 +42,24 @@ function resolveConfigFromEnvFile(envPath) {
41
42
  }
42
43
  var parsed = dotenv_1.default.parse(raw);
43
44
  var instance = parsed.SN_INSTANCE || parsed.SN_DEV_INSTANCE || parsed.SN_PROD_INSTANCE || "";
45
+ var apiKey = parsed.SN_API_KEY || parsed.SN_DEV_API_KEY || parsed.SN_PROD_API_KEY || "";
44
46
  var user = parsed.SN_USER || parsed.SN_DEV_USERNAME || parsed.SN_PROD_USERNAME || "";
45
47
  var password = parsed.SN_PASSWORD || parsed.SN_DEV_PASSWORD || parsed.SN_PROD_PASSWORD || "";
46
48
  if (!instance) {
47
49
  throw new Error("env file '" + envPath + "' does not define a ServiceNow instance — " +
48
50
  "set SN_INSTANCE (preferred) or SN_DEV_INSTANCE / SN_PROD_INSTANCE.");
49
51
  }
52
+ if (apiKey) {
53
+ // Key is the default auth mode when the file defines one. Returning the
54
+ // key WITHOUT user/password keeps the resolved config fully explicit —
55
+ // resolveAuth treats a config that names user/password as a deliberate
56
+ // basic-auth pin, so leaking them here would flip the mode.
57
+ return { instance: instance, apiKey: apiKey };
58
+ }
50
59
  if (!user || !password) {
51
60
  throw new Error("env file '" + envPath + "' is missing ServiceNow credentials — " +
52
- "set SN_USER/SN_PASSWORD (preferred) or SN_DEV_USERNAME/SN_DEV_PASSWORD (or SN_PROD_*).");
61
+ "set SN_API_KEY (inbound API key, preferred), SN_USER/SN_PASSWORD, " +
62
+ "or SN_DEV_USERNAME/SN_DEV_PASSWORD (or SN_PROD_*).");
53
63
  }
54
64
  return { instance: instance, user: user, password: password };
55
65
  }
package/dist/types.d.ts CHANGED
@@ -4,9 +4,16 @@
4
4
  export interface ServiceNowClientConfig {
5
5
  /** Instance host, e.g. "tenonworkstudio.service-now.com". Defaults to SN_INSTANCE env var. */
6
6
  instance?: string;
7
- /** Basic-auth user. Defaults to SN_USER env var. */
7
+ /**
8
+ * Inbound REST API key, sent as the `x-sn-apikey` header. Defaults to the
9
+ * SN_API_KEY env var. When a key is present it is the default auth mode and
10
+ * basic auth is not sent. Requires matching REST API access policies on the
11
+ * instance — an API without a key-enabled policy ignores the header (401).
12
+ */
13
+ apiKey?: string;
14
+ /** Basic-auth user. Defaults to SN_USER env var. Fallback when no API key is configured. */
8
15
  user?: string;
9
- /** Basic-auth password. Defaults to SN_PASSWORD env var. */
16
+ /** Basic-auth password. Defaults to SN_PASSWORD env var. Fallback when no API key is configured. */
10
17
  password?: string;
11
18
  /** Min gap between requests (ms). Defaults to SN_REQUEST_INTERVAL_MS or 20. */
12
19
  requestIntervalMs?: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tenonhq/dovetail-servicenow",
3
- "version": "0.0.35",
3
+ "version": "0.0.36",
4
4
  "engines": {
5
5
  "node": ">=22"
6
6
  },