node-autotask 0.15.0 → 0.17.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/ARCHITECTURE.md +8 -0
- package/N8N_MCP_MANIFEST.json +2 -2
- package/N8N_MCP_MANIFEST.md +3 -3
- package/README.md +40 -2
- package/capabilities.json +4 -4
- package/dist/capabilities.cjs +2 -2
- package/dist/capabilities.js +1 -1
- package/dist/{chunk-Z7YT72SS.cjs → chunk-4RJG73GY.cjs} +164 -81
- package/dist/{chunk-EWMG3HHU.cjs → chunk-5ZGXWS4E.cjs} +3 -3
- package/dist/{chunk-JIDR3NPV.js → chunk-6SP6GJJR.js} +3 -3
- package/dist/{chunk-V7QPZA7L.cjs → chunk-DQIZUZWP.cjs} +135 -135
- package/dist/{chunk-AXVYQPTB.js → chunk-EGR2ZLYM.js} +49 -39
- package/dist/{chunk-CSA6QSV6.js → chunk-HC6YWPIY.js} +104 -21
- package/dist/{chunk-VLIUHEPE.cjs → chunk-NQVD2XLH.cjs} +429 -419
- package/dist/{chunk-LH5BXQ7A.js → chunk-UV2PECAL.js} +2 -2
- package/dist/{index-M0qJIiRt.d.ts → index-BAFdPxjG.d.ts} +88 -7
- package/dist/{index-BXIwhAN4.d.cts → index-CjC0oWZX.d.cts} +88 -7
- package/dist/index.cjs +26 -22
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +8 -4
- package/dist/operations/index.cjs +103 -103
- package/dist/operations/index.d.cts +12 -6
- package/dist/operations/index.d.ts +12 -6
- package/dist/operations/index.js +2 -2
- package/dist/pool.cjs +8 -8
- package/dist/pool.d.cts +1 -1
- package/dist/pool.d.ts +1 -1
- package/dist/pool.js +4 -4
- package/dist/resources/index.cjs +4 -4
- package/dist/resources/index.d.cts +1 -1
- package/dist/resources/index.d.ts +1 -1
- package/dist/resources/index.js +3 -3
- package/docs/API.md +55 -9
- package/examples/basic.ts +2 -2
- package/examples/mcp-server-http.ts +5 -4
- package/examples/mcp-server.ts +3 -3
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
CAPABILITIES
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-6SP6GJJR.js";
|
|
4
4
|
|
|
5
5
|
// src/auth.ts
|
|
6
6
|
var AUTH_HEADERS = {
|
|
@@ -171,6 +171,7 @@ var AutotaskError = class extends Error {
|
|
|
171
171
|
operation;
|
|
172
172
|
httpStatus;
|
|
173
173
|
vendorError;
|
|
174
|
+
/** Legacy unlabeled id list (issue #116) — see `AutotaskErrorOptions.resourceIds`; the labeled companion rides on `details.idRefs`. */
|
|
174
175
|
resourceIds;
|
|
175
176
|
suggestedAction;
|
|
176
177
|
correlationId;
|
|
@@ -1532,6 +1533,8 @@ var DEFAULT_PAGE_SIZE = 10;
|
|
|
1532
1533
|
var MAX_PAGE_SIZE = 500;
|
|
1533
1534
|
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
1534
1535
|
var DEFAULT_MAX_RETRIES = 3;
|
|
1536
|
+
var DEFAULT_MAINTENANCE_MAX_WAIT_MS = 36e5;
|
|
1537
|
+
var DEFAULT_MAINTENANCE_POLL_MS = 3e4;
|
|
1535
1538
|
var DEFAULT_MAX_CONCURRENT = 10;
|
|
1536
1539
|
var DEFAULT_MAX_RESPONSE_BYTES = 64 * 1024 * 1024;
|
|
1537
1540
|
var DEFAULT_MAX_SCAN_RECORDS = 500;
|
|
@@ -1689,6 +1692,14 @@ function resolveConfig(config) {
|
|
|
1689
1692
|
if (!Number.isInteger(maxRetries) || maxRetries < 0) {
|
|
1690
1693
|
throw new AutotaskConfigError("maxRetries must be a non-negative integer");
|
|
1691
1694
|
}
|
|
1695
|
+
const maintenanceMaxWaitMs = config.maintenanceMaxWaitMs ?? DEFAULT_MAINTENANCE_MAX_WAIT_MS;
|
|
1696
|
+
if (!Number.isInteger(maintenanceMaxWaitMs) || maintenanceMaxWaitMs < 0) {
|
|
1697
|
+
throw new AutotaskConfigError("maintenanceMaxWaitMs must be a non-negative integer");
|
|
1698
|
+
}
|
|
1699
|
+
const maintenancePollMs = config.maintenancePollMs ?? DEFAULT_MAINTENANCE_POLL_MS;
|
|
1700
|
+
if (!Number.isInteger(maintenancePollMs) || maintenancePollMs <= 0) {
|
|
1701
|
+
throw new AutotaskConfigError("maintenancePollMs must be a positive integer");
|
|
1702
|
+
}
|
|
1692
1703
|
const maxConcurrent = config.maxConcurrent ?? (agent ? AGENT_PROFILE.maxConcurrent : DEFAULT_MAX_CONCURRENT);
|
|
1693
1704
|
if (!Number.isInteger(maxConcurrent) || maxConcurrent <= 0) {
|
|
1694
1705
|
throw new AutotaskConfigError("maxConcurrent must be a positive integer");
|
|
@@ -1822,6 +1833,8 @@ function resolveConfig(config) {
|
|
|
1822
1833
|
pageSize: pageSizeCapped,
|
|
1823
1834
|
timeoutMs,
|
|
1824
1835
|
maxRetries,
|
|
1836
|
+
maintenanceMaxWaitMs,
|
|
1837
|
+
maintenancePollMs,
|
|
1825
1838
|
maxConcurrent,
|
|
1826
1839
|
maxResponseBytes,
|
|
1827
1840
|
logger,
|
|
@@ -14755,6 +14768,10 @@ function backoffDelay(attempt) {
|
|
|
14755
14768
|
const exp = Math.min(BACKOFF_CAP_MS, BACKOFF_BASE_MS * 2 ** attempt);
|
|
14756
14769
|
return Math.floor(exp / 2 + Math.random() * (exp / 2));
|
|
14757
14770
|
}
|
|
14771
|
+
function isMaintenancePage(err) {
|
|
14772
|
+
const snippet = typeof err.details?.bodySnippet === "string" ? err.details.bodySnippet : "";
|
|
14773
|
+
return /maintenance|temporarily unavailable|service unavailable|outage|downtime|down for/i.test(snippet);
|
|
14774
|
+
}
|
|
14758
14775
|
function parseRetryAfter(value) {
|
|
14759
14776
|
if (value === null || value.length === 0) return void 0;
|
|
14760
14777
|
const trimmed = value.trim();
|
|
@@ -14851,6 +14868,9 @@ var HttpClient = class {
|
|
|
14851
14868
|
update;
|
|
14852
14869
|
auth;
|
|
14853
14870
|
maxRetries;
|
|
14871
|
+
/** Maintenance-window auto-retry budget + poll (issue #112 follow-up; see config). */
|
|
14872
|
+
maintenanceMaxWaitMs;
|
|
14873
|
+
maintenancePollMs;
|
|
14854
14874
|
maxResponseBytes;
|
|
14855
14875
|
perHour;
|
|
14856
14876
|
/** The resolved profile (issue #4) — the agent profile changes the
|
|
@@ -14921,6 +14941,8 @@ var HttpClient = class {
|
|
|
14921
14941
|
impersonationResourceId: config.impersonationResourceId
|
|
14922
14942
|
};
|
|
14923
14943
|
this.maxRetries = config.maxRetries;
|
|
14944
|
+
this.maintenanceMaxWaitMs = config.maintenanceMaxWaitMs;
|
|
14945
|
+
this.maintenancePollMs = config.maintenancePollMs;
|
|
14924
14946
|
this.maxResponseBytes = config.maxResponseBytes;
|
|
14925
14947
|
this.perHour = config.rateLimit?.perHour;
|
|
14926
14948
|
this.tenantKey = buildTenantKey(config.userName, config.baseUrl);
|
|
@@ -15180,11 +15202,21 @@ var HttpClient = class {
|
|
|
15180
15202
|
const correlationId = newCorrelationId();
|
|
15181
15203
|
const timeoutMs = opts.timeoutMs ?? this.timeoutMs;
|
|
15182
15204
|
const maxRetries = opts.maxRetries ?? this.maxRetries;
|
|
15205
|
+
const maintenanceMaxWaitMs = opts.maintenanceMaxWaitMs ?? this.maintenanceMaxWaitMs;
|
|
15206
|
+
const maintenancePollMs = opts.maintenancePollMs ?? this.maintenancePollMs;
|
|
15207
|
+
let firstMaintenanceAt;
|
|
15208
|
+
let maintenanceWaits = 0;
|
|
15183
15209
|
const started = Date.now();
|
|
15184
|
-
|
|
15210
|
+
let deadlineAt = started + timeoutMs;
|
|
15185
15211
|
const callerSignal = opts.signal ?? ambientSignal.getStore();
|
|
15212
|
+
const deadline = new AbortController();
|
|
15213
|
+
let deadlineTimer = setTimeout(() => deadline.abort(), timeoutMs);
|
|
15214
|
+
const armDeadline = (ms) => {
|
|
15215
|
+
clearTimeout(deadlineTimer);
|
|
15216
|
+
deadlineTimer = setTimeout(() => deadline.abort(), Math.max(0, ms));
|
|
15217
|
+
};
|
|
15186
15218
|
const signal = AbortSignal.any(
|
|
15187
|
-
callerSignal === void 0 ? [
|
|
15219
|
+
callerSignal === void 0 ? [deadline.signal] : [deadline.signal, callerSignal]
|
|
15188
15220
|
);
|
|
15189
15221
|
const auditBody = opts.body !== void 0 ? redactAuditBody(opts.body) : void 0;
|
|
15190
15222
|
const retriable = opts.idempotent ?? IDEMPOTENT_METHODS.includes(method);
|
|
@@ -15202,9 +15234,9 @@ var HttpClient = class {
|
|
|
15202
15234
|
}
|
|
15203
15235
|
};
|
|
15204
15236
|
if (this.cooldownStore !== void 0 && !opts.bypassCooldown) {
|
|
15205
|
-
const
|
|
15206
|
-
if (
|
|
15207
|
-
const remainingMs =
|
|
15237
|
+
const deadline2 = this.cooldownStore.get(this.tenantKey);
|
|
15238
|
+
if (deadline2 !== void 0) {
|
|
15239
|
+
const remainingMs = deadline2 - Date.now();
|
|
15208
15240
|
if (remainingMs > 0) {
|
|
15209
15241
|
if (callerSignal?.aborted) throw interrupted();
|
|
15210
15242
|
const seconds = Math.ceil(remainingMs / 1e3);
|
|
@@ -15394,6 +15426,23 @@ var HttpClient = class {
|
|
|
15394
15426
|
httpStatus: status,
|
|
15395
15427
|
suggestedAction: "The zone returned a non-JSON 2xx body; verify baseUrl points at the REST API."
|
|
15396
15428
|
});
|
|
15429
|
+
if (err2.code === "UNEXPECTED_CONTENT_TYPE" && maintenanceMaxWaitMs > 0 && isMaintenancePage(err2)) {
|
|
15430
|
+
const now = Date.now();
|
|
15431
|
+
firstMaintenanceAt ??= now;
|
|
15432
|
+
const remaining = maintenanceMaxWaitMs - (now - firstMaintenanceAt);
|
|
15433
|
+
if (remaining > 0) {
|
|
15434
|
+
const wait = Math.min(maintenancePollMs, remaining);
|
|
15435
|
+
maintenanceWaits += 1;
|
|
15436
|
+
this.logger.warn?.(
|
|
15437
|
+
`[autotask] maintenance window on ${method} ${opts.path}; waiting ${Math.round(wait / 1e3)}s (retry ${maintenanceWaits}, up to ${Math.round(maintenanceMaxWaitMs / 6e4)} min)`,
|
|
15438
|
+
correlationId
|
|
15439
|
+
);
|
|
15440
|
+
deadlineAt = Date.now() + wait + timeoutMs;
|
|
15441
|
+
armDeadline(deadlineAt - Date.now());
|
|
15442
|
+
await pause(wait);
|
|
15443
|
+
continue;
|
|
15444
|
+
}
|
|
15445
|
+
}
|
|
15397
15446
|
if (err2.code === "UNEXPECTED_CONTENT_TYPE" && retriable5xx && attempt < maxRetries) {
|
|
15398
15447
|
const retryAfter = parseRetryAfter(res.headers.get("Retry-After"));
|
|
15399
15448
|
const delay = retryAfter !== void 0 ? retryAfter * 1e3 : backoffDelay(attempt);
|
|
@@ -15407,6 +15456,18 @@ var HttpClient = class {
|
|
|
15407
15456
|
continue;
|
|
15408
15457
|
}
|
|
15409
15458
|
}
|
|
15459
|
+
if (maintenanceWaits > 0 && err2.code === "UNEXPECTED_CONTENT_TYPE") {
|
|
15460
|
+
err2.details = {
|
|
15461
|
+
...err2.details ?? {},
|
|
15462
|
+
maintenance: {
|
|
15463
|
+
waits: maintenanceWaits,
|
|
15464
|
+
waitedMs: Date.now() - (firstMaintenanceAt ?? Date.now()),
|
|
15465
|
+
maxWaitMs: maintenanceMaxWaitMs,
|
|
15466
|
+
exhausted: true
|
|
15467
|
+
}
|
|
15468
|
+
};
|
|
15469
|
+
err2.suggestedAction = `The zone stayed in a maintenance window for ${Math.round(maintenanceMaxWaitMs / 6e4)} min \u2014 the SDK waited and retried ${maintenanceWaits} time(s) and gave up. Re-run after the window (typically under 30 min), or raise maintenanceMaxWaitMs.`;
|
|
15470
|
+
}
|
|
15410
15471
|
attachEmptyReasonDetails(err2);
|
|
15411
15472
|
throw err2;
|
|
15412
15473
|
}
|
|
@@ -15534,6 +15595,7 @@ var HttpClient = class {
|
|
|
15534
15595
|
}
|
|
15535
15596
|
throw err;
|
|
15536
15597
|
} finally {
|
|
15598
|
+
clearTimeout(deadlineTimer);
|
|
15537
15599
|
if (!(method === "GET" || opts.idempotent === true)) {
|
|
15538
15600
|
this.readCache?.clear();
|
|
15539
15601
|
const nameOp = typeof opts.operation === "string" ? opts.operation : void 0;
|
|
@@ -15594,7 +15656,7 @@ function unwrapItems(body, operation) {
|
|
|
15594
15656
|
}
|
|
15595
15657
|
return { items: body.items, pageDetails: body.pageDetails };
|
|
15596
15658
|
}
|
|
15597
|
-
function unwrapItem(body, id, operation) {
|
|
15659
|
+
function unwrapItem(body, id, operation, entity) {
|
|
15598
15660
|
if (!isRecord2(body) || !("item" in body)) {
|
|
15599
15661
|
throw new AutotaskError("Expected the item envelope { item }; got a different shape", {
|
|
15600
15662
|
code: "INVALID_RESPONSE",
|
|
@@ -15610,6 +15672,7 @@ function unwrapItem(body, id, operation) {
|
|
|
15610
15672
|
{
|
|
15611
15673
|
httpStatus: 200,
|
|
15612
15674
|
resourceIds: id !== void 0 ? [id] : void 0,
|
|
15675
|
+
...id !== void 0 && entity !== void 0 ? { details: { idRefs: [{ entity, id }] } } : {},
|
|
15613
15676
|
operation,
|
|
15614
15677
|
suggestedAction: "Verify the id, or resolve the record by name first."
|
|
15615
15678
|
}
|
|
@@ -16930,6 +16993,19 @@ function decodeFileData(body) {
|
|
|
16930
16993
|
{ code: "INVALID_RESPONSE", category: "server" }
|
|
16931
16994
|
);
|
|
16932
16995
|
}
|
|
16996
|
+
var DEPLURAL_EXCEPTIONS = {
|
|
16997
|
+
tag_aliase: "tag_alias"
|
|
16998
|
+
};
|
|
16999
|
+
function entityNameFromPath(resourcePath) {
|
|
17000
|
+
const segment = resourcePath.slice(resourcePath.lastIndexOf("/") + 1);
|
|
17001
|
+
const snake = segment.replace(/([A-Z])/g, (_m, c, i) => (i > 0 ? `_` : "") + c.toLowerCase()).replace(/_{2,}/g, "_");
|
|
17002
|
+
if (snake.endsWith("ies")) return `${snake.slice(0, -3)}y`;
|
|
17003
|
+
if (snake.endsWith("s")) {
|
|
17004
|
+
const stripped = snake.slice(0, -1);
|
|
17005
|
+
return DEPLURAL_EXCEPTIONS[stripped] ?? stripped;
|
|
17006
|
+
}
|
|
17007
|
+
return snake;
|
|
17008
|
+
}
|
|
16933
17009
|
var BaseResource = class {
|
|
16934
17010
|
http;
|
|
16935
17011
|
config;
|
|
@@ -16939,6 +17015,10 @@ var BaseResource = class {
|
|
|
16939
17015
|
get resourcePath() {
|
|
16940
17016
|
return this.config.resourcePath;
|
|
16941
17017
|
}
|
|
17018
|
+
/** The vendor entity this resource's ids identify (issue #116 — the `details.idRefs` label, e.g. `company`). */
|
|
17019
|
+
get entityName() {
|
|
17020
|
+
return entityNameFromPath(this.resourcePath);
|
|
17021
|
+
}
|
|
16942
17022
|
/** Parent-scoped template (from the config); undefined for top-level resources. */
|
|
16943
17023
|
get parentScopedPath() {
|
|
16944
17024
|
return this.config.parentScopedPath;
|
|
@@ -17446,7 +17526,7 @@ var BaseResource = class {
|
|
|
17446
17526
|
async getGlobal(id, fresh = false) {
|
|
17447
17527
|
const numeric = parseIdentifier(id, "id");
|
|
17448
17528
|
const res = await this.cachedGet(`${this.resourcePath}/${numeric}`, `${this.config.key}.get`, fresh, this.config.key);
|
|
17449
|
-
return unwrapItem(res, numeric, `${this.config.key}.get
|
|
17529
|
+
return unwrapItem(res, numeric, `${this.config.key}.get`, this.entityName);
|
|
17450
17530
|
}
|
|
17451
17531
|
/**
|
|
17452
17532
|
* V2 merge read: a FRESH single-record GET that skips response redaction
|
|
@@ -17468,7 +17548,7 @@ var BaseResource = class {
|
|
|
17468
17548
|
skipResponseRedaction: true,
|
|
17469
17549
|
maxRetries: readOpts?.maxRetries
|
|
17470
17550
|
});
|
|
17471
|
-
return unwrapItem(res, numeric, `${this.config.key}.get
|
|
17551
|
+
return unwrapItem(res, numeric, `${this.config.key}.get`, this.entityName);
|
|
17472
17552
|
}
|
|
17473
17553
|
/**
|
|
17474
17554
|
* Issue #34 fallback merge read: a FRESH parent-scoped single-record GET
|
|
@@ -17487,13 +17567,13 @@ var BaseResource = class {
|
|
|
17487
17567
|
skipResponseRedaction: true,
|
|
17488
17568
|
maxRetries: readOpts?.maxRetries
|
|
17489
17569
|
});
|
|
17490
|
-
return unwrapItem(res, numeric, `${this.config.key}.get
|
|
17570
|
+
return unwrapItem(res, numeric, `${this.config.key}.get`, this.entityName);
|
|
17491
17571
|
}
|
|
17492
17572
|
/** Parent-scoped `GET .../{parentId}/<Child>/{id}` → `{ item }` unwrapped. */
|
|
17493
17573
|
async getScopedRaw(parentId, id, fresh = false) {
|
|
17494
17574
|
const numeric = parseIdentifier(id, "id");
|
|
17495
17575
|
const res = await this.cachedGet(this.scopedItemPath(parentId, id), `${this.config.key}.get`, fresh, this.config.key);
|
|
17496
|
-
return unwrapItem(res, numeric, `${this.config.key}.get
|
|
17576
|
+
return unwrapItem(res, numeric, `${this.config.key}.get`, this.entityName);
|
|
17497
17577
|
}
|
|
17498
17578
|
/**
|
|
17499
17579
|
* Attachment single-read: the vendor answers the model DIRECTLY (swagger),
|
|
@@ -17503,23 +17583,23 @@ var BaseResource = class {
|
|
|
17503
17583
|
async getScopedModel(parentId, id, fresh = false) {
|
|
17504
17584
|
const numeric = parseIdentifier(id, "id");
|
|
17505
17585
|
const res = await this.cachedGet(this.scopedItemPath(parentId, id), `${this.config.key}.get`, fresh, this.config.key);
|
|
17506
|
-
return this.unwrapModelLike(res, numeric);
|
|
17586
|
+
return this.unwrapModelLike(res, numeric, this.entityName);
|
|
17507
17587
|
}
|
|
17508
|
-
unwrapModelLike(body, id) {
|
|
17588
|
+
unwrapModelLike(body, id, entity) {
|
|
17509
17589
|
if (!isRecord2(body)) {
|
|
17510
17590
|
throw new AutotaskError(
|
|
17511
17591
|
`Expected an attachment model (or { item } / { items }) envelope for ID ${id}; got a different shape`,
|
|
17512
|
-
{ code: "INVALID_RESPONSE", category: "server", resourceIds: [id] }
|
|
17592
|
+
{ code: "INVALID_RESPONSE", category: "server", resourceIds: [id], details: { idRefs: [{ entity, id }] } }
|
|
17513
17593
|
);
|
|
17514
17594
|
}
|
|
17515
17595
|
if (Object.keys(body).length === 0) {
|
|
17516
|
-
throw new NotFoundError(`The record with ID ${id} was not found`, { httpStatus: 200, resourceIds: [id] });
|
|
17596
|
+
throw new NotFoundError(`The record with ID ${id} was not found`, { httpStatus: 200, resourceIds: [id], details: { idRefs: [{ entity, id }] } });
|
|
17517
17597
|
}
|
|
17518
|
-
if ("item" in body) return unwrapItem(body, id);
|
|
17598
|
+
if ("item" in body) return unwrapItem(body, id, void 0, entity);
|
|
17519
17599
|
if (Array.isArray(body.items)) {
|
|
17520
17600
|
const first = body.items[0];
|
|
17521
17601
|
if (first === null || first === void 0) {
|
|
17522
|
-
throw new NotFoundError(`The record with ID ${id} was not found`, { httpStatus: 200, resourceIds: [id] });
|
|
17602
|
+
throw new NotFoundError(`The record with ID ${id} was not found`, { httpStatus: 200, resourceIds: [id], details: { idRefs: [{ entity, id }] } });
|
|
17523
17603
|
}
|
|
17524
17604
|
return first;
|
|
17525
17605
|
}
|
|
@@ -17867,7 +17947,7 @@ var BaseResource = class {
|
|
|
17867
17947
|
if (!isRecord2(res)) {
|
|
17868
17948
|
throw new AutotaskError(
|
|
17869
17949
|
`Expected the deleted attachment model for ID ${numeric}; got a different shape`,
|
|
17870
|
-
{ code: "INVALID_RESPONSE", category: "server", resourceIds: [numeric] }
|
|
17950
|
+
{ code: "INVALID_RESPONSE", category: "server", resourceIds: [numeric], details: { idRefs: [{ entity: this.entityName, id: numeric }] } }
|
|
17871
17951
|
);
|
|
17872
17952
|
}
|
|
17873
17953
|
return res;
|
|
@@ -18258,9 +18338,9 @@ var BaseResource = class {
|
|
|
18258
18338
|
const resource = RESOURCE_KEY_BY_PATH[entityPath];
|
|
18259
18339
|
const res = await this.cachedGet(`${entityPath}/${numeric}`, `${this.config.key}.fetchRecord`, fresh, resource);
|
|
18260
18340
|
if (this.config.attachmentGet) {
|
|
18261
|
-
return this.unwrapModelLike(res, numeric);
|
|
18341
|
+
return this.unwrapModelLike(res, numeric, entityNameFromPath(entityPath));
|
|
18262
18342
|
}
|
|
18263
|
-
return unwrapItem(res, numeric, `${this.config.key}.fetchRecord
|
|
18343
|
+
return unwrapItem(res, numeric, `${this.config.key}.fetchRecord`, entityNameFromPath(entityPath));
|
|
18264
18344
|
}
|
|
18265
18345
|
/**
|
|
18266
18346
|
* `getContext` part fetch: a single-record read that degrades to `null`
|
|
@@ -18367,6 +18447,7 @@ var BaseResource = class {
|
|
|
18367
18447
|
{
|
|
18368
18448
|
code: "RESOLUTION_AMBIGUOUS",
|
|
18369
18449
|
resourceIds: ids,
|
|
18450
|
+
details: { idRefs: ids.map((cid) => ({ entity: this.entityName, id: cid })) },
|
|
18370
18451
|
// issue #59 PR-B: the miss-channel pointer (the generic floor names
|
|
18371
18452
|
// the resource's own search op — reachable only where searchFields
|
|
18372
18453
|
// is non-empty, so the named op always exists here).
|
|
@@ -18503,7 +18584,7 @@ var BaseResource = class {
|
|
|
18503
18584
|
const ids = matches.map((m) => m.id).filter((id) => typeof id === "number");
|
|
18504
18585
|
throw new ResolutionError(
|
|
18505
18586
|
`findBy${field}: "${value}" is ambiguous on ${this.config.key} (matched ${matches.length} records)`,
|
|
18506
|
-
{ code: "RESOLUTION_AMBIGUOUS", resourceIds: ids }
|
|
18587
|
+
{ code: "RESOLUTION_AMBIGUOUS", resourceIds: ids, details: { idRefs: ids.map((cid) => ({ entity: this.entityName, id: cid })) } }
|
|
18507
18588
|
);
|
|
18508
18589
|
}
|
|
18509
18590
|
return matches[0];
|
|
@@ -18606,6 +18687,8 @@ export {
|
|
|
18606
18687
|
MAX_PAGE_SIZE,
|
|
18607
18688
|
DEFAULT_TIMEOUT_MS,
|
|
18608
18689
|
DEFAULT_MAX_RETRIES,
|
|
18690
|
+
DEFAULT_MAINTENANCE_MAX_WAIT_MS,
|
|
18691
|
+
DEFAULT_MAINTENANCE_POLL_MS,
|
|
18609
18692
|
DEFAULT_MAX_CONCURRENT,
|
|
18610
18693
|
DEFAULT_MAX_RESPONSE_BYTES,
|
|
18611
18694
|
DEFAULT_MAX_SCAN_RECORDS,
|