node-autotask 0.8.0 → 0.8.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.
Files changed (62) hide show
  1. package/ARCHITECTURE.md +54 -12
  2. package/N8N_MCP_MANIFEST.json +2 -2
  3. package/N8N_MCP_MANIFEST.md +3 -3
  4. package/README.md +9 -3
  5. package/dist/{chunk-C7U3ZRRK.js → chunk-2QLOK7PX.js} +27 -31
  6. package/dist/{chunk-C7U3ZRRK.js.map → chunk-2QLOK7PX.js.map} +1 -1
  7. package/dist/{chunk-LDKIIVFV.cjs → chunk-CQIZDSBE.cjs} +272 -276
  8. package/dist/chunk-CQIZDSBE.cjs.map +1 -0
  9. package/dist/{chunk-QUFEIJEB.cjs → chunk-DA73ALND.cjs} +136 -138
  10. package/dist/chunk-DA73ALND.cjs.map +1 -0
  11. package/dist/{chunk-GNS47RBV.js → chunk-IUJJPWUX.js} +612 -18
  12. package/dist/chunk-IUJJPWUX.js.map +1 -0
  13. package/dist/{chunk-RFXGG37L.cjs → chunk-IV2Z3JLR.cjs} +680 -86
  14. package/dist/chunk-IV2Z3JLR.cjs.map +1 -0
  15. package/dist/{chunk-ONPT4PIV.js → chunk-PAQXQFGG.js} +4 -6
  16. package/dist/{chunk-ONPT4PIV.js.map → chunk-PAQXQFGG.js.map} +1 -1
  17. package/dist/index.cjs +120 -27
  18. package/dist/index.cjs.map +1 -1
  19. package/dist/index.d.cts +129 -3
  20. package/dist/index.d.ts +129 -3
  21. package/dist/index.js +156 -63
  22. package/dist/index.js.map +1 -1
  23. package/dist/operations/index.cjs +113 -116
  24. package/dist/operations/index.cjs.map +1 -1
  25. package/dist/operations/index.js +9 -12
  26. package/dist/operations/index.js.map +1 -1
  27. package/dist/pool.cjs +8 -11
  28. package/dist/pool.cjs.map +1 -1
  29. package/dist/pool.js +4 -7
  30. package/dist/pool.js.map +1 -1
  31. package/dist/resources/index.cjs +3 -5
  32. package/dist/resources/index.cjs.map +1 -1
  33. package/dist/resources/index.js +2 -4
  34. package/package.json +9 -3
  35. package/dist/chunk-5QVXRT3A.cjs +0 -580
  36. package/dist/chunk-5QVXRT3A.cjs.map +0 -1
  37. package/dist/chunk-FE225M76.js +0 -580
  38. package/dist/chunk-FE225M76.js.map +0 -1
  39. package/dist/chunk-GNS47RBV.js.map +0 -1
  40. package/dist/chunk-IFXFOYDU.cjs +0 -32
  41. package/dist/chunk-IFXFOYDU.cjs.map +0 -1
  42. package/dist/chunk-LDKIIVFV.cjs.map +0 -1
  43. package/dist/chunk-OOEVTGP4.js +0 -121
  44. package/dist/chunk-OOEVTGP4.js.map +0 -1
  45. package/dist/chunk-PBBFMRI5.cjs +0 -121
  46. package/dist/chunk-PBBFMRI5.cjs.map +0 -1
  47. package/dist/chunk-QF4K3XTK.js +0 -32
  48. package/dist/chunk-QF4K3XTK.js.map +0 -1
  49. package/dist/chunk-QUFEIJEB.cjs.map +0 -1
  50. package/dist/chunk-RFXGG37L.cjs.map +0 -1
  51. package/dist/credential-shape.cjs +0 -15
  52. package/dist/credential-shape.cjs.map +0 -1
  53. package/dist/credential-shape.d.cts +0 -52
  54. package/dist/credential-shape.d.ts +0 -52
  55. package/dist/credential-shape.js +0 -15
  56. package/dist/credential-shape.js.map +0 -1
  57. package/dist/credentials.cjs +0 -17
  58. package/dist/credentials.cjs.map +0 -1
  59. package/dist/credentials.d.cts +0 -78
  60. package/dist/credentials.d.ts +0 -78
  61. package/dist/credentials.js +0 -17
  62. package/dist/credentials.js.map +0 -1
@@ -1,14 +1,560 @@
1
- import {
2
- AutotaskConfigError,
3
- AutotaskError,
4
- HostPolicyDeniedError,
5
- NoopLogger,
6
- PaginationLimitError
7
- } from "./chunk-FE225M76.js";
8
- import {
9
- CREDENTIAL_ENV_LOAD_ACTION,
10
- credentialShapeProblem
11
- } from "./chunk-QF4K3XTK.js";
1
+ // src/logger.ts
2
+ var NoopLogger = {};
3
+ var REDACTED = "[REDACTED]";
4
+ var CREDENTIAL_HEADER_NAMES = [
5
+ "ApiIntegrationCode",
6
+ "UserName",
7
+ "Secret",
8
+ "ImpersonationResourceId",
9
+ "authToken"
10
+ ];
11
+ function normalizeKey(key) {
12
+ return key.toLowerCase().replace(/[_-]/g, "");
13
+ }
14
+ var CREDENTIAL_KEY_SET = new Set(CREDENTIAL_HEADER_NAMES.map(normalizeKey));
15
+ function isCredentialHeaderName(name, extraSecretKeys) {
16
+ const normalized = normalizeKey(name);
17
+ if (CREDENTIAL_KEY_SET.has(normalized)) return true;
18
+ if (extraSecretKeys === void 0) return false;
19
+ return extraSecretKeys.some((extra) => normalizeKey(extra) === normalized);
20
+ }
21
+ function isPlainRecord(value) {
22
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
23
+ const proto = Object.getPrototypeOf(value);
24
+ return proto === Object.prototype || proto === null;
25
+ }
26
+ function redactRecord(value, extraSecretKeys) {
27
+ if (Array.isArray(value)) {
28
+ return value.map((item) => redactRecord(item, extraSecretKeys));
29
+ }
30
+ if (isPlainRecord(value)) {
31
+ const out = {};
32
+ for (const [key, item] of Object.entries(value)) {
33
+ out[key] = isCredentialHeaderName(key, extraSecretKeys) ? REDACTED : redactRecord(item, extraSecretKeys);
34
+ }
35
+ return out;
36
+ }
37
+ return value;
38
+ }
39
+
40
+ // src/redact.ts
41
+ var SECRET_RESPONSE_FIELDS = [
42
+ "password",
43
+ // ClientPortalUserModel
44
+ "secretKey",
45
+ // the 5 webhook models
46
+ "secret"
47
+ // IntegrationVendorInsightModel, IntegrationVendorWidgetModel
48
+ ];
49
+ var PERSONAL_RESPONSE_FIELDS = [
50
+ "additionalAddressInformation",
51
+ "additionalBillToAddressInformation",
52
+ "additionalShipToAddressInformation",
53
+ "address1",
54
+ "address2",
55
+ "addressLine",
56
+ "addressLine1",
57
+ "alternatePhone",
58
+ "alternatePhone1",
59
+ "alternatePhone2",
60
+ "billingAddress1",
61
+ "billingAddress2",
62
+ "billToAdditionalAddressInformation",
63
+ "billToCity",
64
+ "billToPostalCode",
65
+ "billToZipCode",
66
+ "city",
67
+ "email",
68
+ "email2",
69
+ "email3",
70
+ "emailAddress",
71
+ "emailAddress2",
72
+ "emailAddress3",
73
+ "extApprovalContactResponse",
74
+ "fax",
75
+ "faxNumber",
76
+ "gender",
77
+ "homePhone",
78
+ "mobilePhone",
79
+ "notificationEmailAddress",
80
+ "officePhone",
81
+ "phone",
82
+ "postalCode",
83
+ "recipientEmailAddress",
84
+ "rmmIsMobileDeviceManagementEnrolled",
85
+ "shipToAddress1",
86
+ "shipToAddress2",
87
+ "shipToCity",
88
+ "shipToPostalCode",
89
+ "userName",
90
+ "zipCode"
91
+ ];
92
+ var CONFIDENTIAL_RESPONSE_FIELDS = [
93
+ "domain",
94
+ "domainExpirationDateTime",
95
+ "domainLastUpdatedDateTime",
96
+ "domainRegistrationDateTime",
97
+ "primaryContact",
98
+ "rmmDeviceAuditExternalIPAddress",
99
+ "rmmDeviceAuditIPAddress",
100
+ "rmmDeviceAuditMacAddress",
101
+ "rmmDeviceAuditMobileNumber",
102
+ "rmmDeviceAuditSNMPContact",
103
+ "taxID",
104
+ "webAddress"
105
+ ];
106
+ var SECRET_SET = new Set(SECRET_RESPONSE_FIELDS);
107
+ var OPTIONAL_SET = /* @__PURE__ */ new Set([...PERSONAL_RESPONSE_FIELDS, ...CONFIDENTIAL_RESPONSE_FIELDS]);
108
+ function isPlainRecord2(value) {
109
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
110
+ const proto = Object.getPrototypeOf(value);
111
+ return proto === Object.prototype || proto === null;
112
+ }
113
+ function isRedactedFieldName(name, policy) {
114
+ return SECRET_SET.has(name) || policy.redactPersonalData && OPTIONAL_SET.has(name);
115
+ }
116
+ function redactResponseBody(value, policy) {
117
+ const personal = policy.redactPersonalData;
118
+ if (Array.isArray(value)) {
119
+ return value.map((entry) => redactResponseBody(entry, policy));
120
+ }
121
+ if (!isPlainRecord2(value)) return value;
122
+ const out = {};
123
+ for (const [key, entry] of Object.entries(value)) {
124
+ const pinned = SECRET_SET.has(key) || personal && OPTIONAL_SET.has(key);
125
+ out[key] = pinned ? REDACTED : redactResponseBody(entry, policy);
126
+ }
127
+ return out;
128
+ }
129
+
130
+ // src/errors.ts
131
+ function isRecord(value) {
132
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
133
+ const proto = Object.getPrototypeOf(value);
134
+ return proto === Object.prototype || proto === null;
135
+ }
136
+ var AutotaskError = class extends Error {
137
+ code;
138
+ category;
139
+ operation;
140
+ httpStatus;
141
+ vendorError;
142
+ resourceIds;
143
+ suggestedAction;
144
+ correlationId;
145
+ retryable;
146
+ url;
147
+ /** Issue #9/#14: the additive structured extras (recovery refusal payload; emptyReason / pointerNote) — never changes the error contract. */
148
+ details;
149
+ constructor(message, options = {}) {
150
+ super(message);
151
+ this.name = "AutotaskError";
152
+ this.code = options.code ?? "AUTOTASK_ERROR";
153
+ this.category = options.category ?? "server";
154
+ this.retryable = options.retryable ?? false;
155
+ this.operation = options.operation;
156
+ this.httpStatus = options.httpStatus;
157
+ this.vendorError = options.vendorError;
158
+ this.resourceIds = options.resourceIds;
159
+ this.suggestedAction = options.suggestedAction;
160
+ this.correlationId = options.correlationId;
161
+ this.url = options.url;
162
+ this.details = options.details;
163
+ Error.captureStackTrace?.(this, this.constructor);
164
+ }
165
+ };
166
+ var AutotaskConfigError = class extends AutotaskError {
167
+ constructor(message, options = {}) {
168
+ super(message, { code: "CONFIG_ERROR", category: "validation", ...options });
169
+ this.name = "AutotaskConfigError";
170
+ }
171
+ };
172
+ var AutotaskNetworkError = class extends AutotaskError {
173
+ constructor(message, options = {}) {
174
+ super(message, {
175
+ code: "NETWORK_ERROR",
176
+ category: "network",
177
+ retryable: true,
178
+ suggestedAction: options.suggestedAction ?? "Check connectivity to the Autotask zone and retry.",
179
+ ...options
180
+ });
181
+ this.name = "AutotaskNetworkError";
182
+ }
183
+ };
184
+ var AutotaskTimeoutError = class extends AutotaskError {
185
+ constructor(message, options = {}) {
186
+ super(message, {
187
+ code: "TIMEOUT",
188
+ category: "timeout",
189
+ retryable: true,
190
+ suggestedAction: options.suggestedAction ?? "Retry, narrow the query, or raise timeoutMs.",
191
+ ...options
192
+ });
193
+ this.name = "AutotaskTimeoutError";
194
+ }
195
+ };
196
+ var AutotaskAbortError = class extends AutotaskError {
197
+ constructor(message, options = {}) {
198
+ super(message, { code: "ABORTED", category: "network", retryable: false, ...options });
199
+ this.name = "AutotaskAbortError";
200
+ }
201
+ };
202
+ var ResponseRejectedError = class extends AutotaskError {
203
+ constructor(message, options = {}) {
204
+ super(message, { code: "RESPONSE_REJECTED", category: "policy", retryable: false, ...options });
205
+ this.name = "ResponseRejectedError";
206
+ }
207
+ };
208
+ var BadRequestError = class extends AutotaskError {
209
+ constructor(message, options = {}) {
210
+ super(message, { code: "BAD_REQUEST", category: "validation", ...options });
211
+ this.name = "BadRequestError";
212
+ }
213
+ };
214
+ var UnauthorizedError = class extends AutotaskError {
215
+ constructor(message, options = {}) {
216
+ super(message, { code: "UNAUTHORIZED", category: "auth", ...options });
217
+ this.name = "UnauthorizedError";
218
+ }
219
+ };
220
+ var ForbiddenError = class extends AutotaskError {
221
+ constructor(message, options = {}) {
222
+ super(message, { code: "FORBIDDEN", category: "auth", ...options });
223
+ this.name = "ForbiddenError";
224
+ }
225
+ };
226
+ var NotFoundError = class extends AutotaskError {
227
+ constructor(message, options = {}) {
228
+ super(message, { code: "NOT_FOUND", category: "not_found", ...options });
229
+ this.name = "NotFoundError";
230
+ }
231
+ };
232
+ var ConflictError = class extends AutotaskError {
233
+ constructor(message, options = {}) {
234
+ super(message, { code: "CONFLICT", category: "conflict", ...options });
235
+ this.name = "ConflictError";
236
+ }
237
+ };
238
+ var UnprocessableEntityError = class extends AutotaskError {
239
+ constructor(message, options = {}) {
240
+ super(message, { code: "VALIDATION_FAILED", category: "validation", ...options });
241
+ this.name = "UnprocessableEntityError";
242
+ }
243
+ };
244
+ var RateLimitError = class extends AutotaskError {
245
+ /** Seconds the server asked the client to wait (`Retry-After`), when present. */
246
+ retryAfter;
247
+ /** True when the call was refused locally (the tenant cooldown gate) and never sent. */
248
+ notSent;
249
+ constructor(message, options = {}) {
250
+ super(message, { code: "RATE_LIMITED", category: "rate_limit", retryable: true, ...options });
251
+ this.name = "RateLimitError";
252
+ }
253
+ };
254
+ var ServerError = class extends AutotaskError {
255
+ constructor(message, options = {}) {
256
+ super(message, { code: "SERVER_ERROR", category: "server", retryable: true, ...options });
257
+ this.name = "ServerError";
258
+ }
259
+ };
260
+ var UnexpectedContentTypeError = class extends AutotaskError {
261
+ constructor(message, options = {}) {
262
+ super(message, { code: "UNEXPECTED_CONTENT_TYPE", category: "server", retryable: true, ...options });
263
+ this.name = "UnexpectedContentTypeError";
264
+ }
265
+ };
266
+ var EmptyResponseError = class extends AutotaskError {
267
+ constructor(message, options = {}) {
268
+ super(message, { code: "EMPTY_RESPONSE", category: "server", ...options });
269
+ this.name = "EmptyResponseError";
270
+ }
271
+ };
272
+ var CircuitBreakerError = class extends AutotaskError {
273
+ /** Seconds remaining in the open period (absent when the half-open probe budget is in flight). */
274
+ retryAfter;
275
+ /** True when the call was refused locally (the circuit is open) and never sent. */
276
+ notSent;
277
+ constructor(message, options = {}) {
278
+ super(message, { code: "CIRCUIT_BREAKER_OPEN", category: "policy", retryable: true, ...options });
279
+ this.name = "CircuitBreakerError";
280
+ }
281
+ };
282
+ var HostPolicyDeniedError = class extends AutotaskError {
283
+ constructor(message, options = {}) {
284
+ super(message, { code: "HOST_POLICY_DENIED", category: "policy", retryable: false, ...options });
285
+ this.name = "HostPolicyDeniedError";
286
+ }
287
+ };
288
+ var ResolutionError = class extends AutotaskError {
289
+ constructor(message, options = {}) {
290
+ super(message, {
291
+ code: options.code === void 0 ? "RESOLUTION_AMBIGUOUS" : options.code,
292
+ category: "resolution",
293
+ ...options
294
+ });
295
+ this.name = "ResolutionError";
296
+ }
297
+ };
298
+ var PaginationLimitError = class extends AutotaskError {
299
+ constructor(message, options = {}) {
300
+ super(message, {
301
+ code: "PAGINATION_LIMIT",
302
+ category: "policy",
303
+ suggestedAction: options.suggestedAction ?? "Narrow the query with a tighter filter so the walk stays under the 1000-page limit.",
304
+ ...options
305
+ });
306
+ this.name = "PaginationLimitError";
307
+ }
308
+ };
309
+ var QueryTooLongError = class extends AutotaskError {
310
+ constructor(message, options = {}) {
311
+ super(message, {
312
+ code: "QUERY_TOO_LONG",
313
+ category: "policy",
314
+ suggestedAction: options.suggestedAction ?? 'Use the default POST query path (omit via: "get") for filters above the 2048-char GET cap.',
315
+ ...options
316
+ });
317
+ this.name = "QueryTooLongError";
318
+ }
319
+ };
320
+ var MISSING_REQUIRED_FIELD = /^Missing Required Field:\s*([A-Za-z_][A-Za-z0-9_]*)/;
321
+ function parseVendorErrors(body) {
322
+ if (!isRecord(body) || !Array.isArray(body.errors)) return void 0;
323
+ const out = [];
324
+ for (const entry of body.errors) {
325
+ if (typeof entry === "string") {
326
+ const item2 = { message: entry };
327
+ const m = MISSING_REQUIRED_FIELD.exec(entry);
328
+ if (m) item2.field = m[1];
329
+ out.push(item2);
330
+ continue;
331
+ }
332
+ if (!isRecord(entry)) continue;
333
+ const item = {};
334
+ if (typeof entry.message === "string") item.message = entry.message;
335
+ if (typeof entry.field === "string") item.field = entry.field;
336
+ if (typeof entry.code === "string") item.code = entry.code;
337
+ if (item.message !== void 0 || item.field !== void 0 || item.code !== void 0) {
338
+ out.push(item);
339
+ }
340
+ }
341
+ return out;
342
+ }
343
+ var DETERMINISTIC_VENDOR_500_PHRASES = [/exceeds maximum length/i];
344
+ function isDeterministicVendor500(entries) {
345
+ if (entries === void 0) return false;
346
+ return entries.some(
347
+ (entry) => entry.message !== void 0 && DETERMINISTIC_VENDOR_500_PHRASES.some((phrase) => phrase.test(entry.message))
348
+ );
349
+ }
350
+ function formatVendorEntry(entry) {
351
+ const parts = [];
352
+ if (entry.code) parts.push(`[${entry.code}]`);
353
+ if (entry.field) parts.push(`Field '${entry.field}':`);
354
+ if (entry.message) parts.push(entry.message);
355
+ return parts.join(" ");
356
+ }
357
+ function formatVendorDetail(body) {
358
+ const entries = parseVendorErrors(body);
359
+ if (entries && entries.length > 0) {
360
+ const formatted = entries.map(formatVendorEntry).filter((line) => line.length > 0);
361
+ if (formatted.length > 0) return formatted.join(" | ");
362
+ }
363
+ if (isRecord(body) && typeof body.message === "string" && body.message.trim().length > 0) {
364
+ return body.message.trim();
365
+ }
366
+ return void 0;
367
+ }
368
+ function isMachineCode(code) {
369
+ return /^[A-Za-z0-9][A-Za-z0-9_-]{2,}$/.test(code) && /\d/.test(code);
370
+ }
371
+ function errorClassForStatus(status) {
372
+ switch (status) {
373
+ case 400:
374
+ return BadRequestError;
375
+ case 401:
376
+ return UnauthorizedError;
377
+ case 403:
378
+ return ForbiddenError;
379
+ case 404:
380
+ return NotFoundError;
381
+ case 409:
382
+ case 412:
383
+ return ConflictError;
384
+ case 422:
385
+ return UnprocessableEntityError;
386
+ case 429:
387
+ return RateLimitError;
388
+ default:
389
+ return status >= 500 && status < 600 ? ServerError : null;
390
+ }
391
+ }
392
+ function statusDefaultMessage(status) {
393
+ switch (status) {
394
+ case 401:
395
+ return "Authentication failed: Invalid credentials";
396
+ case 404:
397
+ return "Resource not found";
398
+ default:
399
+ return `Autotask API request failed with status ${status}`;
400
+ }
401
+ }
402
+ var VENDOR_BODY_SUFFIX_LIMIT = 200;
403
+ function vendorBodySuffix(body) {
404
+ const rendered = typeof body === "string" ? body : JSON.stringify(body);
405
+ if (rendered === void 0 || rendered === "null" || rendered === "{}" || rendered === "[]") return void 0;
406
+ return rendered.length > VENDOR_BODY_SUFFIX_LIMIT ? rendered.slice(0, VENDOR_BODY_SUFFIX_LIMIT) : rendered;
407
+ }
408
+ var DIAGNOSTIC_SHAPE_ANOMALY = "sent value shows shell-quoting/line-break artifacts \u2014 check your env load";
409
+ var DIAGNOSTIC_NO_ANOMALY = "sent value shows no shape anomalies \u2014 credentials or account state are the likely cause";
410
+ var CREDENTIAL_SHAPE_POINTER = "Load the credential the way the file documents: shell-sourced (`set -a; . ./.env; set +a`) or via loadAutotaskCredentialsFromEnv() from node-autotask \u2014 a quote-preserving parser hands the value over with its quotes intact, which the vendor rejects as invalid credentials.";
411
+ function credentialDiagnosticDetails(credential) {
412
+ if (credential === void 0) return void 0;
413
+ const diagnostic = credential.allValuesClean ? DIAGNOSTIC_NO_ANOMALY : `${DIAGNOSTIC_SHAPE_ANOMALY} (${credential.problem ?? "shape anomaly detected"})`;
414
+ return { diagnostic, pointerNote: CREDENTIAL_SHAPE_POINTER };
415
+ }
416
+ function mergeDetails(base, extra) {
417
+ if (base === void 0) return extra;
418
+ if (extra === void 0) return base;
419
+ return { ...base, ...extra };
420
+ }
421
+ function suggestedActionForStatus(status) {
422
+ switch (status) {
423
+ case 400:
424
+ case 422:
425
+ return "Fix the request payload; see the vendor error for the offending field.";
426
+ case 401:
427
+ return "Check the API integration code, username and secret (3-header auth).";
428
+ case 403:
429
+ return "Verify permissions; if ImpersonationResourceId is set, retry without it.";
430
+ case 404:
431
+ return "Verify the id, or resolve the record by name first.";
432
+ case 409:
433
+ case 412:
434
+ return "Re-read the record and retry with current values.";
435
+ case 429:
436
+ return "Back off and retry; honour Retry-After.";
437
+ default:
438
+ return status >= 500 ? "Retry with backoff; the server error is transient." : void 0;
439
+ }
440
+ }
441
+ function errorFromStatus(status, body, url, operation, redactPersonalData = true, credential) {
442
+ const safeBody = redactResponseBody(body, { redactPersonalData });
443
+ let detail = formatVendorDetail(safeBody);
444
+ if (detail !== void 0 && detail.length > VENDOR_BODY_SUFFIX_LIMIT) {
445
+ detail = detail.slice(0, VENDOR_BODY_SUFFIX_LIMIT);
446
+ }
447
+ const suffix = detail === void 0 ? vendorBodySuffix(safeBody) : void 0;
448
+ const message = detail ?? (suffix !== void 0 ? `${statusDefaultMessage(status)}: ${suffix}` : statusDefaultMessage(status));
449
+ const mapped = errorClassForStatus(status);
450
+ const Ctor = mapped ?? AutotaskError;
451
+ const code = mapped !== null ? defaultCodeForClass(mapped) : `HTTP_${status}`;
452
+ const entries = parseVendorErrors(safeBody);
453
+ const vendorCodeCandidate = (isRecord(safeBody) && typeof safeBody.code === "string" ? safeBody.code : void 0) ?? (entries?.[0]?.code ?? void 0);
454
+ const credentialDetails = status === 401 ? credentialDiagnosticDetails(credential) : void 0;
455
+ const extraDetails = mergeDetails(void 0, credentialDetails);
456
+ const err = new Ctor(message, {
457
+ code: vendorCodeCandidate !== void 0 && isMachineCode(vendorCodeCandidate) ? vendorCodeCandidate : code,
458
+ httpStatus: status,
459
+ vendorError: safeBody,
460
+ url,
461
+ operation,
462
+ retryable: status === 429 || status >= 500,
463
+ suggestedAction: suggestedActionForStatus(status),
464
+ ...extraDetails !== void 0 ? { details: extraDetails } : {}
465
+ });
466
+ if (Ctor === AutotaskError) err.name = "AutotaskError";
467
+ if (status >= 500 && isDeterministicVendor500(entries)) {
468
+ err.retryable = false;
469
+ err.details = mergeDetails(err.details, { deterministic: true });
470
+ const dot = operation !== void 0 ? operation.indexOf(".") : -1;
471
+ const resource = operation !== void 0 && dot > 0 ? operation.slice(0, dot) : void 0;
472
+ err.suggestedAction = "Deterministic field rejection \u2014 fix the value, do not retry. " + (resource !== void 0 ? `Consult ${resource}.getFieldInfo for the field catalog (types, required, maxLength).` : "Consult the resource's getFieldInfo for the field catalog (types, required, maxLength).");
473
+ }
474
+ return err;
475
+ }
476
+ var StaleObjectError = class extends AutotaskError {
477
+ constructor(message, options = {}) {
478
+ super(message, {
479
+ code: "STALE_OBJECT",
480
+ category: "conflict",
481
+ suggestedAction: "Re-read the record and retry with the fresh lastModifiedDate.",
482
+ ...options
483
+ });
484
+ this.name = "StaleObjectError";
485
+ }
486
+ };
487
+ var ConfirmationRequiredError = class extends AutotaskError {
488
+ constructor(message, options = {}) {
489
+ super(message, { code: "CONFIRMATION_REQUIRED", category: "policy", ...options });
490
+ this.name = "ConfirmationRequiredError";
491
+ }
492
+ };
493
+ var DeleteBoundaryError = class extends AutotaskError {
494
+ constructor(message, options = {}) {
495
+ super(message, { code: "DELETE_BOUNDARY", category: "policy", ...options });
496
+ this.name = "DeleteBoundaryError";
497
+ }
498
+ };
499
+ var MarkerUnsafeIdError = class extends AutotaskError {
500
+ constructor(message, options = {}) {
501
+ super(message, { code: "MARKER_UNSAFE_ID", category: "policy", ...options });
502
+ this.name = "MarkerUnsafeIdError";
503
+ }
504
+ };
505
+ var RecoveryTruncatedError = class extends AutotaskError {
506
+ constructor(message, options = {}) {
507
+ super(message, { code: "RECOVERY_TRUNCATED", category: "policy", ...options });
508
+ this.name = "RecoveryTruncatedError";
509
+ }
510
+ };
511
+ function defaultCodeForClass(Ctor) {
512
+ switch (Ctor) {
513
+ case BadRequestError:
514
+ return "BAD_REQUEST";
515
+ case UnauthorizedError:
516
+ return "UNAUTHORIZED";
517
+ case ForbiddenError:
518
+ return "FORBIDDEN";
519
+ case NotFoundError:
520
+ return "NOT_FOUND";
521
+ case ConflictError:
522
+ return "CONFLICT";
523
+ case UnprocessableEntityError:
524
+ return "VALIDATION_FAILED";
525
+ case RateLimitError:
526
+ return "RATE_LIMITED";
527
+ default:
528
+ return "SERVER_ERROR";
529
+ }
530
+ }
531
+ function isAutotaskError(err) {
532
+ return err instanceof AutotaskError;
533
+ }
534
+
535
+ // src/credential-shape.ts
536
+ var QUOTE_CHARS = /* @__PURE__ */ new Set(["'", '"']);
537
+ function hasSurroundingQuotes(value) {
538
+ if (value.length < 2) return false;
539
+ const first = value[0];
540
+ return QUOTE_CHARS.has(first) && value[value.length - 1] === first;
541
+ }
542
+ function credentialShapeProblem(value, options = {}) {
543
+ if (!options.skipEdgeWhitespace) {
544
+ if (/^\s/.test(value)) return "leading whitespace";
545
+ if (/\s$/.test(value)) return "trailing whitespace";
546
+ }
547
+ if (!options.skipSurroundingQuotes && hasSurroundingQuotes(value)) {
548
+ return "single- or double-quote surrounding the value";
549
+ }
550
+ if (value.includes("\r")) return "carriage return";
551
+ return void 0;
552
+ }
553
+ var CREDENTIAL_ENV_LOAD_ACTION = "value appears shell-quoted or line-break contaminated \u2014 check how your env file was loaded (single-quoted values must be shell-sourced, not parsed by a quote-preserving loader)";
554
+ var MIN_PREFIX_SUMMARY_LENGTH = 4;
555
+ function credentialValueSummary(value) {
556
+ return value.length >= MIN_PREFIX_SUMMARY_LENGTH ? `len ${value.length}, starts "${value.slice(0, 2)}"` : "short value (length withheld)";
557
+ }
12
558
 
13
559
  // src/fieldCache.ts
14
560
  var FIELD_CACHE_MAX_ENTITIES = 256;
@@ -838,11 +1384,11 @@ function resolveConfig(config) {
838
1384
 
839
1385
  // src/utils/index.ts
840
1386
  import { randomUUID } from "crypto";
841
- function isRecord(value) {
1387
+ function isRecord2(value) {
842
1388
  return typeof value === "object" && value !== null && !Array.isArray(value);
843
1389
  }
844
1390
  function assertRecord(value, label) {
845
- if (!isRecord(value)) {
1391
+ if (!isRecord2(value)) {
846
1392
  const got = value === null ? "null" : Array.isArray(value) ? "array" : typeof value;
847
1393
  throw new AutotaskError(`Expected ${label} to be an object, got ${got}`, {
848
1394
  code: "INVALID_RESPONSE",
@@ -853,7 +1399,7 @@ function assertRecord(value, label) {
853
1399
  return value;
854
1400
  }
855
1401
  function isMergeable(value) {
856
- return isRecord(value);
1402
+ return isRecord2(value);
857
1403
  }
858
1404
  function deepMergeParams(base, override) {
859
1405
  const out = { ...base };
@@ -887,7 +1433,7 @@ function flattenUdfs(record, options) {
887
1433
  const udfs = record.userDefinedFields;
888
1434
  if (!Array.isArray(udfs)) return out;
889
1435
  for (const udf of udfs) {
890
- if (!isRecord(udf) || typeof udf.name !== "string" || udf.name === "") continue;
1436
+ if (!isRecord2(udf) || typeof udf.name !== "string" || udf.name === "") continue;
891
1437
  const target = udf.name in out ? `${prefix}${udf.name}` : udf.name;
892
1438
  if (!(target in out)) out[target] = udf.value;
893
1439
  }
@@ -899,7 +1445,7 @@ function newCorrelationId() {
899
1445
  var CREDENTIAL_KEY_RE = /(^|[_\-.])(secret|secrets|password|passwd|passphrase|token|tokens|apikey|api_key|apiintegrationcode|credentials?|accesskey|access_key|authcode|authorization)([_\-.]|$)|([A-Za-z0-9])(secrets?|tokens?|password|passwd|passphrase|credentials?|keys?|apiintegrationcode)(?=$)/i;
900
1446
  function redactAuditBody(value) {
901
1447
  if (Array.isArray(value)) return value.map((entry) => redactAuditBody(entry));
902
- if (!isRecord(value)) return value;
1448
+ if (!isRecord2(value)) return value;
903
1449
  const out = {};
904
1450
  for (const [key, entry] of Object.entries(value)) {
905
1451
  out[key] = CREDENTIAL_KEY_RE.test(key) ? "[REDACTED]" : redactAuditBody(entry);
@@ -12850,6 +13396,54 @@ var CREATE_GUIDANCE = {
12850
13396
  };
12851
13397
 
12852
13398
  export {
13399
+ NoopLogger,
13400
+ REDACTED,
13401
+ CREDENTIAL_HEADER_NAMES,
13402
+ isCredentialHeaderName,
13403
+ redactRecord,
13404
+ SECRET_RESPONSE_FIELDS,
13405
+ PERSONAL_RESPONSE_FIELDS,
13406
+ CONFIDENTIAL_RESPONSE_FIELDS,
13407
+ isRedactedFieldName,
13408
+ redactResponseBody,
13409
+ AutotaskError,
13410
+ AutotaskConfigError,
13411
+ AutotaskNetworkError,
13412
+ AutotaskTimeoutError,
13413
+ AutotaskAbortError,
13414
+ ResponseRejectedError,
13415
+ BadRequestError,
13416
+ UnauthorizedError,
13417
+ ForbiddenError,
13418
+ NotFoundError,
13419
+ ConflictError,
13420
+ UnprocessableEntityError,
13421
+ RateLimitError,
13422
+ ServerError,
13423
+ UnexpectedContentTypeError,
13424
+ EmptyResponseError,
13425
+ CircuitBreakerError,
13426
+ HostPolicyDeniedError,
13427
+ ResolutionError,
13428
+ PaginationLimitError,
13429
+ QueryTooLongError,
13430
+ parseVendorErrors,
13431
+ isDeterministicVendor500,
13432
+ formatVendorDetail,
13433
+ CREDENTIAL_SHAPE_POINTER,
13434
+ mergeDetails,
13435
+ errorFromStatus,
13436
+ StaleObjectError,
13437
+ ConfirmationRequiredError,
13438
+ DeleteBoundaryError,
13439
+ MarkerUnsafeIdError,
13440
+ RecoveryTruncatedError,
13441
+ isAutotaskError,
13442
+ hasSurroundingQuotes,
13443
+ credentialShapeProblem,
13444
+ CREDENTIAL_ENV_LOAD_ACTION,
13445
+ MIN_PREFIX_SUMMARY_LENGTH,
13446
+ credentialValueSummary,
12853
13447
  FIELD_CACHE_KINDS,
12854
13448
  DEFAULT_CIRCUIT_BREAKER_WINDOW_MS,
12855
13449
  DEFAULT_CIRCUIT_BREAKER_FAILURE_THRESHOLD,
@@ -12885,7 +13479,7 @@ export {
12885
13479
  zoneUrl,
12886
13480
  resolveBaseUrl,
12887
13481
  resolveConfig,
12888
- isRecord,
13482
+ isRecord2 as isRecord,
12889
13483
  assertRecord,
12890
13484
  deepMergeParams,
12891
13485
  parseIdentifier,
@@ -12912,4 +13506,4 @@ export {
12912
13506
  collectAll,
12913
13507
  toArray
12914
13508
  };
12915
- //# sourceMappingURL=chunk-GNS47RBV.js.map
13509
+ //# sourceMappingURL=chunk-IUJJPWUX.js.map