node-autotask 0.27.0 → 0.28.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.
Files changed (37) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/MCP_TOOL_CATALOG.json +44 -8
  3. package/MCP_TOOL_MANIFEST.md +13 -11
  4. package/N8N_MCP_MANIFEST.json +55 -5
  5. package/N8N_MCP_MANIFEST.md +9 -7
  6. package/README.md +1 -1
  7. package/capabilities.json +1607 -1305
  8. package/dist/capabilities.cjs +2 -2
  9. package/dist/capabilities.js +1 -1
  10. package/dist/{chunk-F7FVOXPQ.js → chunk-6EQSXYRW.js} +1606 -1304
  11. package/dist/{chunk-ZJ4ONAQF.cjs → chunk-D5IVS6GY.cjs} +4 -4
  12. package/dist/{chunk-UHTZLUDE.cjs → chunk-GUY2KK7U.cjs} +143 -143
  13. package/dist/{chunk-42ZUISDH.js → chunk-IRVZGW5K.js} +1 -1
  14. package/dist/{chunk-GURC52J7.js → chunk-O2D7KDDD.js} +2 -2
  15. package/dist/{chunk-ZGTFVZL5.js → chunk-SYX6EWMT.js} +287 -78
  16. package/dist/{chunk-5GGCSRT4.cjs → chunk-WYKGD5UD.cjs} +795 -586
  17. package/dist/{chunk-5CC7LGNM.cjs → chunk-ZEGMVEF3.cjs} +1606 -1304
  18. package/dist/{index-D4XYtn4s.d.cts → index-CORcZUNm.d.cts} +79 -2
  19. package/dist/{index-BIWy9m-a.d.ts → index-DXuV04zM.d.ts} +79 -2
  20. package/dist/index.cjs +22 -22
  21. package/dist/index.d.cts +2 -2
  22. package/dist/index.d.ts +2 -2
  23. package/dist/index.js +4 -4
  24. package/dist/operations/index.cjs +112 -112
  25. package/dist/operations/index.d.cts +2 -2
  26. package/dist/operations/index.d.ts +2 -2
  27. package/dist/operations/index.js +2 -2
  28. package/dist/pool.cjs +9 -9
  29. package/dist/pool.d.cts +1 -1
  30. package/dist/pool.d.ts +1 -1
  31. package/dist/pool.js +4 -4
  32. package/dist/resources/index.cjs +4 -4
  33. package/dist/resources/index.d.cts +1 -1
  34. package/dist/resources/index.d.ts +1 -1
  35. package/dist/resources/index.js +3 -3
  36. package/docs/API.md +40 -2
  37. package/package.json +1 -1
@@ -38,10 +38,10 @@ import {
38
38
  unwrapItem,
39
39
  unwrapItems,
40
40
  unwrapOperation
41
- } from "./chunk-42ZUISDH.js";
41
+ } from "./chunk-IRVZGW5K.js";
42
42
  import {
43
43
  CAPABILITIES
44
- } from "./chunk-F7FVOXPQ.js";
44
+ } from "./chunk-6EQSXYRW.js";
45
45
 
46
46
  // src/resources/appointments.ts
47
47
  var AppointmentsResource = class extends BaseResource {
@@ -1291,6 +1291,82 @@ async function upsertByName(resource, companyName, params, opts, http) {
1291
1291
  return createWithDiscovery(resource, body, owner, caveat, opts);
1292
1292
  }
1293
1293
 
1294
+ // src/resources/special/_shared/ticketStatus.ts
1295
+ var ASSUMED_CLOSED_STATUS_ID = 5;
1296
+ var CLOSED_STATUS_LABELS = ["complete", "completed", "closed", "cancelled", "canceled", "resolved"];
1297
+ var OP = "tickets.statusSemantics";
1298
+ function picklistOf(fields) {
1299
+ if (!Array.isArray(fields)) return [];
1300
+ const status = fields.find((f) => typeof f?.name === "string" && f.name.toLowerCase() === "status");
1301
+ if (status === void 0 || status.isPickList !== true || !Array.isArray(status.picklistValues)) return [];
1302
+ const out = [];
1303
+ for (const v of status.picklistValues) {
1304
+ const id = Number(v?.value);
1305
+ if (!Number.isSafeInteger(id)) continue;
1306
+ out.push({ id, label: typeof v.label === "string" ? v.label : "" });
1307
+ }
1308
+ return out;
1309
+ }
1310
+ async function readFields(http, fresh) {
1311
+ const cache = http.fieldCache;
1312
+ const key = "tickets";
1313
+ if (!fresh && cache !== void 0) {
1314
+ const shared = cache.inflight(key, "fields");
1315
+ if (shared !== void 0) return await shared;
1316
+ const hit = cache.get(key, "fields");
1317
+ if (hit !== void 0) return hit;
1318
+ }
1319
+ const res = await http.request({ method: "GET", path: "/V1.0/Tickets/entityInformation/fields", operation: OP, maxRetries: 0 });
1320
+ const fields = res?.fields;
1321
+ if (!Array.isArray(fields)) {
1322
+ throw new AutotaskError("Expected the FieldInformationResultModel envelope { fields: [] }; got a different shape", { code: "INVALID_RESPONSE", category: "server" });
1323
+ }
1324
+ cache?.set(key, "fields", fields);
1325
+ return fields;
1326
+ }
1327
+ function assumed(excludeStatusIds) {
1328
+ const closedIds = excludeStatusIds === void 0 ? [ASSUMED_CLOSED_STATUS_ID] : [...excludeStatusIds];
1329
+ return {
1330
+ picklist: [],
1331
+ closedIds,
1332
+ source: "assumed",
1333
+ assumedId: ASSUMED_CLOSED_STATUS_ID,
1334
+ note: `closed = status ${closedIds.join(", ")} \u2014 ASSUMED, not read from the tenant: the Tickets status picklist could not be resolved, so the historical id ${ASSUMED_CLOSED_STATUS_ID} stands in (fix: expose the Tickets field catalog to this API user, or pass excludeStatusIds)`
1335
+ };
1336
+ }
1337
+ async function resolveTicketStatusSemantics(http, opts = {}) {
1338
+ if (opts.excludeStatusIds !== void 0) {
1339
+ const closedIds2 = [...new Set(opts.excludeStatusIds.map((id) => Number(id)))];
1340
+ if (closedIds2.length === 0 || closedIds2.some((id) => !Number.isSafeInteger(id) || id < 1)) {
1341
+ throw new AutotaskConfigError(`excludeStatusIds must be a non-empty list of positive status ids (got ${JSON.stringify(opts.excludeStatusIds)})`, { operation: OP });
1342
+ }
1343
+ return { picklist: [], closedIds: closedIds2, source: "picklist", note: `closed = status ${closedIds2.join(", ")} \u2014 supplied by the caller` };
1344
+ }
1345
+ let fields;
1346
+ try {
1347
+ fields = await readFields(http, opts.fresh === true);
1348
+ } catch (e) {
1349
+ if (e instanceof AutotaskError && e.code === "ABORTED") throw e;
1350
+ return assumed();
1351
+ }
1352
+ const picklist = picklistOf(fields);
1353
+ const closedIds = picklist.filter((v) => CLOSED_STATUS_LABELS.includes(v.label.trim().toLowerCase())).map((v) => v.id);
1354
+ if (closedIds.length === 0) return assumed();
1355
+ const labels = picklist.filter((v) => closedIds.includes(v.id)).map((v) => `${v.id} ${v.label}`).join(", ");
1356
+ return {
1357
+ picklist,
1358
+ closedIds,
1359
+ source: "picklist",
1360
+ note: `closed = status not in [${closedIds.join(", ")}] (${labels}) \u2014 resolved from the tenant's Tickets status picklist`
1361
+ };
1362
+ }
1363
+ function openStatusFilter(closedIds) {
1364
+ return closedIds.length === 1 ? { field: "status", op: "noteq", value: closedIds[0] } : { field: "status", op: "notIn", value: [...closedIds] };
1365
+ }
1366
+ function isClosedStatus(status, closedIds) {
1367
+ return typeof status === "number" && closedIds.includes(status);
1368
+ }
1369
+
1294
1370
  // src/resources/special/companies/fullProfile.ts
1295
1371
  var FULL_PROFILE_PROJECTABLE_PARTS = ["locations", "contacts", "notes", "projects", "openTickets"];
1296
1372
  var PART_ENTITY = {
@@ -1300,7 +1376,7 @@ var PART_ENTITY = {
1300
1376
  projects: "projects",
1301
1377
  openTickets: "tickets"
1302
1378
  };
1303
- var OPEN_TICKETS_NOTE = "open = status != 5 (Complete) \u2014 the ww29 ticket-status picklist has no Closed value; tickets in any other status count as open";
1379
+ var OPEN_TICKETS_NOTE = "open = status != 5 (Complete) \u2014 ASSUMED: the tenant Tickets status picklist could not be read, so id 5 is the fallback closed id; tickets in any other status count as open";
1304
1380
  var PROFILE_PART_LIMIT = 50;
1305
1381
  var LOCATION_LIMIT_MAX = 500;
1306
1382
  function locationLimit(value) {
@@ -1361,6 +1437,8 @@ async function scopedCompanyQuery(http, path, companyID, limit, includeFields) {
1361
1437
  return project(unwrapItems(res).items, includeFields);
1362
1438
  }
1363
1439
  async function readOpenTickets(http, companyID, ticketLimit, includeFields) {
1440
+ const semantics = await resolveTicketStatusSemantics(http);
1441
+ const openLeaf = openStatusFilter(semantics.closedIds);
1364
1442
  const openRows = await (async () => {
1365
1443
  const res = await http.request({
1366
1444
  method: "POST",
@@ -1368,30 +1446,31 @@ async function readOpenTickets(http, companyID, ticketLimit, includeFields) {
1368
1446
  path: "/V1.0/Tickets/query",
1369
1447
  body: {
1370
1448
  MaxRecords: ticketLimit,
1371
- filter: [{ field: "companyID", op: "eq", value: companyID }, { field: "status", op: "noteq", value: 5 }],
1449
+ filter: [{ field: "companyID", op: "eq", value: companyID }, openLeaf],
1372
1450
  ...includeFields ? { IncludeFields: includeFields } : {}
1373
1451
  },
1374
1452
  operation: "companies.fullProfile"
1375
1453
  });
1376
1454
  return project(unwrapItems(res).items, includeFields);
1377
1455
  })();
1456
+ const closedStatus = { closedIds: semantics.closedIds, source: semantics.source, note: semantics.note };
1457
+ const partNote = semantics.source === "assumed" ? OPEN_TICKETS_NOTE : semantics.note;
1378
1458
  let openTickets = {
1379
1459
  items: openRows,
1380
1460
  capped: openRows.length >= ticketLimit,
1381
1461
  blind: false,
1382
- note: OPEN_TICKETS_NOTE
1462
+ note: partNote,
1463
+ closedStatus
1383
1464
  };
1384
1465
  if (openRows.length === 0) {
1385
- const stamp = blindStampFor(BLIND_SURFACE_STAMPS["tickets"]?.query, [
1386
- { field: "companyID", op: "eq", value: companyID },
1387
- { field: "status", op: "noteq", value: 5 }
1388
- ]);
1466
+ const stamp = blindStampFor(BLIND_SURFACE_STAMPS["tickets"]?.query, [{ field: "companyID", op: "eq", value: companyID }, openLeaf]);
1389
1467
  if (stamp !== void 0) {
1390
1468
  openTickets = {
1391
1469
  items: openRows,
1392
1470
  capped: openRows.length >= ticketLimit,
1393
1471
  blind: true,
1394
- note: `${stamp.note} ${OPEN_TICKETS_NOTE}`,
1472
+ note: `${stamp.note} ${partNote}`,
1473
+ closedStatus,
1395
1474
  blindSurface: stamp
1396
1475
  };
1397
1476
  }
@@ -1589,7 +1668,7 @@ function validateUnbilledWorkOptions(companyId, opts) {
1589
1668
  }
1590
1669
 
1591
1670
  // src/resources/special/companies/unbilledWork.ts
1592
- var OP = "companies.unbilledWork";
1671
+ var OP2 = "companies.unbilledWork";
1593
1672
  function capJoinSet(ids2, cap) {
1594
1673
  const sorted = [...new Set(ids2)].sort((a, b) => a - b);
1595
1674
  if (sorted.length <= cap) return { ids: sorted, truncated: false };
@@ -1626,14 +1705,14 @@ async function readCompanyAndParent(resource, id, includeParent) {
1626
1705
  }
1627
1706
  async function readJoinSets(http, companyIds, v) {
1628
1707
  const ticketsCond = [{ field: "companyID", op: "in", value: companyIds }];
1629
- const ticketsRaw = await boundedPart(http, "/V1.0/Tickets/query", ticketsCond, v.limit, OP);
1708
+ const ticketsRaw = await boundedPart(http, "/V1.0/Tickets/query", ticketsCond, v.limit, OP2);
1630
1709
  const projectsCond = [{ field: "companyID", op: "in", value: companyIds }];
1631
- const projectsRaw = await boundedPart(http, "/V1.0/Projects/query", projectsCond, v.limit, OP);
1710
+ const projectsRaw = await boundedPart(http, "/V1.0/Projects/query", projectsCond, v.limit, OP2);
1632
1711
  const projectIds = projectsRaw.items.map((p) => num(p.id)).filter((n) => n > 0);
1633
1712
  let taskIds = [];
1634
1713
  let tasksHasMore = false;
1635
1714
  if (projectIds.length > 0) {
1636
- const tasksRaw = await boundedPart(http, "/V1.0/Tasks/query", [{ field: "projectID", op: "in", value: projectIds }], v.limit, OP);
1715
+ const tasksRaw = await boundedPart(http, "/V1.0/Tasks/query", [{ field: "projectID", op: "in", value: projectIds }], v.limit, OP2);
1637
1716
  taskIds = tasksRaw.items.map((t) => num(t.id)).filter((n) => n > 0);
1638
1717
  tasksHasMore = tasksRaw.hasMore;
1639
1718
  }
@@ -1657,7 +1736,7 @@ async function readBillingPart(http, v, companyIds) {
1657
1736
  }
1658
1737
  const billingFields = resolveFieldsPreset("billing_join", "billing_items");
1659
1738
  const billingIncludeFields = [...billingFields, "id", ...v.includeNotes ? ["description"] : []];
1660
- const billingRaw = await boundedPart(http, "/V1.0/BillingItems/query", billingConditions, v.limit, OP, billingIncludeFields);
1739
+ const billingRaw = await boundedPart(http, "/V1.0/BillingItems/query", billingConditions, v.limit, OP2, billingIncludeFields);
1661
1740
  let billingItems = billingRaw.items;
1662
1741
  let billingNoteCharsTruncated = 0;
1663
1742
  if (v.includeNotes) {
@@ -1701,7 +1780,7 @@ async function readTimePart(http, v, join) {
1701
1780
  if (v.dateFrom !== void 0) timeConditions.push({ field: "dateWorked", op: "gte", value: v.dateFrom });
1702
1781
  if (v.dateTo !== void 0) timeConditions.push({ field: "dateWorked", op: "lte", value: v.dateTo });
1703
1782
  const timeFields = ["taskID", "ticketID", "hoursWorked", "dateWorked", "id", ...v.includeNotes ? ["summaryNotes"] : []];
1704
- const raw = await boundedPart(http, "/V1.0/TimeEntries/query", timeConditions, v.limit, OP, timeFields);
1783
+ const raw = await boundedPart(http, "/V1.0/TimeEntries/query", timeConditions, v.limit, OP2, timeFields);
1705
1784
  entries = raw.items;
1706
1785
  let cappedItems = entries;
1707
1786
  let noteCharsTruncated = 0;
@@ -5220,7 +5299,7 @@ function appendWiresNote(wires, note) {
5220
5299
  wires.note = (wires.note ? wires.note + " | " : "") + note;
5221
5300
  }
5222
5301
  function makeFetchCtx(rawHttp, v, contractId, budget) {
5223
- const OP7 = PROFITABILITY_OP;
5302
+ const OP9 = PROFITABILITY_OP;
5224
5303
  const id = parseIdentifier(contractId, "contractId");
5225
5304
  const idNum = typeof id === "number" ? id : Number(id);
5226
5305
  const wires = { issued: 0, requests: 0, budget };
@@ -5232,19 +5311,19 @@ function makeFetchCtx(rawHttp, v, contractId, budget) {
5232
5311
  } };
5233
5312
  const readPart = (path, conditions, fields) => {
5234
5313
  wires.issued += 1;
5235
- return degradable(() => boundedPart(http, path, conditions, v.limit, OP7, fields));
5314
+ return degradable(() => boundedPart(http, path, conditions, v.limit, OP9, fields));
5236
5315
  };
5237
5316
  const truncatedMonths = {};
5238
5317
  const windowedPart = async (name, key, path, cond, fields, dateField) => {
5239
5318
  const first = await readPart(path, cond, fields);
5240
5319
  if (first.absent !== void 0 || !first.hasMore) return { part: stampPart(key, first, cond), rows: first.items };
5241
- const wp = await degradable(() => expandByMonth(http, path, cond, v.limit, OP7, fields, dateField, v.dateFrom, v.dateTo, wires));
5320
+ const wp = await degradable(() => expandByMonth(http, path, cond, v.limit, OP9, fields, dateField, v.dateFrom, v.dateTo, wires));
5242
5321
  if (!("truncatedMonths" in wp)) return { part: stampPart(key, wp, cond), rows: [] };
5243
5322
  const { truncatedMonths: tm, ...part } = wp;
5244
5323
  truncatedMonths[name] = tm;
5245
5324
  return { part, rows: wp.items };
5246
5325
  };
5247
- return { v, OP: OP7, id, idNum, rawHttp, http, wires, windowOn, readPart, windowedPart, truncatedMonths };
5326
+ return { v, OP: OP9, id, idNum, rawHttp, http, wires, windowOn, readPart, windowedPart, truncatedMonths };
5248
5327
  }
5249
5328
 
5250
5329
  // src/resources/special/contracts/profitability.stages.ts
@@ -10434,7 +10513,6 @@ var TicketChangeRequestApprovalsResource = class extends BaseResource {
10434
10513
  // src/resources/special/_shared/move-engine.ts
10435
10514
  import { randomUUID } from "crypto";
10436
10515
  var MOVE_SOURCE_AGE_DEFAULT_DAYS = 14;
10437
- var TICKET_COMPLETE_STATUS = 5;
10438
10516
  var MOVE_EFFECTS_NOTE = "Create then delete: the new record gets a fresh create date and creator; creating/deleting may trigger notifications, SLA and workflow rules the API cannot suppress; vendor acceptance of the create is not validated by a dry run";
10439
10517
  var refuse = (code, message, ids2, operation, extra = {}) => new MoveRefusedError(message, { code, operation, details: { status: "failed", ...ids2, ...extra } });
10440
10518
  async function queryRows(http, resource, filter, operation, retries) {
@@ -10487,11 +10565,19 @@ async function preview(http, spec, sourceId, toTicketId, opts) {
10487
10565
  }
10488
10566
  const target = await readTicket(http, toTicketId, opts.maxRetries, op);
10489
10567
  if (target === null) throw refuse("MOVE_TARGET_MISSING", `${op}: target ticket ${toTicketId} does not exist`, ids2, op);
10490
- if (target.status === TICKET_COMPLETE_STATUS || target.completedDate !== void 0 && target.completedDate !== null) {
10491
- throw refuse("MOVE_TARGET_CLOSED", `${op}: target ticket ${toTicketId} is closed (status 5 or completed)`, ids2, op);
10568
+ const closedStatus = await resolveTicketStatusSemantics(http);
10569
+ if (isClosedStatus(target.status, closedStatus.closedIds) || target.completedDate !== void 0 && target.completedDate !== null) {
10570
+ throw refuse(
10571
+ "MOVE_TARGET_CLOSED",
10572
+ `${op}: target ticket ${toTicketId} is closed (status in [${closedStatus.closedIds.join(", ")}] or completed) \u2014 closed ids ${closedStatus.source === "assumed" ? "ASSUMED (the tenant status picklist could not be read)" : "resolved from the tenant Tickets status picklist"}`,
10573
+ ids2,
10574
+ op,
10575
+ { closedStatusIds: closedStatus.closedIds, closedStatusSource: closedStatus.source }
10576
+ );
10492
10577
  }
10493
10578
  const origin = await readTicket(http, sourceTicketId, opts.maxRetries, op);
10494
10579
  const facts = {
10580
+ closedStatus: { closedIds: closedStatus.closedIds, source: closedStatus.source, note: closedStatus.note },
10495
10581
  sourceTicketId,
10496
10582
  sourceCompanyID: origin?.companyID ?? null,
10497
10583
  targetCompanyID: target.companyID ?? null,
@@ -10605,22 +10691,22 @@ function uiLinkFor(http, entity, id) {
10605
10691
  }
10606
10692
 
10607
10693
  // src/resources/special/ticket_charges.ts
10608
- var OP2 = "ticket_charges.moveToTicket";
10694
+ var OP3 = "ticket_charges.moveToTicket";
10609
10695
  var COPY = ["name", "description", "chargeType", "billingCodeID", "productID", "unitQuantity", "unitCost", "unitPrice", "datePurchased", "isBillableToCompany", "notes", "purchaseOrderNumber", "internalPurchaseOrderNumber"];
10610
10696
  var CONTRACT_SCOPED = ["contractServiceID", "contractServiceBundleID"];
10611
10697
  async function readCharge(http, id, retries) {
10612
- const rows = await queryRows(http, "TicketCharges", [{ field: "id", op: "eq", value: id }], OP2, retries);
10698
+ const rows = await queryRows(http, "TicketCharges", [{ field: "id", op: "eq", value: id }], OP3, retries);
10613
10699
  return rows[0] ?? null;
10614
10700
  }
10615
10701
  async function checkSource({ http, source, sourceId, sourceTicketId, toTicketId }) {
10616
10702
  if (source.isBilled === true) {
10617
- throw refuse("MOVE_BLOCKED_BILLED", `${OP2}: ticket charge ${sourceId} is billed \u2014 move it in the Autotask UI`, { sourceId, toTicketId }, OP2, {
10703
+ throw refuse("MOVE_BLOCKED_BILLED", `${OP3}: ticket charge ${sourceId} is billed \u2014 move it in the Autotask UI`, { sourceId, toTicketId }, OP3, {
10618
10704
  manualAction: { action: "delete_source", uiLink: uiLinkFor(http, "tickets", sourceTicketId), reason: "billed charges are not moved by the API (the ticket link opens its charges)" }
10619
10705
  });
10620
10706
  }
10621
10707
  }
10622
10708
  var SPEC = {
10623
- operation: OP2,
10709
+ operation: OP3,
10624
10710
  entity: "ticket_charge",
10625
10711
  readSource: readCharge,
10626
10712
  sourceTicketId: (s) => typeof s.ticketID === "number" ? s.ticketID : null,
@@ -12061,6 +12147,121 @@ async function fullContext(resource, id, opts = {}, http) {
12061
12147
  return result;
12062
12148
  }
12063
12149
 
12150
+ // src/resources/special/tickets/forResource.ts
12151
+ var OP4 = "tickets.forResource";
12152
+ var TICKET_CHUNK = 100;
12153
+ var LIMIT_DEFAULT = 100;
12154
+ var LIMIT_MAX = 500;
12155
+ var LINK_READ_CAP = 500;
12156
+ var TICKET_FIELDS2 = ["id", "ticketNumber", "title", "status", "assignedResourceID", "companyID"];
12157
+ function positiveId(value, name) {
12158
+ const n = typeof value === "string" && /^\d+$/.test(value.trim()) ? Number(value) : value;
12159
+ if (typeof n !== "number" || !Number.isSafeInteger(n) || n < 1) {
12160
+ throw new AutotaskConfigError(`${OP4}: ${name} must be a positive integer id (got ${String(value)})`, { operation: OP4 });
12161
+ }
12162
+ return n;
12163
+ }
12164
+ async function ticketPage(http, filter, maxRecords, fields) {
12165
+ const res = await http.request({
12166
+ method: "POST",
12167
+ idempotent: true,
12168
+ path: "/V1.0/Tickets/query",
12169
+ body: { MaxRecords: maxRecords, filter, IncludeFields: fields },
12170
+ operation: OP4
12171
+ });
12172
+ const page = unwrapItems(res, OP4);
12173
+ return { rows: page.items, more: page.pageDetails.nextPageUrl !== null };
12174
+ }
12175
+ async function forResource(_resource, resourceIDIn, http, opts = {}) {
12176
+ if (opts === null || typeof opts !== "object") throw new AutotaskConfigError(`${OP4}: opts must be an object`, { operation: OP4 });
12177
+ const resourceID = positiveId(resourceIDIn, "resourceID");
12178
+ const scope = opts.scope ?? "both";
12179
+ if (scope !== "primary" && scope !== "secondary" && scope !== "both") {
12180
+ throw new AutotaskConfigError(`${OP4}: scope must be 'primary', 'secondary' or 'both' (got ${JSON.stringify(opts.scope)})`, { operation: OP4 });
12181
+ }
12182
+ if (opts.openOnly !== void 0 && typeof opts.openOnly !== "boolean") {
12183
+ throw new AutotaskConfigError(`${OP4}: openOnly must be a boolean`, { operation: OP4 });
12184
+ }
12185
+ const limit = opts.limit === void 0 ? LIMIT_DEFAULT : opts.limit;
12186
+ if (!Number.isInteger(limit) || limit < 1 || limit > LIMIT_MAX) {
12187
+ throw new AutotaskConfigError(`${OP4}: limit must be an integer 1-${LIMIT_MAX} (got ${String(opts.limit)})`, { operation: OP4 });
12188
+ }
12189
+ const includeFields = opts.includeFields ?? [];
12190
+ if (!Array.isArray(includeFields) || includeFields.some((f) => typeof f !== "string" || f.length === 0)) {
12191
+ throw new AutotaskConfigError(`${OP4}: includeFields must be a list of field names`, { operation: OP4 });
12192
+ }
12193
+ const openOnly = opts.openOnly === true;
12194
+ const fields = [.../* @__PURE__ */ new Set([...TICKET_FIELDS2, ...includeFields])];
12195
+ const closed = await resolveTicketStatusSemantics(http, opts.excludeStatusIds === void 0 ? {} : { excludeStatusIds: opts.excludeStatusIds });
12196
+ const closedStatus = { closedIds: closed.closedIds, source: closed.source, note: closed.note };
12197
+ const openLeaf = openOnly ? [openStatusFilter(closed.closedIds)] : [];
12198
+ const linkIds = [];
12199
+ let capped = false;
12200
+ if (scope !== "primary") {
12201
+ const res = await http.request({
12202
+ method: "POST",
12203
+ idempotent: true,
12204
+ path: "/V1.0/TicketSecondaryResources/query",
12205
+ body: { MaxRecords: LINK_READ_CAP, filter: [{ field: "resourceID", op: "eq", value: resourceID }], IncludeFields: ["ticketID"] },
12206
+ operation: OP4
12207
+ });
12208
+ const page = unwrapItems(res, OP4);
12209
+ for (const row of page.items) if (typeof row.ticketID === "number" && !linkIds.includes(row.ticketID)) linkIds.push(row.ticketID);
12210
+ if (page.items.length >= LINK_READ_CAP || page.pageDetails.nextPageUrl !== null) capped = true;
12211
+ }
12212
+ const primary = /* @__PURE__ */ new Map();
12213
+ if (scope !== "secondary") {
12214
+ const page = await ticketPage(http, [{ field: "assignedResourceID", op: "eq", value: resourceID }, ...openLeaf], limit, fields);
12215
+ if (page.more || page.rows.length >= limit) capped = true;
12216
+ for (const t of page.rows) if (typeof t.id === "number") primary.set(t.id, t);
12217
+ }
12218
+ const secondary = /* @__PURE__ */ new Map();
12219
+ const unresolvedIds = [];
12220
+ for (let c = 0; c < linkIds.length; c += TICKET_CHUNK) {
12221
+ const chunk = linkIds.slice(c, c + TICKET_CHUNK);
12222
+ const page = await ticketPage(http, [{ field: "id", op: "in", value: chunk }, ...openLeaf], chunk.length, fields);
12223
+ const seen = /* @__PURE__ */ new Set();
12224
+ for (const t of page.rows) {
12225
+ if (typeof t.id !== "number") continue;
12226
+ secondary.set(t.id, t);
12227
+ seen.add(t.id);
12228
+ }
12229
+ for (const id of chunk) if (!seen.has(id)) unresolvedIds.push(id);
12230
+ }
12231
+ const merged = /* @__PURE__ */ new Map();
12232
+ for (const [id, t] of primary) merged.set(id, t);
12233
+ for (const [id, t] of secondary) if (!merged.has(id)) merged.set(id, t);
12234
+ let tickets = [...merged.values()];
12235
+ if (openOnly) tickets = tickets.filter((t) => !isClosedStatus(t.status, closed.closedIds));
12236
+ tickets.sort((a, b) => Number(a.id) - Number(b.id));
12237
+ if (tickets.length > limit) {
12238
+ tickets = tickets.slice(0, limit);
12239
+ capped = true;
12240
+ }
12241
+ const openCount = tickets.filter((t) => !isClosedStatus(t.status, closed.closedIds)).length;
12242
+ const primaryIds = [...primary.keys()].sort((a, b) => a - b);
12243
+ const secondaryIds = [...secondary.keys()].sort((a, b) => a - b);
12244
+ const both = primaryIds.filter((id) => secondary.has(id));
12245
+ return {
12246
+ resourceID,
12247
+ scope,
12248
+ openOnly,
12249
+ tickets,
12250
+ primaryIds,
12251
+ secondaryIds,
12252
+ bothIds: both,
12253
+ counts: { primary: primaryIds.length, secondary: secondaryIds.length, both: both.length, open: openCount },
12254
+ capped,
12255
+ unresolvedIds: unresolvedIds.sort((a, b) => a - b),
12256
+ closedStatus
12257
+ };
12258
+ }
12259
+
12260
+ // src/resources/special/tickets/statusSemantics.ts
12261
+ async function statusSemantics(_resource, opts = {}, http) {
12262
+ return resolveTicketStatusSemantics(http, opts);
12263
+ }
12264
+
12064
12265
  // src/resources/tickets.ts
12065
12266
  var TicketsResource = class extends BaseResource {
12066
12267
  constructor(http, pageSize) {
@@ -12166,6 +12367,14 @@ var TicketsResource = class extends BaseResource {
12166
12367
  fullContext(id, opts) {
12167
12368
  return fullContext(this, id, opts, this.http);
12168
12369
  }
12370
+ /** Node-parity special op (hand-written in src/resources/special/tickets.ts). */
12371
+ statusSemantics(opts) {
12372
+ return statusSemantics(this, opts, this.http);
12373
+ }
12374
+ /** Node-parity special op (hand-written in src/resources/special/tickets.ts). */
12375
+ forResource(resourceID, opts) {
12376
+ return forResource(this, resourceID, this.http, opts);
12377
+ }
12169
12378
  };
12170
12379
 
12171
12380
  // src/resources/ticket_webhooks.ts
@@ -12294,12 +12503,12 @@ async function postedDateStatusQuery(_resource, mode, http, opts) {
12294
12503
  return items;
12295
12504
  }
12296
12505
  async function postedWithin(_resource, from, to, http, opts, opLabel = "time_entries.postedWithin") {
12297
- const OP7 = opLabel;
12298
- if (opts?.tz !== void 0) validateZone(opts.tz, OP7);
12299
- const fromB = customRangeDay(from, "from", void 0, OP7);
12300
- const toB = customRangeDay(to, "to", void 0, OP7);
12506
+ const OP9 = opLabel;
12507
+ if (opts?.tz !== void 0) validateZone(opts.tz, OP9);
12508
+ const fromB = customRangeDay(from, "from", void 0, OP9);
12509
+ const toB = customRangeDay(to, "to", void 0, OP9);
12301
12510
  if (fromB.ms > toB.ms) {
12302
- throw new AutotaskConfigError(`${OP7}: from (${iso2(fromB.ms)}) must not be after to (${iso2(toB.ms)})`, { operation: OP7 });
12511
+ throw new AutotaskConfigError(`${OP9}: from (${iso2(fromB.ms)}) must not be after to (${iso2(toB.ms)})`, { operation: OP9 });
12303
12512
  }
12304
12513
  const windowToMs = toB.ms + DAY_MS2 - 1;
12305
12514
  const filter = [
@@ -12310,7 +12519,7 @@ async function postedWithin(_resource, from, to, http, opts, opLabel = "time_ent
12310
12519
  else if (opts?.posted === false) filter.push({ field: "postedDate", op: "notExist" });
12311
12520
  if (opts?.ticketID !== void 0) filter.push({ field: "ticketID", op: "eq", value: opts.ticketID });
12312
12521
  if (opts?.resourceID !== void 0) filter.push({ field: "resourceID", op: "eq", value: opts.resourceID });
12313
- const { items, hasMore } = await queryTimeEntries(_resource, filter, clampWindowLimit(opts?.limit), OP7, http);
12522
+ const { items, hasMore } = await queryTimeEntries(_resource, filter, clampWindowLimit(opts?.limit), OP9, http);
12314
12523
  let postedCount = 0;
12315
12524
  for (const item of items) {
12316
12525
  const postedDate = item.postedDate;
@@ -12520,7 +12729,7 @@ async function resolveCode(http, family, familyLabel2, pinnedId, departments, op
12520
12729
  }
12521
12730
 
12522
12731
  // src/resources/special/time_entries/createForResource.ts
12523
- var OP3 = "time_entries.createForResource";
12732
+ var OP5 = "time_entries.createForResource";
12524
12733
  var PARAM_KEYS = /* @__PURE__ */ new Set([
12525
12734
  "internallAllocationCodeID",
12526
12735
  "ticketID",
@@ -12556,30 +12765,30 @@ function validateCreateParams(params) {
12556
12765
  if (pinCount > 1) {
12557
12766
  throw new AutotaskConfigError(
12558
12767
  "createForResource: exactly one linkage pin is honoured (internallAllocationCodeID | ticketID | taskID) \u2014 the vendor gate requires exactly one; pass one pin (or none for the default internal linkage)",
12559
- { operation: OP3 }
12768
+ { operation: OP5 }
12560
12769
  );
12561
12770
  }
12562
12771
  if (pins.role !== void 0 && pins.code !== void 0) {
12563
12772
  throw new AutotaskConfigError(
12564
12773
  "createForResource: a pinned roleID is only meaningful with the task/ticket linkage (internallAllocationCodeID pins the internal linkage and owns the code) \u2014 drop one",
12565
- { operation: OP3 }
12774
+ { operation: OP5 }
12566
12775
  );
12567
12776
  }
12568
12777
  if (params.hoursWorked !== void 0 && (typeof params.hoursWorked !== "number" || !Number.isFinite(params.hoursWorked) || params.hoursWorked <= 0)) {
12569
12778
  throw new AutotaskConfigError(
12570
12779
  `createForResource: hoursWorked must be a positive finite number (got ${String(params.hoursWorked)})`,
12571
- { operation: OP3 }
12780
+ { operation: OP5 }
12572
12781
  );
12573
12782
  }
12574
12783
  return pins;
12575
12784
  }
12576
12785
  async function assertResourceActive(http, rid) {
12577
- const rec = await fetchItem2(http, `/V1.0/Resources/${rid}`, OP3);
12786
+ const rec = await fetchItem2(http, `/V1.0/Resources/${rid}`, OP5);
12578
12787
  if (rec === null) {
12579
12788
  throw new ResourceInvalidError(`Resource ${rid} was not found \u2014 a time entry cannot attach to a missing resource`, {
12580
12789
  resourceIds: [rid],
12581
12790
  details: { idRefs: [{ entity: "resource", id: rid }] },
12582
- operation: OP3,
12791
+ operation: OP5,
12583
12792
  category: "not_found",
12584
12793
  suggestedAction: "Resolve the resource id (resources.get / resources.search) and retry."
12585
12794
  });
@@ -12588,7 +12797,7 @@ async function assertResourceActive(http, rid) {
12588
12797
  throw new ResourceInvalidError(`Resource ${rid} is not active (isActive !== true) \u2014 the vendor would reject a time entry for an inactive resource`, {
12589
12798
  resourceIds: [rid],
12590
12799
  details: { idRefs: [{ entity: "resource", id: rid }] },
12591
- operation: OP3,
12800
+ operation: OP5,
12592
12801
  suggestedAction: "Activate the resource or pick an active one."
12593
12802
  });
12594
12803
  }
@@ -12598,11 +12807,11 @@ async function resolveAllocationCode(http, rid, linkage, pins, departments) {
12598
12807
  const pinnedForCode = pins.code !== void 0 ? pins.code : pins.role;
12599
12808
  let code = null;
12600
12809
  const familiesTried = [];
12601
- code = await resolveCode(http, familyOf(linkage), familyLabel(familyOf(linkage)), pinnedForCode, departments, OP3);
12810
+ code = await resolveCode(http, familyOf(linkage), familyLabel(familyOf(linkage)), pinnedForCode, departments, OP5);
12602
12811
  familiesTried.push({ family: familyOf(linkage), label: familyLabel(familyOf(linkage)), filters: [{ field: "useType", op: "eq", value: familyOf(linkage) }, { field: "isActive", op: "eq", value: true }] });
12603
12812
  if (code === null && linkage === "task" && pins.role === void 0) {
12604
12813
  usedLinkage = "internal";
12605
- code = await resolveCode(http, USE_TYPE_INTERNAL, familyLabel(USE_TYPE_INTERNAL), void 0, departments, OP3);
12814
+ code = await resolveCode(http, USE_TYPE_INTERNAL, familyLabel(USE_TYPE_INTERNAL), void 0, departments, OP5);
12606
12815
  familiesTried.push({ family: USE_TYPE_INTERNAL, label: familyLabel(USE_TYPE_INTERNAL), filters: [{ field: "useType", op: "eq", value: USE_TYPE_INTERNAL }, { field: "isActive", op: "eq", value: true }] });
12607
12816
  }
12608
12817
  if (code === null) {
@@ -12612,7 +12821,7 @@ async function resolveAllocationCode(http, rid, linkage, pins, departments) {
12612
12821
  families: familiesTried,
12613
12822
  resourceDepartments: [...departments].sort((a, b) => a - b)
12614
12823
  },
12615
- { operation: OP3 }
12824
+ { operation: OP5 }
12616
12825
  );
12617
12826
  }
12618
12827
  const resolvedCode = codeInfo(code);
@@ -12651,7 +12860,7 @@ function createDryRun2(http, rid, body, linkage, usedLinkage, resolvedCode) {
12651
12860
  }
12652
12861
  return buildDryRunResult(
12653
12862
  {
12654
- operation: OP3,
12863
+ operation: OP5,
12655
12864
  method: "POST",
12656
12865
  path: "/V1.0/TimeEntries",
12657
12866
  ids: [rid],
@@ -12693,7 +12902,7 @@ async function createWithAttempts(resource, http, opts, body, defaults, pins, us
12693
12902
  const gate3 = ROLE_REJECTION_PATTERN.test(text);
12694
12903
  if (usedLinkage !== "internal" && gate3) {
12695
12904
  if (!taskRoleFetched && usedLinkage === "task" && pins.task !== void 0) {
12696
- const task = await fetchItem2(http, `/V1.0/Tasks/${pins.task}`, OP3);
12905
+ const task = await fetchItem2(http, `/V1.0/Tasks/${pins.task}`, OP5);
12697
12906
  taskRoleFetched = true;
12698
12907
  if (task !== null) pushLadder(task.assignedResourceRoleID);
12699
12908
  }
@@ -12719,7 +12928,7 @@ async function createForResource(resource, resourceID, params, opts = {}, http)
12719
12928
  const pins = validateCreateParams(params);
12720
12929
  await assertResourceActive(http, rid);
12721
12930
  const linkage = pins.code !== void 0 ? "internal" : pins.ticket !== void 0 ? "ticket" : pins.task !== void 0 ? "task" : "internal";
12722
- const roleRows = await resourceRoleRows(http, rid, OP3);
12931
+ const roleRows = await resourceRoleRows(http, rid, OP5);
12723
12932
  const departments = roleRows.departments;
12724
12933
  const { usedLinkage, resolvedCode } = await resolveAllocationCode(http, rid, linkage, pins, departments);
12725
12934
  const defaults = windowDefaults(params);
@@ -12742,12 +12951,12 @@ function discoveryField(err, body, defaults) {
12742
12951
  }
12743
12952
 
12744
12953
  // src/resources/special/time_entries/move.ts
12745
- var OP4 = "time_entries.moveToTicket";
12954
+ var OP6 = "time_entries.moveToTicket";
12746
12955
  var COPY2 = ["resourceID", "roleID", "billingCodeID", "dateWorked", "startDateTime", "endDateTime", "hoursWorked", "offsetHours", "summaryNotes", "internalNotes", "isNonBillable", "showOnInvoice"];
12747
12956
  var CONTRACT_SCOPED2 = ["contractID", "contractServiceID", "contractServiceBundleID"];
12748
12957
  async function readTimeEntry(http, id, retries) {
12749
12958
  try {
12750
- const res = await http.request({ method: "GET", path: `/V1.0/TimeEntries/${id}`, operation: OP4, maxRetries: retries });
12959
+ const res = await http.request({ method: "GET", path: `/V1.0/TimeEntries/${id}`, operation: OP6, maxRetries: retries });
12751
12960
  return res?.item ?? null;
12752
12961
  } catch (e) {
12753
12962
  if (isAutotaskError(e) && (e.httpStatus === 404 || e.code === "NOT_FOUND")) return null;
@@ -12757,27 +12966,27 @@ async function readTimeEntry(http, id, retries) {
12757
12966
  async function checkSource2({ http, source, sourceId, toTicketId, retries }) {
12758
12967
  const ids2 = { sourceId, toTicketId };
12759
12968
  if (source.taskID !== null && source.taskID !== void 0) {
12760
- throw refuse("MOVE_SOURCE_UNSUPPORTED", `${OP4}: time entry ${sourceId} is on a task \u2014 only ticket time entries are moved`, ids2, OP4);
12969
+ throw refuse("MOVE_SOURCE_UNSUPPORTED", `${OP6}: time entry ${sourceId} is on a task \u2014 only ticket time entries are moved`, ids2, OP6);
12761
12970
  }
12762
12971
  if (source.billingApprovalDateTime || source.billingApprovalResourceID || source.billingApprovalLevelMostRecent) {
12763
- throw refuse("MOVE_BLOCKED_APPROVED", `${OP4}: time entry ${sourceId} has billing approval set \u2014 move it in the Autotask UI`, ids2, OP4, { manualAction: { action: "delete_source", uiLink: uiLinkFor(http, "time_entries", sourceId), reason: "approved entries are not moved by the API" } });
12972
+ throw refuse("MOVE_BLOCKED_APPROVED", `${OP6}: time entry ${sourceId} has billing approval set \u2014 move it in the Autotask UI`, ids2, OP6, { manualAction: { action: "delete_source", uiLink: uiLinkFor(http, "time_entries", sourceId), reason: "approved entries are not moved by the API" } });
12764
12973
  }
12765
12974
  let billing;
12766
12975
  try {
12767
- billing = await queryRows(http, "BillingItems", [{ field: "timeEntryID", op: "eq", value: sourceId }], OP4, retries);
12976
+ billing = await queryRows(http, "BillingItems", [{ field: "timeEntryID", op: "eq", value: sourceId }], OP6, retries);
12768
12977
  } catch {
12769
- throw refuse("MOVE_BLOCKED_BILLED", `${OP4}: the billing items of time entry ${sourceId} could not be read \u2014 refused (fails closed)`, ids2, OP4, { manualAction: { action: "delete_source", uiLink: uiLinkFor(http, "time_entries", sourceId), reason: "billing state unreadable" } });
12978
+ throw refuse("MOVE_BLOCKED_BILLED", `${OP6}: the billing items of time entry ${sourceId} could not be read \u2014 refused (fails closed)`, ids2, OP6, { manualAction: { action: "delete_source", uiLink: uiLinkFor(http, "time_entries", sourceId), reason: "billing state unreadable" } });
12770
12979
  }
12771
12980
  if (billing.some((b) => b.invoiceID || b.postedDate)) {
12772
- throw refuse("MOVE_BLOCKED_BILLED", `${OP4}: time entry ${sourceId} is billed or posted \u2014 move it in the Autotask UI`, ids2, OP4, { manualAction: { action: "delete_source", uiLink: uiLinkFor(http, "time_entries", sourceId), reason: "billed entries are not moved by the API" } });
12981
+ throw refuse("MOVE_BLOCKED_BILLED", `${OP6}: time entry ${sourceId} is billed or posted \u2014 move it in the Autotask UI`, ids2, OP6, { manualAction: { action: "delete_source", uiLink: uiLinkFor(http, "time_entries", sourceId), reason: "billed entries are not moved by the API" } });
12773
12982
  }
12774
- const attachments = await queryRows(http, "TimeEntryAttachments", [{ field: "timeEntryID", op: "eq", value: sourceId }], OP4, retries);
12983
+ const attachments = await queryRows(http, "TimeEntryAttachments", [{ field: "timeEntryID", op: "eq", value: sourceId }], OP6, retries);
12775
12984
  if (attachments.length > 0) {
12776
- throw refuse("MOVE_BLOCKED_ATTACHMENTS", `${OP4}: time entry ${sourceId} has attachments, which a delete would destroy \u2014 move it in the Autotask UI`, ids2, OP4, { manualAction: { action: "delete_source", uiLink: uiLinkFor(http, "time_entries", sourceId), reason: "entries with attachments are not moved by the API" } });
12985
+ throw refuse("MOVE_BLOCKED_ATTACHMENTS", `${OP6}: time entry ${sourceId} has attachments, which a delete would destroy \u2014 move it in the Autotask UI`, ids2, OP6, { manualAction: { action: "delete_source", uiLink: uiLinkFor(http, "time_entries", sourceId), reason: "entries with attachments are not moved by the API" } });
12777
12986
  }
12778
12987
  }
12779
12988
  var SPEC2 = {
12780
- operation: OP4,
12989
+ operation: OP6,
12781
12990
  entity: "time_entry",
12782
12991
  readSource: readTimeEntry,
12783
12992
  sourceTicketId: (s) => typeof s.ticketID === "number" ? s.ticketID : null,
@@ -12812,7 +13021,7 @@ function moveToTicket2(_resource, sourceId, toTicketId, opts, http) {
12812
13021
  }
12813
13022
 
12814
13023
  // src/resources/special/time_entries/approve.ts
12815
- var OP5 = "time_entries.approve";
13024
+ var OP7 = "time_entries.approve";
12816
13025
  var APPROVAL_LEVELS_PATH = "/V1.0/BillingItemApprovalLevels";
12817
13026
  function toIso(value) {
12818
13027
  if (value instanceof Date) {
@@ -12850,7 +13059,7 @@ async function approve(resource, id, opts = {}, http) {
12850
13059
  if (opts.dryRun === true) {
12851
13060
  return buildDryRunResult(
12852
13061
  {
12853
- operation: OP5,
13062
+ operation: OP7,
12854
13063
  method: "POST",
12855
13064
  path: APPROVAL_LEVELS_PATH,
12856
13065
  ids: [numericId],
@@ -12873,10 +13082,10 @@ async function approve(resource, id, opts = {}, http) {
12873
13082
  method: "POST",
12874
13083
  path: APPROVAL_LEVELS_PATH,
12875
13084
  body,
12876
- operation: OP5,
13085
+ operation: OP7,
12877
13086
  maxRetries: opts.maxRetries
12878
13087
  });
12879
- const levelId = toNumber(parseIdentifier(unwrapOperation(created, OP5).id, "billingItemApprovalLevelID"));
13088
+ const levelId = toNumber(parseIdentifier(unwrapOperation(created, OP7).id, "billingItemApprovalLevelID"));
12880
13089
  await resource.update(
12881
13090
  numericId,
12882
13091
  { billingApprovalDateTime: approvalDateTime, billingApprovalResourceID: approvalResourceID },
@@ -12899,28 +13108,28 @@ async function approve(resource, id, opts = {}, http) {
12899
13108
  }
12900
13109
 
12901
13110
  // src/resources/special/time_entries/recentTickets.ts
12902
- var OP6 = "time_entries.recentTickets";
13111
+ var OP8 = "time_entries.recentTickets";
12903
13112
  var DAY_MS3 = 864e5;
12904
- var TICKET_CHUNK = 100;
12905
- var TICKET_FIELDS2 = ["id", "ticketNumber", "title", "status", "assignedResourceID", "companyID"];
12906
- function positiveId(value, name) {
13113
+ var TICKET_CHUNK2 = 100;
13114
+ var TICKET_FIELDS3 = ["id", "ticketNumber", "title", "status", "assignedResourceID", "companyID"];
13115
+ function positiveId2(value, name) {
12907
13116
  const n = typeof value === "string" && /^\d+$/.test(value.trim()) ? Number(value) : value;
12908
13117
  if (typeof n !== "number" || !Number.isSafeInteger(n) || n < 1) {
12909
- throw new AutotaskConfigError(`${OP6}: ${name} must be a positive integer id (got ${String(value)})`, { operation: OP6 });
13118
+ throw new AutotaskConfigError(`${OP8}: ${name} must be a positive integer id (got ${String(value)})`, { operation: OP8 });
12910
13119
  }
12911
13120
  return n;
12912
13121
  }
12913
13122
  async function recentTickets(_resource, from, to, http, opts) {
12914
13123
  if (opts === void 0 || opts === null || typeof opts !== "object") {
12915
- throw new AutotaskConfigError(`${OP6}: opts { resourceID, companyID?, tz?, limit? } is required`, { operation: OP6 });
13124
+ throw new AutotaskConfigError(`${OP8}: opts { resourceID, companyID?, tz?, limit? } is required`, { operation: OP8 });
12916
13125
  }
12917
- const resourceID = positiveId(opts.resourceID, "resourceID");
12918
- const companyID = opts.companyID === void 0 ? void 0 : positiveId(opts.companyID, "companyID");
12919
- if (opts.tz !== void 0) validateZone(opts.tz, OP6);
12920
- const fromB = customRangeDay(from, "from", void 0, OP6);
12921
- const toB = customRangeDay(to, "to", void 0, OP6);
13126
+ const resourceID = positiveId2(opts.resourceID, "resourceID");
13127
+ const companyID = opts.companyID === void 0 ? void 0 : positiveId2(opts.companyID, "companyID");
13128
+ if (opts.tz !== void 0) validateZone(opts.tz, OP8);
13129
+ const fromB = customRangeDay(from, "from", void 0, OP8);
13130
+ const toB = customRangeDay(to, "to", void 0, OP8);
12922
13131
  if (fromB.ms > toB.ms) {
12923
- throw new AutotaskConfigError(`${OP6}: from (${isoUtc(fromB.ms)}) must not be after to (${isoUtc(toB.ms)})`, { operation: OP6 });
13132
+ throw new AutotaskConfigError(`${OP8}: from (${isoUtc(fromB.ms)}) must not be after to (${isoUtc(toB.ms)})`, { operation: OP8 });
12924
13133
  }
12925
13134
  const windowToMs = toB.ms + DAY_MS3 - 1;
12926
13135
  const { items: entries, hasMore } = await queryTimeEntries(
@@ -12931,7 +13140,7 @@ async function recentTickets(_resource, from, to, http, opts) {
12931
13140
  { field: "resourceID", op: "eq", value: resourceID }
12932
13141
  ],
12933
13142
  clampWindowLimit(opts.limit),
12934
- OP6,
13143
+ OP8,
12935
13144
  http
12936
13145
  );
12937
13146
  const groups = /* @__PURE__ */ new Map();
@@ -12949,17 +13158,17 @@ async function recentTickets(_resource, from, to, http, opts) {
12949
13158
  }
12950
13159
  const ids2 = [...groups.keys()];
12951
13160
  const tickets = /* @__PURE__ */ new Map();
12952
- for (let c = 0; c < ids2.length; c += TICKET_CHUNK) {
12953
- const chunk = ids2.slice(c, c + TICKET_CHUNK);
13161
+ for (let c = 0; c < ids2.length; c += TICKET_CHUNK2) {
13162
+ const chunk = ids2.slice(c, c + TICKET_CHUNK2);
12954
13163
  const filter = [{ field: "id", op: "in", value: chunk }];
12955
13164
  const res = await http.request({
12956
13165
  method: "POST",
12957
13166
  idempotent: true,
12958
13167
  path: "/V1.0/Tickets/query",
12959
- body: { MaxRecords: TICKET_CHUNK, filter, IncludeFields: TICKET_FIELDS2 },
12960
- operation: OP6
13168
+ body: { MaxRecords: TICKET_CHUNK2, filter, IncludeFields: TICKET_FIELDS3 },
13169
+ operation: OP8
12961
13170
  });
12962
- for (const t of unwrapItems(res, OP6).items) if (typeof t.id === "number") tickets.set(t.id, t);
13171
+ for (const t of unwrapItems(res, OP8).items) if (typeof t.id === "number") tickets.set(t.id, t);
12963
13172
  }
12964
13173
  const items = [];
12965
13174
  const unresolvedTicketIDs = [];