node-autotask 0.39.3 → 0.40.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 (44) hide show
  1. package/ARCHITECTURE.md +6 -2
  2. package/CHANGELOG.md +32 -0
  3. package/MCP_TOOL_CATALOG.json +3 -3
  4. package/MCP_TOOL_MANIFEST.md +5 -5
  5. package/N8N_MCP_MANIFEST.json +2 -2
  6. package/N8N_MCP_MANIFEST.md +3 -3
  7. package/README.md +28 -36
  8. package/capabilities.json +1315 -1320
  9. package/dist/base-CVx2VMSj.d.cts +103 -0
  10. package/dist/base-CVx2VMSj.d.ts +103 -0
  11. package/dist/capabilities.cjs +2 -2
  12. package/dist/capabilities.js +1 -1
  13. package/dist/{chunk-MBI24PEF.cjs → chunk-E5W24BZB.cjs} +143 -143
  14. package/dist/{chunk-F4YDKUGY.cjs → chunk-EUGSF3U4.cjs} +287 -292
  15. package/dist/{chunk-VR6RYXTD.cjs → chunk-EVDVESQM.cjs} +1314 -1319
  16. package/dist/{chunk-BPRDYT5L.cjs → chunk-FEYVLIHD.cjs} +738 -1699
  17. package/dist/{chunk-5JAS6CAN.js → chunk-MTFRSD7P.js} +2 -2
  18. package/dist/{chunk-K6RZDRWX.js → chunk-O32L6GS5.js} +4 -9
  19. package/dist/{chunk-GSMFRPYB.js → chunk-VDVBXXJW.js} +570 -1531
  20. package/dist/{chunk-GDSCYY2G.js → chunk-Y6YJYKBZ.js} +1314 -1319
  21. package/dist/{index-DkZVRMeW.d.ts → index-BKp8_P2L.d.cts} +125 -806
  22. package/dist/{index-DkZVRMeW.d.cts → index-BKp8_P2L.d.ts} +125 -806
  23. package/dist/index.cjs +22 -50
  24. package/dist/index.d.cts +103 -102
  25. package/dist/index.d.ts +103 -102
  26. package/dist/index.js +18 -46
  27. package/dist/operations/index.cjs +178 -178
  28. package/dist/operations/index.d.cts +5 -4
  29. package/dist/operations/index.d.ts +5 -4
  30. package/dist/operations/index.js +3 -3
  31. package/dist/pool.cjs +17 -60
  32. package/dist/pool.d.cts +5 -13
  33. package/dist/pool.d.ts +5 -13
  34. package/dist/pool.js +13 -56
  35. package/dist/resources/index.cjs +4 -4
  36. package/dist/resources/index.d.cts +1 -1
  37. package/dist/resources/index.d.ts +1 -1
  38. package/dist/resources/index.js +3 -3
  39. package/dist/types/index.d.cts +1 -1
  40. package/dist/types/index.d.ts +1 -1
  41. package/docs/API.md +131 -320
  42. package/docs/N8N-ADAPTER.md +7 -7
  43. package/examples/mcp-server-http.ts +18 -18
  44. package/package.json +1 -1
@@ -1,6 +1,6 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } async function _asyncNullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return await rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } async function _asyncOptionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = await fn(value); } else if (op === 'call' || op === 'optionalCall') { value = await fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2; var _class3; var _class4; var _class5; var _class6; var _class7; var _class8;
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } async function _asyncNullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return await rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } async function _asyncOptionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = await fn(value); } else if (op === 'call' || op === 'optionalCall') { value = await fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2; var _class3;
2
2
 
3
- var _chunkVR6RYXTDcjs = require('./chunk-VR6RYXTD.cjs');
3
+ var _chunkEVDVESQMcjs = require('./chunk-EVDVESQM.cjs');
4
4
 
5
5
 
6
6
  var _chunkMJTKK3QGcjs = require('./chunk-MJTKK3QG.cjs');
@@ -181,14 +181,14 @@ var EmptyResponseError = class extends AutotaskError {
181
181
  this.name = "EmptyResponseError";
182
182
  }
183
183
  };
184
- var CircuitBreakerError = class extends AutotaskError {
185
- /** Seconds remaining in the open period (absent when the half-open probe budget is in flight). */
184
+ var MaintenanceError = class extends AutotaskError {
185
+ /** Seconds until the next probe slot. */
186
186
 
187
- /** True when the call was refused locally (the circuit is open) and never sent. */
187
+ /** True on a local refusal (the call was never sent); undefined when the call received the page. */
188
188
 
189
189
  constructor(message, options = {}) {
190
- super(message, { code: "CIRCUIT_BREAKER_OPEN", category: "policy", retryable: true, ...options });
191
- this.name = "CircuitBreakerError";
190
+ super(message, { code: "MAINTENANCE", category: "server", retryable: true, ...options });
191
+ this.name = "MaintenanceError";
192
192
  }
193
193
  };
194
194
  var HostPolicyDeniedError = class extends AutotaskError {
@@ -588,53 +588,36 @@ var MoveRefusedError = class extends AutotaskError {
588
588
 
589
589
  // src/cache.ts
590
590
  var READ_CACHE_MAX_ENTRIES = 4096;
591
- var ReadCache = (_class = class {
592
- /** The configured TTL in ms (the fallback for resources without an override). */
591
+ var REFERENCE_TTL_MS = 36e5;
592
+ var REFERENCE_ENTITIES = ["BillingCodes", "Countries", "Departments", "Phases", "Roles", "Services"];
593
+ var FIELD_CACHE_KINDS = ["info", "fields", "udf"];
594
+ function fieldKey(resourceKey, kind) {
595
+ return `${resourceKey}:${kind}`;
596
+ }
597
+ function readTtlFor(key, ttlMs) {
598
+ const entity = key.split("\0")[0].split("/").at(-2);
599
+ return entity !== void 0 && REFERENCE_ENTITIES.includes(entity) ? Math.max(ttlMs, REFERENCE_TTL_MS) : ttlMs;
600
+ }
601
+ var TtlCache = (_class = class {
602
+
593
603
 
594
- /** The per-resource TTL overrides (issue #32; absent = every resource uses `ttlMs`). */
595
604
 
596
605
  __init() {this.store = /* @__PURE__ */ new Map()}
597
- __init2() {this.epochCounter = 0}
598
- constructor(ttlMs, perResourceTtlMs) {;_class.prototype.__init.call(this);_class.prototype.__init2.call(this);
606
+ __init2() {this.inflightMap = /* @__PURE__ */ new Map()}
607
+ __init3() {this.epochCounter = 0}
608
+ constructor(ttlMs, options = {}) {;_class.prototype.__init.call(this);_class.prototype.__init2.call(this);_class.prototype.__init3.call(this);
599
609
  if (!Number.isInteger(ttlMs) || ttlMs <= 0) {
600
- throw new AutotaskConfigError(`cache.ttlMs must be a positive integer (got ${ttlMs})`);
610
+ throw new AutotaskConfigError(`cache ttlMs must be a positive integer (got ${String(ttlMs)})`);
601
611
  }
602
- if (perResourceTtlMs !== void 0) {
603
- if (perResourceTtlMs === null || typeof perResourceTtlMs !== "object" || Array.isArray(perResourceTtlMs)) {
604
- throw new AutotaskConfigError(
605
- `cache.perResourceTtlMs must be an object of resource key -> ms (got ${String(perResourceTtlMs)})`
606
- );
607
- }
608
- for (const [key, value] of Object.entries(perResourceTtlMs)) {
609
- if (key === "") {
610
- throw new AutotaskConfigError("cache.perResourceTtlMs keys must be non-empty resource keys");
611
- }
612
- if (!Number.isInteger(value) || value <= 0) {
613
- throw new AutotaskConfigError(
614
- `cache.perResourceTtlMs.${key} must be a positive integer (got ${String(value)})`
615
- );
616
- }
617
- }
612
+ const maxEntries = _nullishCoalesce(options.maxEntries, () => ( READ_CACHE_MAX_ENTRIES));
613
+ if (!Number.isInteger(maxEntries) || maxEntries <= 0) {
614
+ throw new AutotaskConfigError(`cache maxEntries must be a positive integer (got ${String(maxEntries)})`);
618
615
  }
619
616
  this.ttlMs = ttlMs;
620
- this.perResourceTtlMs = perResourceTtlMs;
621
- }
622
- /**
623
- * The TTL (ms) for a resource plan key: the `perResourceTtlMs` override
624
- * when the map is present and names the resource, else the base
625
- * `ttlMs` (absent map, absent key, and `undefined` all fall back).
626
- */
627
- ttlFor(resource) {
628
- if (resource !== void 0 && this.perResourceTtlMs !== void 0) {
629
- const override = this.perResourceTtlMs[resource];
630
- if (override !== void 0) return override;
631
- }
632
- return this.ttlMs;
617
+ this.maxEntries = maxEntries;
618
+ this.ttlFor = _nullishCoalesce(options.ttlFor, () => ( (() => ttlMs)));
633
619
  }
634
- /**
635
- * Read-through lookup: the live entry's value, or `undefined` (absent OR
636
- * expired — an expired entry is purged lazily on this read).
637
- */
620
+ /** The live value (a copy), or `undefined` when absent or expired. */
638
621
  get(key) {
639
622
  const entry = this.store.get(key);
640
623
  if (entry === void 0) return void 0;
@@ -644,19 +627,46 @@ var ReadCache = (_class = class {
644
627
  }
645
628
  return structuredClone(entry.value);
646
629
  }
647
- /** Store a successful read payload under `key`, live for `ttlFor(resource)`:
648
- * the per-resource override (issue #32) when named, else `ttlMs`.
649
- * `epoch` is the invalidation epoch the caller captured BEFORE its wire
650
- * read: a read that straddled a `clear()` (a write) is dropped, never
651
- * re-stored with its pre-write body. */
652
- set(key, value, resource, epoch = this.epochCounter) {
630
+ /** Store `value` under `key`. `epoch` is the value the caller read BEFORE its
631
+ * wire request: a read that straddled a `clear()` (a write) is dropped. */
632
+ set(key, value, epoch = this.epochCounter) {
653
633
  if (epoch !== this.epochCounter) return;
654
- if (this.store.size >= READ_CACHE_MAX_ENTRIES && this.purgeExpired() === 0) {
634
+ if (!this.store.has(key) && this.store.size >= this.maxEntries && this.purgeExpired() === 0) {
655
635
  const oldest = this.store.keys().next();
656
636
  if (!oldest.done) this.store.delete(oldest.value);
657
637
  }
638
+ const now = Date.now();
658
639
  this.store.delete(key);
659
- this.store.set(key, { value: structuredClone(value), expiresAt: Date.now() + this.ttlFor(resource) });
640
+ this.store.set(key, { value: structuredClone(value), storedAt: now, expiresAt: now + this.ttlFor(key) });
641
+ }
642
+ /** `{ hit, age }` without consuming or cloning the entry (age in ms since stored). */
643
+ peek(key) {
644
+ const entry = this.store.get(key);
645
+ if (entry === void 0 || entry.expiresAt <= Date.now()) return { hit: false, age: null };
646
+ return { hit: true, age: Date.now() - entry.storedAt };
647
+ }
648
+ /** The shared in-flight read for `key`, if any. */
649
+ inflight(key) {
650
+ return this.inflightMap.get(key);
651
+ }
652
+ /** Register an in-flight read so concurrent callers share it; dropped on settle. */
653
+ track(key, promise) {
654
+ this.inflightMap.set(key, promise);
655
+ const drop = () => {
656
+ if (this.inflightMap.get(key) === promise) this.inflightMap.delete(key);
657
+ };
658
+ promise.then(drop, drop);
659
+ return promise;
660
+ }
661
+ /** Drop one key. */
662
+ delete(key) {
663
+ this.inflightMap.delete(key);
664
+ return this.store.delete(key);
665
+ }
666
+ /** Drop every stored and in-flight key that starts with `prefix`. */
667
+ invalidate(prefix) {
668
+ for (const key of [...this.store.keys()]) if (key.startsWith(prefix)) this.store.delete(key);
669
+ for (const key of [...this.inflightMap.keys()]) if (key.startsWith(prefix)) this.inflightMap.delete(key);
660
670
  }
661
671
  /** Drop every expired entry (returns the number removed). */
662
672
  purgeExpired() {
@@ -670,24 +680,19 @@ var ReadCache = (_class = class {
670
680
  }
671
681
  return removed;
672
682
  }
673
- /** Current entry count (live + not-yet-purged expired). */
683
+ /** Stored entry count (live + not-yet-purged expired). */
674
684
  get size() {
675
685
  return this.store.size;
676
686
  }
677
- /** Drop one key (issue #36: a later positive outcome clears a stale 'absent' marker). */
678
- delete(key) {
679
- return this.store.delete(key);
680
- }
681
- /** The invalidation epoch: bumped by `clear()`; capture it before a wire
682
- * read and pass it to `set` so a read in flight across a write cannot
683
- * re-store its stale answer. */
687
+ /** Bumped by `clear()`; capture it before a wire read and pass it to `set`. */
684
688
  get epoch() {
685
689
  return this.epochCounter;
686
690
  }
687
- /** Drop everything (e.g. a caller that knows the tenant was reset). */
691
+ /** Drop everything and bump the epoch. */
688
692
  clear() {
689
693
  this.epochCounter += 1;
690
694
  this.store.clear();
695
+ this.inflightMap.clear();
691
696
  }
692
697
  }, _class);
693
698
 
@@ -790,279 +795,6 @@ var AGENT_PROFILE = {
790
795
  resolution: { maxScanRecords: 200, maxScanPages: 2 }
791
796
  };
792
797
 
793
- // src/cache-ttl-tables.ts
794
- var CACHE_TTL_REFERENCE_MS = 36e5;
795
- var CACHE_TTL_DEFAULT_MS = 3e5;
796
- var DEFAULT_PER_RESOURCE_TTL_MS = {
797
- "appointments": 3e5,
798
- "authenticate": 3e5,
799
- "billing_codes": 36e5,
800
- "billing_item_approval_levels": 3e5,
801
- "billing_items": 3e5,
802
- "change_request_links": 3e5,
803
- "checklist_libraries": 3e5,
804
- "checklist_library_checklist_items": 3e5,
805
- "classification_icons": 3e5,
806
- "companies": 3e5,
807
- "company_alerts": 3e5,
808
- "company_locations": 3e5,
809
- "company_notes": 3e5,
810
- "company_site_configurations": 3e5,
811
- "company_webhooks": 3e5,
812
- "configuration_item_billing_product_associations": 3e5,
813
- "configuration_item_categories": 3e5,
814
- "configuration_item_category_udf_associations": 3e5,
815
- "configuration_item_dns_records": 3e5,
816
- "configuration_item_notes": 3e5,
817
- "configuration_item_related_items": 3e5,
818
- "configuration_item_ssl_subject_alternative_names": 3e5,
819
- "configuration_item_types": 3e5,
820
- "configuration_item_webhooks": 3e5,
821
- "configuration_items": 3e5,
822
- "contact_group_contacts": 3e5,
823
- "contact_groups": 3e5,
824
- "contact_webhooks": 3e5,
825
- "contacts": 3e5,
826
- "contract_billing_rules": 3e5,
827
- "contract_block_hour_factors": 3e5,
828
- "contract_blocks": 3e5,
829
- "contract_charges": 3e5,
830
- "contract_exclusion_billing_codes": 3e5,
831
- "contract_exclusion_roles": 3e5,
832
- "contract_exclusion_set_excluded_roles": 3e5,
833
- "contract_exclusion_set_excluded_work_types": 3e5,
834
- "contract_exclusion_sets": 3e5,
835
- "contract_milestones": 3e5,
836
- "contract_notes": 3e5,
837
- "contract_rates": 3e5,
838
- "contract_retainers": 3e5,
839
- "contract_role_costs": 3e5,
840
- "contract_service_adjustments": 3e5,
841
- "contract_service_bundle_adjustments": 3e5,
842
- "contract_service_bundle_units": 3e5,
843
- "contract_service_bundles": 3e5,
844
- "contract_service_units": 3e5,
845
- "contract_services": 3e5,
846
- "contract_ticket_purchases": 3e5,
847
- "contracts": 3e5,
848
- "countries": 36e5,
849
- "currencies": 3e5,
850
- "departments": 36e5,
851
- "domain_registrars": 3e5,
852
- "entity_information": 3e5,
853
- "expense_item_attachments": 3e5,
854
- "expense_items": 3e5,
855
- "expense_reports": 3e5,
856
- "holiday_sets": 3e5,
857
- "holidays": 3e5,
858
- "inventory_products": 3e5,
859
- "invoice_templates": 3e5,
860
- "invoices": 3e5,
861
- "modules": 3e5,
862
- "notification_history": 3e5,
863
- "opportunities": 3e5,
864
- "opportunity_attachments": 3e5,
865
- "opportunity_categories": 3e5,
866
- "organizational_level1s": 3e5,
867
- "organizational_level2s": 3e5,
868
- "organizational_level_associations": 3e5,
869
- "phases": 36e5,
870
- "product_vendors": 3e5,
871
- "products": 3e5,
872
- "project_charges": 3e5,
873
- "project_notes": 3e5,
874
- "projects": 3e5,
875
- "purchase_approvals": 3e5,
876
- "purchase_order_item_receiving": 3e5,
877
- "purchase_order_items": 3e5,
878
- "purchase_orders": 3e5,
879
- "quote_items": 3e5,
880
- "quote_locations": 3e5,
881
- "quote_templates": 3e5,
882
- "quotes": 3e5,
883
- "resource_role_queues": 3e5,
884
- "resource_roles": 3e5,
885
- "resource_skills": 3e5,
886
- "resource_time_off_additional": 3e5,
887
- "resource_time_off_approvers": 3e5,
888
- "resource_time_off_balances": 3e5,
889
- "resources": 3e5,
890
- "roles": 36e5,
891
- "sales_orders": 3e5,
892
- "service_call_task_resources": 3e5,
893
- "service_call_tasks": 3e5,
894
- "service_call_ticket_resources": 3e5,
895
- "service_call_tickets": 3e5,
896
- "service_calls": 3e5,
897
- "service_level_agreement_results": 3e5,
898
- "services": 36e5,
899
- "skills": 3e5,
900
- "subscription_periods": 3e5,
901
- "subscriptions": 3e5,
902
- "survey_results": 3e5,
903
- "surveys": 3e5,
904
- "tag_aliases": 3e5,
905
- "tag_groups": 3e5,
906
- "tags": 3e5,
907
- "task_secondary_resources": 3e5,
908
- "tasks": 3e5,
909
- "tax_regions": 3e5,
910
- "threshold_information": 3e5,
911
- "ticket_additional_configuration_items": 3e5,
912
- "ticket_additional_contacts": 3e5,
913
- "ticket_attachments": 3e5,
914
- "ticket_categories": 3e5,
915
- "ticket_category_field_defaults": 3e5,
916
- "ticket_change_request_approvals": 3e5,
917
- "ticket_charges": 3e5,
918
- "ticket_checklist_items": 3e5,
919
- "ticket_checklist_libraries": 3e5,
920
- "ticket_history": 3e5,
921
- "ticket_note_attachments": 3e5,
922
- "ticket_note_webhooks": 3e5,
923
- "ticket_notes": 3e5,
924
- "ticket_secondary_resources": 3e5,
925
- "ticket_webhooks": 3e5,
926
- "tickets": 3e5,
927
- "time_entries": 3e5,
928
- "time_entry_attachments": 3e5,
929
- "time_off_requests": 3e5,
930
- "version": 3e5,
931
- "version_information": 3e5,
932
- "zone_information": 3e5
933
- };
934
- var RESOURCE_KEY_BY_PATH = {
935
- "/V1.0/Appointments": "appointments",
936
- "/V1.0/BillingCodes": "billing_codes",
937
- "/V1.0/BillingItemApprovalLevels": "billing_item_approval_levels",
938
- "/V1.0/BillingItems": "billing_items",
939
- "/V1.0/ChangeRequestLinks": "change_request_links",
940
- "/V1.0/ChecklistLibraries": "checklist_libraries",
941
- "/V1.0/ChecklistLibraryChecklistItems": "checklist_library_checklist_items",
942
- "/V1.0/ClassificationIcons": "classification_icons",
943
- "/V1.0/Companies": "companies",
944
- "/V1.0/CompanyAlerts": "company_alerts",
945
- "/V1.0/CompanyLocations": "company_locations",
946
- "/V1.0/CompanyNotes": "company_notes",
947
- "/V1.0/CompanySiteConfigurations": "company_site_configurations",
948
- "/V1.0/CompanyWebhooks": "company_webhooks",
949
- "/V1.0/ConfigurationItemBillingProductAssociations": "configuration_item_billing_product_associations",
950
- "/V1.0/ConfigurationItemCategories": "configuration_item_categories",
951
- "/V1.0/ConfigurationItemCategoryUdfAssociations": "configuration_item_category_udf_associations",
952
- "/V1.0/ConfigurationItemDnsRecords": "configuration_item_dns_records",
953
- "/V1.0/ConfigurationItemNotes": "configuration_item_notes",
954
- "/V1.0/ConfigurationItemRelatedItems": "configuration_item_related_items",
955
- "/V1.0/ConfigurationItemSslSubjectAlternativeNames": "configuration_item_ssl_subject_alternative_names",
956
- "/V1.0/ConfigurationItemTypes": "configuration_item_types",
957
- "/V1.0/ConfigurationItemWebhooks": "configuration_item_webhooks",
958
- "/V1.0/ConfigurationItems": "configuration_items",
959
- "/V1.0/ContactGroupContacts": "contact_group_contacts",
960
- "/V1.0/ContactGroups": "contact_groups",
961
- "/V1.0/ContactWebhooks": "contact_webhooks",
962
- "/V1.0/Contacts": "contacts",
963
- "/V1.0/ContractBillingRules": "contract_billing_rules",
964
- "/V1.0/ContractBlockHourFactors": "contract_block_hour_factors",
965
- "/V1.0/ContractBlocks": "contract_blocks",
966
- "/V1.0/ContractCharges": "contract_charges",
967
- "/V1.0/ContractExclusionBillingCodes": "contract_exclusion_billing_codes",
968
- "/V1.0/ContractExclusionRoles": "contract_exclusion_roles",
969
- "/V1.0/ContractExclusionSetExcludedRoles": "contract_exclusion_set_excluded_roles",
970
- "/V1.0/ContractExclusionSetExcludedWorkTypes": "contract_exclusion_set_excluded_work_types",
971
- "/V1.0/ContractExclusionSets": "contract_exclusion_sets",
972
- "/V1.0/ContractMilestones": "contract_milestones",
973
- "/V1.0/ContractNotes": "contract_notes",
974
- "/V1.0/ContractRates": "contract_rates",
975
- "/V1.0/ContractRetainers": "contract_retainers",
976
- "/V1.0/ContractRoleCosts": "contract_role_costs",
977
- "/V1.0/ContractServiceAdjustments": "contract_service_adjustments",
978
- "/V1.0/ContractServiceBundleAdjustments": "contract_service_bundle_adjustments",
979
- "/V1.0/ContractServiceBundleUnits": "contract_service_bundle_units",
980
- "/V1.0/ContractServiceBundles": "contract_service_bundles",
981
- "/V1.0/ContractServiceUnits": "contract_service_units",
982
- "/V1.0/ContractServices": "contract_services",
983
- "/V1.0/ContractTicketPurchases": "contract_ticket_purchases",
984
- "/V1.0/Contracts": "contracts",
985
- "/V1.0/Countries": "countries",
986
- "/V1.0/Currencies": "currencies",
987
- "/V1.0/Departments": "departments",
988
- "/V1.0/DomainRegistrars": "domain_registrars",
989
- "/V1.0/ExpenseItemAttachments": "expense_item_attachments",
990
- "/V1.0/ExpenseItems": "expense_items",
991
- "/V1.0/ExpenseReports": "expense_reports",
992
- "/V1.0/HolidaySets": "holiday_sets",
993
- "/V1.0/Holidays": "holidays",
994
- "/V1.0/InventoryProducts": "inventory_products",
995
- "/V1.0/InvoiceTemplates": "invoice_templates",
996
- "/V1.0/Invoices": "invoices",
997
- "/V1.0/NotificationHistory": "notification_history",
998
- "/V1.0/Opportunities": "opportunities",
999
- "/V1.0/OpportunityAttachments": "opportunity_attachments",
1000
- "/V1.0/OpportunityCategories": "opportunity_categories",
1001
- "/V1.0/OrganizationalLevel1s": "organizational_level1s",
1002
- "/V1.0/OrganizationalLevel2s": "organizational_level2s",
1003
- "/V1.0/OrganizationalLevelAssociations": "organizational_level_associations",
1004
- "/V1.0/Phases": "phases",
1005
- "/V1.0/ProductVendors": "product_vendors",
1006
- "/V1.0/Products": "products",
1007
- "/V1.0/ProjectCharges": "project_charges",
1008
- "/V1.0/ProjectNotes": "project_notes",
1009
- "/V1.0/Projects": "projects",
1010
- "/V1.0/PurchaseApprovals": "purchase_approvals",
1011
- "/V1.0/PurchaseOrderItemReceiving": "purchase_order_item_receiving",
1012
- "/V1.0/PurchaseOrderItems": "purchase_order_items",
1013
- "/V1.0/PurchaseOrders": "purchase_orders",
1014
- "/V1.0/QuoteItems": "quote_items",
1015
- "/V1.0/QuoteLocations": "quote_locations",
1016
- "/V1.0/QuoteTemplates": "quote_templates",
1017
- "/V1.0/Quotes": "quotes",
1018
- "/V1.0/ResourceRoleQueues": "resource_role_queues",
1019
- "/V1.0/ResourceRoles": "resource_roles",
1020
- "/V1.0/ResourceSkills": "resource_skills",
1021
- "/V1.0/ResourceTimeOffAdditional": "resource_time_off_additional",
1022
- "/V1.0/ResourceTimeOffApprovers": "resource_time_off_approvers",
1023
- "/V1.0/ResourceTimeOffBalances": "resource_time_off_balances",
1024
- "/V1.0/Resources": "resources",
1025
- "/V1.0/Roles": "roles",
1026
- "/V1.0/SalesOrders": "sales_orders",
1027
- "/V1.0/ServiceCallTaskResources": "service_call_task_resources",
1028
- "/V1.0/ServiceCallTasks": "service_call_tasks",
1029
- "/V1.0/ServiceCallTicketResources": "service_call_ticket_resources",
1030
- "/V1.0/ServiceCallTickets": "service_call_tickets",
1031
- "/V1.0/ServiceCalls": "service_calls",
1032
- "/V1.0/ServiceLevelAgreementResults": "service_level_agreement_results",
1033
- "/V1.0/Services": "services",
1034
- "/V1.0/Skills": "skills",
1035
- "/V1.0/SubscriptionPeriods": "subscription_periods",
1036
- "/V1.0/Subscriptions": "subscriptions",
1037
- "/V1.0/SurveyResults": "survey_results",
1038
- "/V1.0/Surveys": "surveys",
1039
- "/V1.0/TagAliases": "tag_aliases",
1040
- "/V1.0/TagGroups": "tag_groups",
1041
- "/V1.0/Tags": "tags",
1042
- "/V1.0/TaskSecondaryResources": "task_secondary_resources",
1043
- "/V1.0/Tasks": "tasks",
1044
- "/V1.0/TaxRegions": "tax_regions",
1045
- "/V1.0/TicketAdditionalConfigurationItems": "ticket_additional_configuration_items",
1046
- "/V1.0/TicketAdditionalContacts": "ticket_additional_contacts",
1047
- "/V1.0/TicketAttachments": "ticket_attachments",
1048
- "/V1.0/TicketCategories": "ticket_categories",
1049
- "/V1.0/TicketCategoryFieldDefaults": "ticket_category_field_defaults",
1050
- "/V1.0/TicketChangeRequestApprovals": "ticket_change_request_approvals",
1051
- "/V1.0/TicketCharges": "ticket_charges",
1052
- "/V1.0/TicketChecklistItems": "ticket_checklist_items",
1053
- "/V1.0/TicketChecklistLibraries": "ticket_checklist_libraries",
1054
- "/V1.0/TicketHistory": "ticket_history",
1055
- "/V1.0/TicketNoteAttachments": "ticket_note_attachments",
1056
- "/V1.0/TicketNoteWebhooks": "ticket_note_webhooks",
1057
- "/V1.0/TicketNotes": "ticket_notes",
1058
- "/V1.0/TicketSecondaryResources": "ticket_secondary_resources",
1059
- "/V1.0/TicketWebhooks": "ticket_webhooks",
1060
- "/V1.0/Tickets": "tickets",
1061
- "/V1.0/TimeEntries": "time_entries",
1062
- "/V1.0/TimeEntryAttachments": "time_entry_attachments",
1063
- "/V1.0/TimeOffRequests": "time_off_requests"
1064
- };
1065
-
1066
798
  // src/config/defaults.ts
1067
799
  var DEFAULT_PAGE_SIZE = 10;
1068
800
  var MAX_PAGE_SIZE = 500;
@@ -1075,9 +807,8 @@ var DEFAULT_MAX_CONCURRENT = 10;
1075
807
  var DEFAULT_MAX_RESPONSE_BYTES = 64 * 1024 * 1024;
1076
808
  var DEFAULT_MAX_SCAN_RECORDS = 500;
1077
809
  var DEFAULT_MAX_SCAN_PAGES = 4;
1078
- var DEFAULT_READ_CACHE_TTL_MS = CACHE_TTL_DEFAULT_MS;
1079
- var DEFAULT_FIELD_CACHE_TTL_MS = CACHE_TTL_DEFAULT_MS;
1080
- var DEFAULT_RESOURCE_NAME_CACHE_TTL_MS = CACHE_TTL_REFERENCE_MS;
810
+ var DEFAULT_READ_CACHE_TTL_MS = 3e5;
811
+ var DEFAULT_FIELD_CACHE_TTL_MS = 3e5;
1081
812
 
1082
813
  // src/credential-shape.ts
1083
814
  var QUOTE_CHARS = /* @__PURE__ */ new Set(["'", '"']);
@@ -1104,390 +835,105 @@ function credentialValueSummary(value) {
1104
835
  return value.length >= MIN_PREFIX_SUMMARY_LENGTH ? `len ${value.length}, starts "${value.slice(0, 2)}"` : "short value (length withheld)";
1105
836
  }
1106
837
 
1107
- // src/fieldCache.ts
1108
- var FIELD_CACHE_MAX_ENTITIES = 256;
1109
- var FIELD_CACHE_KINDS = ["info", "fields", "udf"];
1110
- var FieldCache = (_class2 = class _FieldCache {
1111
- /** The configured TTL in ms. */
1112
-
1113
- /** The entity bound (the cap applies to STORED entries; inflight never counts). */
1114
-
1115
- __init3() {this.store = /* @__PURE__ */ new Map()}
1116
- __init4() {this.inflightMap = /* @__PURE__ */ new Map()}
1117
- constructor(ttlMs, maxEntities = FIELD_CACHE_MAX_ENTITIES) {;_class2.prototype.__init3.call(this);_class2.prototype.__init4.call(this);
1118
- if (!Number.isInteger(ttlMs) || ttlMs <= 0) {
1119
- throw new AutotaskConfigError(`fieldCache.ttlMs must be a positive integer (got ${ttlMs})`);
1120
- }
1121
- if (!Number.isInteger(maxEntities) || maxEntities <= 0) {
1122
- throw new AutotaskConfigError(`fieldCache.maxEntities must be a positive integer (got ${maxEntities})`);
1123
- }
1124
- this.ttlMs = ttlMs;
1125
- this.maxEntities = maxEntities;
1126
- }
1127
- /** The composite key: one entity's catalogs are one key apart. */
1128
- static key(resourceKey, kind) {
1129
- return `${resourceKey}:${kind}`;
1130
- }
1131
- /**
1132
- * Read-through lookup: the live entry's value (a copy), or `undefined`
1133
- * (absent OR expired — an expired entry is purged lazily on this read).
1134
- */
1135
- get(resourceKey, kind) {
1136
- const entry = this.store.get(_FieldCache.key(resourceKey, kind));
1137
- if (entry === void 0) return void 0;
1138
- if (entry.expiresAt <= Date.now()) {
1139
- this.store.delete(_FieldCache.key(resourceKey, kind));
1140
- return void 0;
1141
- }
1142
- return structuredClone(entry.value);
1143
- }
1144
- /**
1145
- * Store a SUCCESSFUL, NON-EMPTY metadata value under (entity, kind),
1146
- * live for `ttlMs`. An EMPTY value (`[]` / `{}` / null) is refused
1147
- * silently (never cached — an empty is not an answer): the next call
1148
- * refetches.
1149
- */
1150
- set(resourceKey, kind, value) {
1151
- if (_FieldCache.isEmpty(value)) return;
1152
- const key = _FieldCache.key(resourceKey, kind);
1153
- if (this.store.size >= this.maxEntities && this.purgeExpired() === 0) {
1154
- const oldest = this.store.keys().next();
1155
- if (!oldest.done) this.store.delete(oldest.value);
1156
- }
1157
- this.store.delete(key);
1158
- this.store.set(key, { value: structuredClone(value), expiresAt: Date.now() + this.ttlMs });
1159
- }
1160
- /** True for the values that must never be cached (an empty is not an answer). */
1161
- static isEmpty(value) {
1162
- if (value === null || value === void 0) return true;
1163
- if (Array.isArray(value)) return value.length === 0;
1164
- if (typeof value === "object") return Object.keys(value).length === 0;
1165
- return false;
1166
- }
1167
- /**
1168
- * In-flight de-duplication: the shared in-flight promise for
1169
- * (entity, kind), or `undefined` (nothing in flight — the caller
1170
- * issues the request and registers it with `track`).
1171
- */
1172
- inflight(resourceKey, kind) {
1173
- return this.inflightMap.get(_FieldCache.key(resourceKey, kind));
1174
- }
1175
- /**
1176
- * Register an in-flight metadata read: concurrent callers for the same
1177
- * (entity, kind) await the SAME promise (one wire request). The entry
1178
- * is dropped on settle — success OR failure (a failure is never
1179
- * cached; the next caller refetches).
1180
- */
1181
- track(resourceKey, kind, promise) {
1182
- const key = _FieldCache.key(resourceKey, kind);
1183
- this.inflightMap.set(key, promise);
1184
- promise.then(
1185
- () => {
1186
- this.inflightMap.delete(key);
1187
- },
1188
- () => {
1189
- this.inflightMap.delete(key);
1190
- }
1191
- );
1192
- return promise;
1193
- }
1194
- /**
1195
- * The drift signal: drop the entity's STORED entries AND its in-flight
1196
- * promise (a refetch must not join a request whose premise just moved).
1197
- * A no-op when the entity holds nothing.
1198
- */
1199
- invalidate(resourceKey) {
1200
- for (const kind of FIELD_CACHE_KINDS) {
1201
- const key = _FieldCache.key(resourceKey, kind);
1202
- this.store.delete(key);
1203
- this.inflightMap.delete(key);
1204
- }
1205
- }
1206
- /**
1207
- * A non-mutating probe for the served op's per-row `cache` report:
1208
- * `hit` = a live (unexpired) entry exists; `age` = ms since that entry
1209
- * was stored (null when `hit` is false). Never purges, never clones.
1210
- */
1211
- peek(resourceKey, kind) {
1212
- const entry = this.store.get(_FieldCache.key(resourceKey, kind));
1213
- if (entry === void 0 || entry.expiresAt <= Date.now()) {
1214
- return { hit: false, age: null };
1215
- }
1216
- return { hit: true, age: Date.now() - (entry.expiresAt - this.ttlMs) };
1217
- }
1218
- /** Drop every expired entry (returns the number removed). */
1219
- purgeExpired() {
1220
- const now = Date.now();
1221
- let removed = 0;
1222
- for (const [key, entry] of this.store) {
1223
- if (entry.expiresAt <= now) {
1224
- this.store.delete(key);
1225
- removed += 1;
1226
- }
1227
- }
1228
- return removed;
1229
- }
1230
- /** Current entry count (live + not-yet-purged expired). */
1231
- get size() {
1232
- return this.store.size;
1233
- }
1234
- /** Drop everything (e.g. a caller that knows the tenant was reset). */
1235
- clear() {
1236
- this.store.clear();
1237
- this.inflightMap.clear();
1238
- }
1239
- }, _class2);
1240
-
1241
- // src/circuit-breaker.ts
1242
- var DEFAULT_CIRCUIT_BREAKER_WINDOW_MS = 1e4;
1243
- var DEFAULT_CIRCUIT_BREAKER_FAILURE_THRESHOLD = 5;
1244
- var DEFAULT_CIRCUIT_BREAKER_OPEN_TIMEOUT_MS = 3e4;
1245
- var DEFAULT_CIRCUIT_BREAKER_SUCCESSES_TO_CLOSE = 2;
1246
- var DEFAULT_CIRCUIT_BREAKER_MAX_ENTRIES = 1024;
1247
- var InMemoryCircuitBreakerStore = (_class3 = class {
1248
-
1249
- __init5() {this.states = /* @__PURE__ */ new Map()}
1250
- constructor(maxEntries = DEFAULT_CIRCUIT_BREAKER_MAX_ENTRIES) {;_class3.prototype.__init5.call(this);
1251
- if (!Number.isInteger(maxEntries) || maxEntries <= 0) {
1252
- throw new TypeError(`InMemoryCircuitBreakerStore maxEntries must be a positive integer (got ${String(maxEntries)})`);
1253
- }
1254
- this.maxEntries = maxEntries;
1255
- }
1256
- get(hostKey) {
1257
- return this.states.get(hostKey);
1258
- }
1259
- set(hostKey, state) {
1260
- if (this.states.has(hostKey)) {
1261
- this.states.set(hostKey, state);
1262
- return;
1263
- }
1264
- if (this.states.size >= this.maxEntries) {
1265
- let victim;
1266
- let victimActivity = Number.POSITIVE_INFINITY;
1267
- for (const [key, s] of this.states) {
1268
- const activity = s.openedAt !== null ? s.openedAt : s.failures.length > 0 ? s.failures[s.failures.length - 1] : 0;
1269
- if (activity < victimActivity) {
1270
- victimActivity = activity;
1271
- victim = key;
1272
- }
1273
- }
1274
- if (victim !== void 0) this.states.delete(victim);
1275
- }
1276
- this.states.set(hostKey, state);
1277
- }
1278
- delete(hostKey) {
1279
- this.states.delete(hostKey);
1280
- }
1281
- /** Drop every entry (test helper surface; also used by resetDefaultCircuitBreakerStore). */
1282
- clear() {
1283
- this.states.clear();
1284
- }
1285
- }, _class3);
1286
- var defaultStore;
1287
- function defaultCircuitBreakerStore() {
1288
- if (defaultStore === void 0) defaultStore = new InMemoryCircuitBreakerStore();
1289
- return defaultStore;
1290
- }
1291
- function resetDefaultCircuitBreakerStore() {
1292
- _optionalChain([defaultStore, 'optionalAccess', _5 => _5.clear, 'call', _6 => _6()]);
1293
- }
1294
- function buildBreakerKey(baseUrl) {
1295
- return new URL(baseUrl).host.toLowerCase();
1296
- }
1297
- function isBreakerFailure(err) {
1298
- if (err.notSent === true) return false;
1299
- if (err.code === "NETWORK_ERROR" || err.code === "TIMEOUT") return true;
1300
- const status = err.httpStatus;
1301
- if (status === void 0) return false;
1302
- if (status === 429) return true;
1303
- if (status >= 500 && status < 600) {
1304
- return _optionalChain([err, 'access', _7 => _7.details, 'optionalAccess', _8 => _8.deterministic]) !== true;
1305
- }
1306
- return err.code === "UNEXPECTED_CONTENT_TYPE";
1307
- }
1308
-
1309
- // src/cooldown.ts
838
+ // src/http/gates.ts
1310
839
  var DEFAULT_COOLDOWN_MAX_MS = 15 * 60 * 1e3;
1311
840
  var DEFAULT_COOLDOWN_DEFAULT_MS = 6e4;
1312
- var DEFAULT_COOLDOWN_MAX_ENTRIES = 1024;
1313
841
  var THREAD_THRESHOLD_MAX_MS = 1e4;
1314
842
  var THREAD_THRESHOLD_DEFAULT_MS = 5e3;
1315
- var InMemoryCooldownStore = (_class4 = class {
1316
-
1317
- __init6() {this.deadlines = /* @__PURE__ */ new Map()}
1318
- constructor(maxEntries = DEFAULT_COOLDOWN_MAX_ENTRIES) {;_class4.prototype.__init6.call(this);
1319
- if (!Number.isInteger(maxEntries) || maxEntries <= 0) {
1320
- throw new TypeError(`InMemoryCooldownStore maxEntries must be a positive integer (got ${String(maxEntries)})`);
1321
- }
1322
- this.maxEntries = maxEntries;
1323
- }
1324
- get(tenantKey) {
1325
- return this.deadlines.get(tenantKey);
843
+ var cooldowns = /* @__PURE__ */ new Map();
844
+ var maintenance = /* @__PURE__ */ new Map();
845
+ function hostKey(baseUrl) {
846
+ return new URL(baseUrl).host.toLowerCase();
847
+ }
848
+ function tenantKey(userName, baseUrl) {
849
+ return `${userName.toLowerCase()}@${hostKey(baseUrl)}`;
850
+ }
851
+ function cooldownRefusal(key, ctx) {
852
+ const deadline = cooldowns.get(key);
853
+ if (deadline === void 0) return void 0;
854
+ const remainingMs = deadline - Date.now();
855
+ if (remainingMs <= 0) {
856
+ cooldowns.delete(key);
857
+ return void 0;
1326
858
  }
1327
- set(tenantKey, deadline) {
1328
- if (this.deadlines.has(tenantKey)) {
1329
- this.deadlines.set(tenantKey, deadline);
1330
- return;
1331
- }
1332
- if (this.deadlines.size >= this.maxEntries) {
1333
- let victim;
1334
- let victimDeadline = Number.POSITIVE_INFINITY;
1335
- for (const [key, d] of this.deadlines) {
1336
- if (d < victimDeadline) {
1337
- victimDeadline = d;
1338
- victim = key;
1339
- }
1340
- }
1341
- if (victim !== void 0) this.deadlines.delete(victim);
859
+ const seconds = Math.ceil(remainingMs / 1e3);
860
+ const err = new RateLimitError(
861
+ `Tenant is in a rate-limit cooldown; the call was NOT sent. ${seconds}s remain \u2014 do not retry before then, and narrow the query (filter/includeFields) before retrying.`,
862
+ {
863
+ ...ctx,
864
+ code: "RATE_LIMITED_COOLDOWN",
865
+ retryable: false,
866
+ suggestedAction: `Wait ${seconds}s (per-tenant 429 cooldown) and retry with a narrower query; the call was not sent and consumed no vendor budget.`
1342
867
  }
1343
- this.deadlines.set(tenantKey, deadline);
1344
- }
1345
- delete(tenantKey) {
1346
- this.deadlines.delete(tenantKey);
1347
- }
1348
- /** Drop every entry (test helper surface; also used by resetDefaultCooldownStore). */
1349
- clear() {
1350
- this.deadlines.clear();
1351
- }
1352
- }, _class4);
1353
- var defaultStore2;
1354
- function defaultCooldownStore() {
1355
- if (defaultStore2 === void 0) defaultStore2 = new InMemoryCooldownStore();
1356
- return defaultStore2;
868
+ );
869
+ err.retryAfter = seconds;
870
+ err.notSent = true;
871
+ return err;
1357
872
  }
1358
- function resetDefaultCooldownStore() {
1359
- _optionalChain([defaultStore2, 'optionalAccess', _9 => _9.clear, 'call', _10 => _10()]);
873
+ function armCooldown(key, deadline) {
874
+ const now = Date.now();
875
+ for (const [k, d] of cooldowns) if (d <= now) cooldowns.delete(k);
876
+ cooldowns.set(key, deadline);
1360
877
  }
1361
- function buildTenantKey(userName, baseUrl) {
1362
- return `${userName.toLowerCase()}@${new URL(baseUrl).host.toLowerCase()}`;
878
+ function clearCooldown(key) {
879
+ cooldowns.delete(key);
1363
880
  }
1364
881
  function cooldownDurationMs(retryAfterSecs, opts) {
1365
882
  if (opts.threadThreshold) {
1366
- const secs = retryAfterSecs !== void 0 ? Math.min(retryAfterSecs, Math.round(THREAD_THRESHOLD_MAX_MS / 1e3)) : Math.round(THREAD_THRESHOLD_DEFAULT_MS / 1e3);
1367
- return Math.max(0, secs) * 1e3;
883
+ const ms = retryAfterSecs !== void 0 ? Math.min(retryAfterSecs * 1e3, THREAD_THRESHOLD_MAX_MS) : THREAD_THRESHOLD_DEFAULT_MS;
884
+ return Math.max(0, ms);
1368
885
  }
1369
- const rawMs = retryAfterSecs !== void 0 ? retryAfterSecs * 1e3 : opts.defaultMs;
1370
- return Math.min(rawMs, opts.maxMs);
886
+ return Math.min(retryAfterSecs !== void 0 ? retryAfterSecs * 1e3 : opts.defaultMs, opts.maxMs);
1371
887
  }
1372
-
1373
- // src/resourceNameCache.ts
1374
- var RESOURCE_NAME_CACHE_MAX_ENTRIES = 1024;
1375
- var RESOURCE_NAME_CACHE_BLIND_TTL_MS = 6e4;
1376
- var ResourceNameCache = (_class5 = class _ResourceNameCache {
1377
- /** The configured TTL in ms. */
1378
-
1379
- /** The entry bound (the cap applies to STORED entries; inflight never counts). */
1380
-
1381
- __init7() {this.store = /* @__PURE__ */ new Map()}
1382
- __init8() {this.inflightMap = /* @__PURE__ */ new Map()}
1383
- __init9() {this.epochCounter = 0}
1384
- constructor(ttlMs, maxEntries = RESOURCE_NAME_CACHE_MAX_ENTRIES) {;_class5.prototype.__init7.call(this);_class5.prototype.__init8.call(this);_class5.prototype.__init9.call(this);
1385
- if (!Number.isInteger(ttlMs) || ttlMs <= 0) {
1386
- throw new AutotaskConfigError(`resourceNameCache.ttlMs must be a positive integer (got ${ttlMs})`);
1387
- }
1388
- if (!Number.isInteger(maxEntries) || maxEntries <= 0) {
1389
- throw new AutotaskConfigError(`resourceNameCache.maxEntries must be a positive integer (got ${maxEntries})`);
1390
- }
1391
- this.ttlMs = ttlMs;
1392
- this.maxEntries = maxEntries;
1393
- }
1394
- /** The namespaced key: `resources:<canonical id>` (the namespace
1395
- * leaves a documented path to future FK tables — v1 serves
1396
- * `resources` only). */
1397
- static key(id) {
1398
- return `resources:${id}`;
1399
- }
1400
- /**
1401
- * Read-through lookup: the live entry (a copy), or `undefined`
1402
- * (absent OR expired — an expired entry is purged lazily on this
1403
- * read).
1404
- */
1405
- get(id) {
1406
- const entry = this.store.get(_ResourceNameCache.key(id));
1407
- if (entry === void 0) return void 0;
1408
- if (entry.expiresAt <= Date.now()) {
1409
- this.store.delete(_ResourceNameCache.key(id));
1410
- return void 0;
1411
- }
1412
- return structuredClone(entry.value);
1413
- }
1414
- /**
1415
- * Store a name answer under the id, live for `ttlMs`. ALWAYS stores
1416
- * (the no-name-part answer `{ name: null, blind: false }` is a stable
1417
- * fact and is cached — the documented difference from the fieldCache
1418
- * empty rule); past the cap, purge expired first, then evict the
1419
- * oldest-inserted (Map insertion order; refreshes re-insert so the
1420
- * memory guard drops the OLDEST-inserted entries first).
1421
- */
1422
- set(id, value, epoch = this.epochCounter) {
1423
- if (epoch !== this.epochCounter) return;
1424
- const key = _ResourceNameCache.key(id);
1425
- if (!this.store.has(key) && this.store.size >= this.maxEntries && this.purgeExpired() === 0) {
1426
- const oldest = this.store.keys().next();
1427
- if (!oldest.done) this.store.delete(oldest.value);
1428
- }
1429
- this.store.delete(key);
1430
- this.store.set(key, { value: structuredClone(value), expiresAt: Date.now() + (value.blind ? Math.min(this.ttlMs, RESOURCE_NAME_CACHE_BLIND_TTL_MS) : this.ttlMs) });
1431
- }
1432
- /**
1433
- * In-flight de-duplication: the shared in-flight promise for the id,
1434
- * or `undefined` (nothing in flight — the caller issues the request
1435
- * and registers it with `track`).
1436
- */
1437
- inflight(id) {
1438
- return this.inflightMap.get(_ResourceNameCache.key(id));
1439
- }
1440
- /**
1441
- * Register an in-flight name read: concurrent callers for the same id
1442
- * await the SAME promise (one wire request). The entry is dropped on
1443
- * settle — success OR failure (a failure is never cached; the next
1444
- * caller refetches).
1445
- */
1446
- track(id, promise) {
1447
- const key = _ResourceNameCache.key(id);
1448
- this.inflightMap.set(key, promise);
1449
- promise.then(
1450
- () => {
1451
- this.inflightMap.delete(key);
1452
- },
1453
- () => {
1454
- this.inflightMap.delete(key);
1455
- }
1456
- );
1457
- return promise;
1458
- }
1459
- /** Drop every expired entry (returns the number removed). */
1460
- purgeExpired() {
1461
- const now = Date.now();
1462
- let removed = 0;
1463
- for (const [key, entry] of this.store) {
1464
- if (entry.expiresAt <= now) {
1465
- this.store.delete(key);
1466
- removed += 1;
1467
- }
888
+ function admitMaintenance(host, ctx) {
889
+ const gate = maintenance.get(host);
890
+ if (gate === void 0) return "send";
891
+ const now = Date.now();
892
+ if (!gate.probing && now >= gate.nextProbeAt) {
893
+ gate.probing = true;
894
+ return gate;
895
+ }
896
+ const retryAfterMs = gate.probing ? Math.max(gate.nextProbeAt - now, gate.pollMs) : Math.max(0, gate.nextProbeAt - now);
897
+ const seconds = Math.max(1, Math.ceil(retryAfterMs / 1e3));
898
+ const since = new Date(gate.since).toISOString();
899
+ const err = new MaintenanceError(
900
+ `Zone ${host} is in a maintenance window (since ${since}); the call was NOT sent. Retry in about ${seconds}s.`,
901
+ {
902
+ ...ctx,
903
+ details: { since, retryAfterMs },
904
+ suggestedAction: `The Autotask zone is in its maintenance window (since ${since}). The call was not sent; retry later (the next probe is in about ${seconds}s, windows usually last 30-90 min).`
1468
905
  }
1469
- return removed;
1470
- }
1471
- /** The invalidation epoch: bumped by `clear()`; capture it before a wire
1472
- * read and pass it to `set` so a stale answer is dropped. */
1473
- get epoch() {
1474
- return this.epochCounter;
1475
- }
1476
- /** Current entry count (live + not-yet-purged expired). */
1477
- get size() {
1478
- return this.store.size;
1479
- }
1480
- /**
1481
- * Drop everything — the SCOPED invalidation target: a real write to
1482
- * the `resources` resource through the same client (a rename via this
1483
- * client is always fresh).
1484
- */
1485
- clear() {
1486
- this.epochCounter += 1;
1487
- this.store.clear();
1488
- this.inflightMap.clear();
1489
- }
1490
- }, _class5);
906
+ );
907
+ err.retryAfter = seconds;
908
+ err.notSent = true;
909
+ return err;
910
+ }
911
+ function openMaintenance(host, pollMs, logger, correlationId) {
912
+ if (maintenance.has(host)) return;
913
+ const now = Date.now();
914
+ maintenance.set(host, { since: now, pollMs, nextProbeAt: now + pollMs, probing: false });
915
+ _optionalChain([logger, 'access', _5 => _5.warn, 'optionalCall', _6 => _6(`[autotask] maintenance gate OPEN for ${host}: other calls on this zone fail fast (MAINTENANCE) until a probe sees a normal response`, correlationId)]);
916
+ }
917
+ function claimMaintenanceProbe(host) {
918
+ const gate = maintenance.get(host);
919
+ if (gate === void 0 || gate.probing) return void 0;
920
+ gate.probing = true;
921
+ return gate;
922
+ }
923
+ function releaseMaintenanceProbe(host, gate) {
924
+ if (maintenance.get(host) !== gate) return;
925
+ gate.probing = false;
926
+ gate.nextProbeAt = Date.now() + gate.pollMs;
927
+ }
928
+ function closeMaintenance(host, logger, correlationId) {
929
+ const gate = maintenance.get(host);
930
+ if (gate === void 0) return;
931
+ maintenance.delete(host);
932
+ _optionalChain([logger, 'access', _7 => _7.warn, 'optionalCall', _8 => _8(`[autotask] maintenance gate CLOSED for ${host} after ${Math.round((Date.now() - gate.since) / 1e3)}s`, correlationId)]);
933
+ }
934
+ function maintenanceSince(host) {
935
+ return _optionalChain([maintenance, 'access', _9 => _9.get, 'call', _10 => _10(host), 'optionalAccess', _11 => _11.since]);
936
+ }
1491
937
 
1492
938
  // src/host-policy.ts
1493
939
  function canonicalHost(origin) {
@@ -1770,123 +1216,73 @@ function resolveRateLimit(config, agent) {
1770
1216
  return rateLimit;
1771
1217
  }
1772
1218
  function resolveCooldown(config) {
1773
- const cooldownEnabled = _nullishCoalesce(_optionalChain([config, 'access', _11 => _11.cooldown, 'optionalAccess', _12 => _12.enabled]), () => ( true));
1774
- const cooldownMaxMs = _nullishCoalesce(_optionalChain([config, 'access', _13 => _13.cooldown, 'optionalAccess', _14 => _14.maxMs]), () => ( DEFAULT_COOLDOWN_MAX_MS));
1219
+ const cooldownEnabled = _nullishCoalesce(_optionalChain([config, 'access', _12 => _12.cooldown, 'optionalAccess', _13 => _13.enabled]), () => ( true));
1220
+ const cooldownMaxMs = _nullishCoalesce(_optionalChain([config, 'access', _14 => _14.cooldown, 'optionalAccess', _15 => _15.maxMs]), () => ( DEFAULT_COOLDOWN_MAX_MS));
1775
1221
  if (!Number.isInteger(cooldownMaxMs) || cooldownMaxMs <= 0) {
1776
1222
  throw new AutotaskConfigError("cooldown.maxMs must be a positive integer (ms)");
1777
1223
  }
1778
- const cooldownDefaultMs = _nullishCoalesce(_optionalChain([config, 'access', _15 => _15.cooldown, 'optionalAccess', _16 => _16.defaultMs]), () => ( DEFAULT_COOLDOWN_DEFAULT_MS));
1224
+ const cooldownDefaultMs = _nullishCoalesce(_optionalChain([config, 'access', _16 => _16.cooldown, 'optionalAccess', _17 => _17.defaultMs]), () => ( DEFAULT_COOLDOWN_DEFAULT_MS));
1779
1225
  if (!Number.isInteger(cooldownDefaultMs) || cooldownDefaultMs <= 0) {
1780
1226
  throw new AutotaskConfigError("cooldown.defaultMs must be a positive integer (ms)");
1781
1227
  }
1782
- const cooldownStore = _optionalChain([config, 'access', _17 => _17.cooldown, 'optionalAccess', _18 => _18.store]);
1783
- if (cooldownStore !== void 0 && (typeof cooldownStore.get !== "function" || typeof cooldownStore.set !== "function" || typeof cooldownStore.delete !== "function")) {
1784
- throw new AutotaskConfigError("cooldown.store must implement get/set/delete (a CooldownStore)");
1785
- }
1786
1228
  return {
1787
1229
  enabled: cooldownEnabled,
1788
1230
  maxMs: cooldownMaxMs,
1789
- defaultMs: cooldownDefaultMs,
1790
- store: cooldownStore
1791
- };
1792
- }
1793
- function resolveCircuitBreaker(config) {
1794
- const breakerEnabled = _nullishCoalesce(_optionalChain([config, 'access', _19 => _19.circuitBreaker, 'optionalAccess', _20 => _20.enabled]), () => ( false));
1795
- const breakerWindowMs = _nullishCoalesce(_optionalChain([config, 'access', _21 => _21.circuitBreaker, 'optionalAccess', _22 => _22.failureWindowMs]), () => ( DEFAULT_CIRCUIT_BREAKER_WINDOW_MS));
1796
- if (!Number.isInteger(breakerWindowMs) || breakerWindowMs <= 0) {
1797
- throw new AutotaskConfigError("circuitBreaker.failureWindowMs must be a positive integer (ms)");
1798
- }
1799
- const breakerThreshold = _nullishCoalesce(_optionalChain([config, 'access', _23 => _23.circuitBreaker, 'optionalAccess', _24 => _24.failureThreshold]), () => ( DEFAULT_CIRCUIT_BREAKER_FAILURE_THRESHOLD));
1800
- if (!Number.isInteger(breakerThreshold) || breakerThreshold <= 0) {
1801
- throw new AutotaskConfigError("circuitBreaker.failureThreshold must be a positive integer");
1802
- }
1803
- const breakerOpenTimeoutMs = _nullishCoalesce(_optionalChain([config, 'access', _25 => _25.circuitBreaker, 'optionalAccess', _26 => _26.openTimeoutMs]), () => ( DEFAULT_CIRCUIT_BREAKER_OPEN_TIMEOUT_MS));
1804
- if (!Number.isInteger(breakerOpenTimeoutMs) || breakerOpenTimeoutMs <= 0) {
1805
- throw new AutotaskConfigError("circuitBreaker.openTimeoutMs must be a positive integer (ms)");
1806
- }
1807
- const breakerSuccessesToClose = _nullishCoalesce(_optionalChain([config, 'access', _27 => _27.circuitBreaker, 'optionalAccess', _28 => _28.successesToClose]), () => ( DEFAULT_CIRCUIT_BREAKER_SUCCESSES_TO_CLOSE));
1808
- if (!Number.isInteger(breakerSuccessesToClose) || breakerSuccessesToClose <= 0) {
1809
- throw new AutotaskConfigError("circuitBreaker.successesToClose must be a positive integer");
1810
- }
1811
- const breakerHalfOpenProbes = _nullishCoalesce(_optionalChain([config, 'access', _29 => _29.circuitBreaker, 'optionalAccess', _30 => _30.halfOpenProbes]), () => ( breakerSuccessesToClose));
1812
- if (!Number.isInteger(breakerHalfOpenProbes) || breakerHalfOpenProbes <= 0) {
1813
- throw new AutotaskConfigError("circuitBreaker.halfOpenProbes must be a positive integer");
1814
- }
1815
- if (breakerHalfOpenProbes < breakerSuccessesToClose) {
1816
- throw new AutotaskConfigError(
1817
- `circuitBreaker.halfOpenProbes (${breakerHalfOpenProbes}) must be >= successesToClose (${breakerSuccessesToClose}) \u2014 the breaker could never close`
1818
- );
1819
- }
1820
- const breakerStore = _optionalChain([config, 'access', _31 => _31.circuitBreaker, 'optionalAccess', _32 => _32.store]);
1821
- if (breakerStore !== void 0 && (typeof breakerStore.get !== "function" || typeof breakerStore.set !== "function" || typeof breakerStore.delete !== "function")) {
1822
- throw new AutotaskConfigError("circuitBreaker.store must implement get/set/delete (a CircuitBreakerStore)");
1823
- }
1824
- const breakerIsFailure = _optionalChain([config, 'access', _33 => _33.circuitBreaker, 'optionalAccess', _34 => _34.isFailure]);
1825
- if (breakerIsFailure !== void 0 && typeof breakerIsFailure !== "function") {
1826
- throw new AutotaskConfigError("circuitBreaker.isFailure must be a function");
1827
- }
1828
- return {
1829
- enabled: breakerEnabled,
1830
- failureWindowMs: breakerWindowMs,
1831
- failureThreshold: breakerThreshold,
1832
- openTimeoutMs: breakerOpenTimeoutMs,
1833
- successesToClose: breakerSuccessesToClose,
1834
- halfOpenProbes: breakerHalfOpenProbes,
1835
- store: breakerStore,
1836
- isFailure: breakerIsFailure
1231
+ defaultMs: cooldownDefaultMs
1837
1232
  };
1838
1233
  }
1839
1234
  function resolveResolution(config, agent) {
1840
- const maxScanRecords = _nullishCoalesce(_optionalChain([config, 'access', _35 => _35.resolution, 'optionalAccess', _36 => _36.maxScanRecords]), () => ( (agent ? AGENT_PROFILE.resolution.maxScanRecords : DEFAULT_MAX_SCAN_RECORDS)));
1235
+ const maxScanRecords = _nullishCoalesce(_optionalChain([config, 'access', _18 => _18.resolution, 'optionalAccess', _19 => _19.maxScanRecords]), () => ( (agent ? AGENT_PROFILE.resolution.maxScanRecords : DEFAULT_MAX_SCAN_RECORDS)));
1841
1236
  if (!Number.isInteger(maxScanRecords) || maxScanRecords < 1) {
1842
1237
  throw new AutotaskConfigError("resolution.maxScanRecords must be an integer >= 1");
1843
1238
  }
1844
- const maxScanPages = _nullishCoalesce(_optionalChain([config, 'access', _37 => _37.resolution, 'optionalAccess', _38 => _38.maxScanPages]), () => ( (agent ? AGENT_PROFILE.resolution.maxScanPages : DEFAULT_MAX_SCAN_PAGES)));
1239
+ const maxScanPages = _nullishCoalesce(_optionalChain([config, 'access', _20 => _20.resolution, 'optionalAccess', _21 => _21.maxScanPages]), () => ( (agent ? AGENT_PROFILE.resolution.maxScanPages : DEFAULT_MAX_SCAN_PAGES)));
1845
1240
  if (!Number.isInteger(maxScanPages) || maxScanPages < 1) {
1846
1241
  throw new AutotaskConfigError("resolution.maxScanPages must be an integer >= 1");
1847
1242
  }
1848
- const scanOnMiss = _optionalChain([config, 'access', _39 => _39.resolution, 'optionalAccess', _40 => _40.scanOnMiss]) === void 0 ? false : config.resolution.scanOnMiss;
1243
+ const scanOnMiss = _optionalChain([config, 'access', _22 => _22.resolution, 'optionalAccess', _23 => _23.scanOnMiss]) === void 0 ? false : config.resolution.scanOnMiss;
1849
1244
  if (typeof scanOnMiss !== "boolean") {
1850
1245
  throw new AutotaskConfigError("resolution.scanOnMiss must be a boolean");
1851
1246
  }
1852
1247
  return { maxScanRecords, maxScanPages, scanOnMiss };
1853
1248
  }
1249
+ var FIELD_CACHE_MAX_ENTRIES = 256;
1854
1250
  function resolveCaches(config) {
1855
- const cacheBlock = config.cache === false || config.cache === void 0 ? void 0 : config.cache;
1856
- resolveReadCacheStore(cacheBlock);
1857
- const fieldCacheBlock = config.fieldCache === false || config.fieldCache === void 0 ? void 0 : config.fieldCache;
1858
- const resourceNameCacheBlock = config.resourceNameCache === false || config.resourceNameCache === void 0 ? void 0 : config.resourceNameCache;
1859
- const readCache = config.cache === false ? void 0 : _nullishCoalesce(_optionalChain([cacheBlock, 'optionalAccess', _41 => _41.store]), () => ( buildReadCache(config)));
1860
- const fieldCache = config.fieldCache === false ? void 0 : new FieldCache(
1861
- _nullishCoalesce(_optionalChain([fieldCacheBlock, 'optionalAccess', _42 => _42.ttlMs]), () => ( DEFAULT_FIELD_CACHE_TTL_MS)),
1862
- _nullishCoalesce(_optionalChain([fieldCacheBlock, 'optionalAccess', _43 => _43.maxEntities]), () => ( FIELD_CACHE_MAX_ENTITIES))
1863
- );
1864
- const resourceNameCache = config.resourceNameCache === false ? void 0 : new ResourceNameCache(
1865
- _nullishCoalesce(_optionalChain([resourceNameCacheBlock, 'optionalAccess', _44 => _44.ttlMs]), () => ( DEFAULT_RESOURCE_NAME_CACHE_TTL_MS)),
1866
- _nullishCoalesce(_optionalChain([resourceNameCacheBlock, 'optionalAccess', _45 => _45.maxEntries]), () => ( RESOURCE_NAME_CACHE_MAX_ENTRIES))
1867
- );
1868
- return { readCache, fieldCache, resourceNameCache };
1869
- }
1870
- function resolveReadCacheStore(cacheBlock) {
1871
- if (cacheBlock === void 0 || cacheBlock === false || cacheBlock.store === void 0) return;
1872
- if (!(cacheBlock.store instanceof ReadCache)) {
1873
- throw new AutotaskConfigError(
1874
- "cache.store must be a ReadCache instance (import { ReadCache } from the SDK; the pool supplies its own)"
1875
- );
1251
+ for (const removed of ["fieldCache", "resourceNameCache"]) {
1252
+ if (removed in config) {
1253
+ throw new AutotaskConfigError(
1254
+ `${removed} was removed in 0.40.0: use cache: false (every channel off) or cache: { ttlMs?, fieldTtlMs? }`
1255
+ );
1256
+ }
1876
1257
  }
1877
- if (cacheBlock.ttlMs !== void 0 || cacheBlock.perResourceTtlMs !== void 0) {
1878
- throw new AutotaskConfigError(
1879
- "cache.store cannot be combined with cache.ttlMs or cache.perResourceTtlMs \u2014 the supplied ReadCache instance owns its TTLs"
1880
- );
1258
+ const block = config.cache;
1259
+ if (block === false) return {};
1260
+ if (_optionalChain([block, 'optionalAccess', _24 => _24.store]) !== void 0) {
1261
+ if (!(block.store instanceof TtlCache)) {
1262
+ throw new AutotaskConfigError("cache.store must be a TtlCache instance (import { TtlCache } from the SDK; the pool supplies its own)");
1263
+ }
1264
+ if (block.ttlMs !== void 0) {
1265
+ throw new AutotaskConfigError("cache.store cannot be combined with cache.ttlMs: the supplied instance owns its TTL");
1266
+ }
1267
+ }
1268
+ return {
1269
+ readCache: _nullishCoalesce(_optionalChain([block, 'optionalAccess', _25 => _25.store]), () => ( buildReadCache(config))),
1270
+ fieldCache: new TtlCache(ttlOption(_optionalChain([block, 'optionalAccess', _26 => _26.fieldTtlMs]), DEFAULT_FIELD_CACHE_TTL_MS, "cache.fieldTtlMs"), {
1271
+ maxEntries: FIELD_CACHE_MAX_ENTRIES
1272
+ })
1273
+ };
1274
+ }
1275
+ function ttlOption(value, fallback, name) {
1276
+ const ms = _nullishCoalesce(value, () => ( fallback));
1277
+ if (!Number.isInteger(ms) || ms <= 0) {
1278
+ throw new AutotaskConfigError(`${name} must be a positive integer (got ${String(ms)})`);
1881
1279
  }
1280
+ return ms;
1882
1281
  }
1883
1282
  function buildReadCache(config) {
1884
1283
  if (config.cache === false) return void 0;
1885
- const cacheBlock = config.cache === void 0 ? void 0 : config.cache;
1886
- return new ReadCache(
1887
- _nullishCoalesce(_optionalChain([cacheBlock, 'optionalAccess', _46 => _46.ttlMs]), () => ( DEFAULT_READ_CACHE_TTL_MS)),
1888
- { ...DEFAULT_PER_RESOURCE_TTL_MS, ..._optionalChain([cacheBlock, 'optionalAccess', _47 => _47.perResourceTtlMs]) }
1889
- );
1284
+ const ttlMs = ttlOption(_optionalChain([config, 'access', _27 => _27.cache, 'optionalAccess', _28 => _28.ttlMs]), DEFAULT_READ_CACHE_TTL_MS, "cache.ttlMs");
1285
+ return new TtlCache(ttlMs, { ttlFor: (key) => readTtlFor(key, ttlMs) });
1890
1286
  }
1891
1287
  function resolveConfig(config) {
1892
1288
  if (config === null || typeof config !== "object") {
@@ -1899,16 +1295,15 @@ function resolveConfig(config) {
1899
1295
  const limits = resolveLimits(config, agent);
1900
1296
  const rateLimit = resolveRateLimit(config, agent);
1901
1297
  const cooldown = resolveCooldown(config);
1902
- const circuitBreaker = resolveCircuitBreaker(config);
1903
1298
  const resolution = resolveResolution(config, agent);
1904
1299
  const redactPersonalData = _nullishCoalesce(config.redactPersonalData, () => ( true));
1905
1300
  const caches = resolveCaches(config);
1906
- const update = { putFallback: _optionalChain([config, 'access', _48 => _48.update, 'optionalAccess', _49 => _49.putFallback]) === true };
1301
+ const update = { putFallback: _optionalChain([config, 'access', _29 => _29.update, 'optionalAccess', _30 => _30.putFallback]) === true };
1907
1302
  const reResolveZoneOn401 = config.reResolveZoneOn401 === true;
1908
1303
  const logger = _nullishCoalesce(config.logger, () => ( NoopLogger));
1909
1304
  if (credentials.baseUrl.startsWith("http:")) {
1910
1305
  const host = new URL(credentials.baseUrl).host;
1911
- _optionalChain([logger, 'access', _50 => _50.warn, 'optionalCall', _51 => _51(
1306
+ _optionalChain([logger, 'access', _31 => _31.warn, 'optionalCall', _32 => _32(
1912
1307
  `[autotask] cleartext credentials to ${host} \u2014 https recommended (baseUrl uses http:; all catalog zones are https)`
1913
1308
  )]);
1914
1309
  }
@@ -1924,7 +1319,6 @@ function resolveConfig(config) {
1924
1319
  logger,
1925
1320
  rateLimit,
1926
1321
  cooldown,
1927
- circuitBreaker,
1928
1322
  resolution,
1929
1323
  onAudit: config.onAudit,
1930
1324
  redactPersonalData,
@@ -1989,7 +1383,7 @@ function parseIdentifier(value, label = "id") {
1989
1383
  return parsed;
1990
1384
  }
1991
1385
  function flattenUdfs(record, options) {
1992
- const prefix = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _52 => _52.prefix]), () => ( "udf_"));
1386
+ const prefix = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _33 => _33.prefix]), () => ( "udf_"));
1993
1387
  const out = { ...record };
1994
1388
  const udfs = record.userDefinedFields;
1995
1389
  if (!Array.isArray(udfs)) return out;
@@ -2311,7 +1705,7 @@ function maintenanceProbe(text) {
2311
1705
  return Buffer.from(text, "utf8").subarray(0, MAINTENANCE_PROBE_BYTES).toString("utf8");
2312
1706
  }
2313
1707
  function looksLikeMaintenanceText(text) {
2314
- return /maintenance|temporarily unavailable|service unavailable|outage|downtime|down for/i.test(
1708
+ return /maintenance|outage|downtime|down for/i.test(
2315
1709
  maintenanceProbe(text)
2316
1710
  );
2317
1711
  }
@@ -16354,7 +15748,7 @@ function attachEmptyReasonDetails(err) {
16354
15748
  const resource = err.operation.slice(0, dot);
16355
15749
  const op = err.operation.slice(dot + 1);
16356
15750
  const surface = err.operation;
16357
- const stamp = _optionalChain([EMPTY_REASON_STAMPS, 'access', _53 => _53[resource], 'optionalAccess', _54 => _54[op]]);
15751
+ const stamp = _optionalChain([EMPTY_REASON_STAMPS, 'access', _34 => _34[resource], 'optionalAccess', _35 => _35[op]]);
16358
15752
  const pointer = POINTER_NOTES[surface];
16359
15753
  const isGetMiss = err instanceof NotFoundError && op === "get";
16360
15754
  if (stamp === void 0 && pointer === void 0 && !isGetMiss) return;
@@ -16458,88 +15852,6 @@ function passthrough(body) {
16458
15852
  return body;
16459
15853
  }
16460
15854
 
16461
- // src/latest-count-cache.ts
16462
- var LATEST_COUNT_CACHE_TTL_MS = 3e4;
16463
- var LATEST_COUNT_CACHE_MAX_ENTRIES = 4096;
16464
- var LatestCountCache = (_class6 = class {
16465
- /** The TTL in ms. */
16466
-
16467
- /** The entry bound (applies to STORED entries). */
16468
-
16469
- __init10() {this.store = /* @__PURE__ */ new Map()}
16470
- /** The invalidation epoch (the `ReadCache` pattern — issue #139): bumped
16471
- * by `clear()`; capture it before a wire read and pass it to `set` so a
16472
- * probe in flight across a write cannot re-store its stale answer. */
16473
- __init11() {this.epochCounter = 0}
16474
- constructor(ttlMs, maxEntries = LATEST_COUNT_CACHE_MAX_ENTRIES) {;_class6.prototype.__init10.call(this);_class6.prototype.__init11.call(this);
16475
- if (!Number.isInteger(ttlMs) || ttlMs <= 0) {
16476
- throw new AutotaskConfigError(`latestCountCache.ttlMs must be a positive integer (got ${String(ttlMs)})`);
16477
- }
16478
- if (!Number.isInteger(maxEntries) || maxEntries <= 0) {
16479
- throw new AutotaskConfigError(`latestCountCache.maxEntries must be a positive integer (got ${String(maxEntries)})`);
16480
- }
16481
- this.ttlMs = ttlMs;
16482
- this.maxEntries = maxEntries;
16483
- }
16484
- /** The composite key: (resource plan key, canonical filter serialisation)
16485
- * — the `\u0000` separator is unencodable in a filter value, so no two
16486
- * (resource, conditions) pairs collide. */
16487
- static key(resourceKey, conditions) {
16488
- return `${resourceKey}\0${JSON.stringify(_nullishCoalesce(conditions, () => ( [])))}`;
16489
- }
16490
- /** The live entry's count, or `undefined` (absent OR expired — an expired
16491
- * entry is purged lazily on this read). */
16492
- get(key) {
16493
- const entry = this.store.get(key);
16494
- if (entry === void 0) return void 0;
16495
- if (entry.expiresAt <= Date.now()) {
16496
- this.store.delete(key);
16497
- return void 0;
16498
- }
16499
- return entry.value;
16500
- }
16501
- /** The invalidation epoch: bumped by `clear()`; capture it before a wire
16502
- * read and pass it to `set` so a probe in flight across a write cannot
16503
- * re-store its stale answer (issue #139, the `ReadCache` pattern). */
16504
- get epoch() {
16505
- return this.epochCounter;
16506
- }
16507
- /** Store a SUCCESSFUL plain-number count under `key`, live for `ttlMs`.
16508
- * `epoch` is the invalidation epoch the caller captured BEFORE its wire
16509
- * read: a probe that straddled a `clear()` (a write) is dropped, never
16510
- * re-stored with its pre-write body (issue #139). Past the cap: purge
16511
- * expired entries first, then evict the oldest-inserted (Map iteration
16512
- * order = insertion order). */
16513
- set(key, value, epoch = this.epochCounter) {
16514
- if (epoch !== this.epochCounter) return;
16515
- if (!Number.isFinite(value)) return;
16516
- const entry = { value, expiresAt: Date.now() + this.ttlMs };
16517
- if (!this.store.has(key) && this.store.size >= this.maxEntries) {
16518
- for (const [k, e] of this.store) {
16519
- if (e.expiresAt <= Date.now()) this.store.delete(k);
16520
- }
16521
- if (this.store.size >= this.maxEntries) {
16522
- const oldest = this.store.keys().next();
16523
- if (!oldest.done) this.store.delete(oldest.value);
16524
- }
16525
- }
16526
- this.store.set(key, entry);
16527
- }
16528
- /** Drop one entry (absent = no-op). Issue #157: the stale-LOW recovery
16529
- * deletes the cached total before its one fresh wire re-probe, so the
16530
- * re-probe cannot be served by the entry it is meant to replace. */
16531
- delete(key) {
16532
- this.store.delete(key);
16533
- }
16534
- /** Clear every entry (a real write through the owning client — the
16535
- * write-then-read re-fetch rule) and bump the invalidation epoch: a
16536
- * `set` captured before this `clear` is discarded (issue #139). */
16537
- clear() {
16538
- this.epochCounter += 1;
16539
- this.store.clear();
16540
- }
16541
- }, _class6);
16542
-
16543
15855
  // src/http/body.ts
16544
15856
  async function readBodyText(res, max, what, ctx) {
16545
15857
  const tooLarge = () => new ResponseRejectedError(`Response for ${what} exceeds maxResponseBytes (${max})`, {
@@ -16549,7 +15861,7 @@ async function readBodyText(res, max, what, ctx) {
16549
15861
  });
16550
15862
  const declared = Number(res.headers.get("Content-Length"));
16551
15863
  if (Number.isFinite(declared) && declared > max) {
16552
- await _optionalChain([res, 'access', _55 => _55.body, 'optionalAccess', _56 => _56.cancel, 'call', _57 => _57(), 'access', _58 => _58.catch, 'call', _59 => _59(() => void 0)]);
15864
+ await _optionalChain([res, 'access', _36 => _36.body, 'optionalAccess', _37 => _37.cancel, 'call', _38 => _38(), 'access', _39 => _39.catch, 'call', _40 => _40(() => void 0)]);
16553
15865
  throw tooLarge();
16554
15866
  }
16555
15867
  if (res.body === null) return "";
@@ -16570,7 +15882,7 @@ async function readBodyText(res, max, what, ctx) {
16570
15882
  return text + decoder.decode();
16571
15883
  }
16572
15884
  function isJsonMediaType(header) {
16573
- const mediaType = _nullishCoalesce(_optionalChain([header, 'access', _60 => _60.split, 'call', _61 => _61(";"), 'access', _62 => _62[0], 'optionalAccess', _63 => _63.trim, 'call', _64 => _64(), 'access', _65 => _65.toLowerCase, 'call', _66 => _66()]), () => ( ""));
15885
+ const mediaType = _nullishCoalesce(_optionalChain([header, 'access', _41 => _41.split, 'call', _42 => _42(";"), 'access', _43 => _43[0], 'optionalAccess', _44 => _44.trim, 'call', _45 => _45(), 'access', _46 => _46.toLowerCase, 'call', _47 => _47()]), () => ( ""));
16574
15886
  return mediaType.startsWith("application/json") || mediaType.endsWith("+json");
16575
15887
  }
16576
15888
  function bodySnippet(text, redactPersonalData) {
@@ -16578,129 +15890,16 @@ function bodySnippet(text, redactPersonalData) {
16578
15890
  return redactResponseBody(bytes, { redactPersonalData });
16579
15891
  }
16580
15892
 
16581
- // src/http/breaker-budget.ts
16582
- function cooldownRefusal(store, tenantKey, ctx) {
16583
- const deadline = store.get(tenantKey);
16584
- if (deadline === void 0) return void 0;
16585
- const remainingMs = deadline - Date.now();
16586
- if (remainingMs > 0) {
16587
- const seconds = Math.ceil(remainingMs / 1e3);
16588
- const err = new RateLimitError(
16589
- `Tenant is in a rate-limit cooldown; the call was NOT sent. ${seconds}s remain \u2014 do not retry before then, and narrow the query (filter/includeFields) before retrying.`,
16590
- {
16591
- ...ctx,
16592
- code: "RATE_LIMITED_COOLDOWN",
16593
- retryable: false,
16594
- suggestedAction: `Wait ${seconds}s (per-tenant 429 cooldown) and retry with a narrower query; the call was not sent and consumed no vendor budget.`
16595
- }
16596
- );
16597
- err.retryAfter = seconds;
16598
- err.notSent = true;
16599
- return err;
16600
- }
16601
- store.delete(tenantKey);
16602
- return void 0;
16603
- }
16604
- function evaluateBreakerGate(breaker, key, ctx) {
16605
- const now = Date.now();
16606
- const st = breaker.store.get(key);
16607
- if (st !== void 0 && st.openedAt !== null) {
16608
- const openUntil = st.openedAt + breaker.openTimeoutMs;
16609
- if (now < openUntil) {
16610
- const remainingMs = openUntil - now;
16611
- const seconds = Math.ceil(remainingMs / 1e3);
16612
- const err = new CircuitBreakerError(
16613
- `The circuit breaker for ${key} is open; the call was NOT sent. ${seconds}s remain \u2014 retry the same call after the wait (a host-scoped vendor fault, not the request).`,
16614
- {
16615
- ...ctx,
16616
- details: { state: "open", retryAfterMs: remainingMs, failureCount: st.failures.length },
16617
- suggestedAction: `Wait ${seconds}s (the circuit is open for ${key}) and retry the SAME call; the call was not sent and consumed no vendor budget.`
16618
- }
16619
- );
16620
- err.retryAfter = seconds;
16621
- err.notSent = true;
16622
- return { refusal: err };
16623
- }
16624
- if (st.probes >= breaker.halfOpenProbes) {
16625
- const err = new CircuitBreakerError(
16626
- `The circuit breaker for ${key} is half-open and its probes are in flight; the call was NOT sent \u2014 retry once a probe settles.`,
16627
- {
16628
- ...ctx,
16629
- details: { state: "half-open", retryAfterMs: 0, failureCount: st.failures.length },
16630
- suggestedAction: "A half-open probe is settling; retry shortly \u2014 the call was not sent and consumed no vendor budget."
16631
- }
16632
- );
16633
- err.notSent = true;
16634
- return { refusal: err };
16635
- }
16636
- const admitted = { ...st, probes: st.probes + 1 };
16637
- breaker.store.set(key, admitted);
16638
- return { probe: true, probeWindow: _nullishCoalesce(admitted.windowOpenedAt, () => ( void 0)) };
16639
- }
16640
- return { probe: false };
16641
- }
16642
- function recordSuccess(breaker, key, probe, probeWindow, logger) {
16643
- const st = breaker.store.get(key);
16644
- if (st === void 0 || st.openedAt === null) return;
16645
- if (probe && probeWindow !== void 0 && st.windowOpenedAt !== probeWindow) return;
16646
- const next = {
16647
- failures: st.failures,
16648
- openedAt: st.openedAt,
16649
- successes: st.successes + 1,
16650
- probes: probe ? Math.max(0, st.probes - 1) : st.probes,
16651
- windowOpenedAt: st.windowOpenedAt
16652
- // a success never moves the window identity
16653
- };
16654
- if (next.successes >= breaker.successesToClose) {
16655
- breaker.store.delete(key);
16656
- _optionalChain([logger, 'access', _67 => _67.warn, 'optionalCall', _68 => _68(
16657
- `[autotask] circuit breaker CLOSED for ${key} (${next.successes} probe successes)`,
16658
- void 0
16659
- )]);
16660
- return;
16661
- }
16662
- breaker.store.set(key, next);
16663
- }
16664
- function recordFailure(breaker, key, probe, probeWindow, logger, correlationId) {
16665
- const now = Date.now();
16666
- const st = _nullishCoalesce(breaker.store.get(key), () => ( { failures: [], openedAt: null, successes: 0, probes: 0, windowOpenedAt: null }));
16667
- if (probe && probeWindow !== void 0 && st.openedAt !== null && st.windowOpenedAt !== probeWindow) return;
16668
- if (st.openedAt === null) {
16669
- const failures = [...st.failures.filter((t) => t > now - breaker.failureWindowMs), now];
16670
- if (failures.length >= breaker.failureThreshold) {
16671
- breaker.store.set(key, { failures, openedAt: now, successes: 0, probes: 0, windowOpenedAt: now });
16672
- _optionalChain([logger, 'access', _69 => _69.warn, 'optionalCall', _70 => _70(
16673
- `[autotask] circuit breaker OPEN for ${key} (${failures.length} counted failures in ${Math.round(breaker.failureWindowMs / 1e3)}s) \u2014 refusing locally for ${Math.round(breaker.openTimeoutMs / 1e3)}s`,
16674
- correlationId
16675
- )]);
16676
- return;
16677
- }
16678
- breaker.store.set(key, { ...st, failures });
16679
- return;
16680
- }
16681
- breaker.store.set(key, {
16682
- failures: st.failures,
16683
- openedAt: now,
16684
- successes: 0,
16685
- probes: probe ? Math.max(0, st.probes - 1) : st.probes,
16686
- windowOpenedAt: st.windowOpenedAt
16687
- });
16688
- }
16689
- function settleProbe(breaker, key, probeWindow) {
16690
- const st = breaker.store.get(key);
16691
- if (st === void 0 || st.openedAt === null || st.probes === 0) return;
16692
- if (probeWindow !== void 0 && st.windowOpenedAt !== probeWindow) return;
16693
- breaker.store.set(key, { ...st, probes: st.probes - 1 });
16694
- }
16695
- var HourlyBudget = (_class7 = class {
16696
- constructor(perHour, profile) {;_class7.prototype.__init12.call(this);
15893
+ // src/http/limits.ts
15894
+ var HourlyBudget = (_class2 = class {
15895
+ constructor(perHour, profile) {;_class2.prototype.__init4.call(this);
16697
15896
  this.perHour = perHour;
16698
15897
  this.profile = profile;
16699
15898
  }
16700
15899
 
16701
15900
 
16702
15901
  /** Request timestamps inside the trailing hour. */
16703
- __init12() {this.stamps = []}
15902
+ __init4() {this.stamps = []}
16704
15903
  prune(now) {
16705
15904
  while (this.stamps.length > 0 && this.stamps[0] <= now - HOUR_MS) this.stamps.shift();
16706
15905
  }
@@ -16729,14 +15928,14 @@ var HourlyBudget = (_class7 = class {
16729
15928
  await sleep(waitMs, signal);
16730
15929
  }
16731
15930
  }
16732
- }, _class7);
16733
- var Semaphore = (_class8 = class {
16734
- constructor(maxConcurrent) {;_class8.prototype.__init13.call(this);_class8.prototype.__init14.call(this);
15931
+ }, _class2);
15932
+ var Semaphore = (_class3 = class {
15933
+ constructor(maxConcurrent) {;_class3.prototype.__init5.call(this);_class3.prototype.__init6.call(this);
16735
15934
  this.maxConcurrent = maxConcurrent;
16736
15935
  }
16737
15936
 
16738
- __init13() {this.inFlight = 0}
16739
- __init14() {this.waitQueue = []}
15937
+ __init5() {this.inFlight = 0}
15938
+ __init6() {this.waitQueue = []}
16740
15939
  acquire() {
16741
15940
  if (this.inFlight < this.maxConcurrent) {
16742
15941
  this.inFlight += 1;
@@ -16754,7 +15953,7 @@ var Semaphore = (_class8 = class {
16754
15953
  const next = this.waitQueue.shift();
16755
15954
  if (next !== void 0) next();
16756
15955
  }
16757
- }, _class8);
15956
+ }, _class3);
16758
15957
 
16759
15958
  // src/http/credentials.ts
16760
15959
  var CREDENTIAL_HEADER_NAMES2 = /* @__PURE__ */ new Set([
@@ -16831,7 +16030,7 @@ async function discoverZoneBase(userName, maxResponseBytes, signal, ctx) {
16831
16030
  return void 0;
16832
16031
  }
16833
16032
  if (res.status < 200 || res.status >= 300) {
16834
- await _optionalChain([res, 'access', _71 => _71.body, 'optionalAccess', _72 => _72.cancel, 'call', _73 => _73(), 'access', _74 => _74.catch, 'call', _75 => _75(() => void 0)]);
16033
+ await _optionalChain([res, 'access', _48 => _48.body, 'optionalAccess', _49 => _49.cancel, 'call', _50 => _50(), 'access', _51 => _51.catch, 'call', _52 => _52(() => void 0)]);
16835
16034
  return void 0;
16836
16035
  }
16837
16036
  let text;
@@ -16878,16 +16077,9 @@ var HttpClient = class {
16878
16077
 
16879
16078
  /** Bounded client-scan caps for resolution (section 19.2; per-client). */
16880
16079
 
16881
- /** Single-record read cache (issue #80: the default instance when `cache` is absent; undefined only when the user set `false` explicitly). */
16882
-
16883
- /** Field-metadata cache for the entityInformation trio (issue #80: the default instance when `fieldCache` is absent; undefined only when the user set `false` explicitly). */
16080
+ /** Record-read cache (absent only for `cache: false`; issue #286). */
16884
16081
 
16885
- /** Resource-name cache for `operations.expandResourceNames` (issue #80: the default instance when `resourceNameCache` is absent; undefined only when the user set `false` explicitly, issue #65). */
16886
-
16887
- /** The `latest` walk's count-probe cache (issue #127: the short-TTL
16888
- * per-client count cache — ALWAYS on, process-internal: no config
16889
- * surface, no served keys; the plain `count` op and `operations.aggregate`
16890
- * bypass it by construction and promise fresh totals). */
16082
+ /** Field-metadata cache for the entityInformation trio (absent only for `cache: false`). */
16891
16083
 
16892
16084
  /** Resolved update fallback gate (issue #34; OFF by default). */
16893
16085
 
@@ -16901,7 +16093,7 @@ var HttpClient = class {
16901
16093
 
16902
16094
  /** FIFO semaphore bounding in-flight requests to `maxConcurrent`. */
16903
16095
 
16904
- /** Tenant 429 cooldown gate store (undefined when the gate is disabled). */
16096
+ /** Tenant 429 cooldown gate (issue #6; ON by default). */
16905
16097
 
16906
16098
 
16907
16099
 
@@ -16921,17 +16113,8 @@ var HttpClient = class {
16921
16113
  * Gates the issue-#43 re-point target: a refused host is a discovery
16922
16114
  * fault (the original 401 propagates byte-identically, zero wire). */
16923
16115
 
16924
- /** The gate key for this client's tenant (lowercased userName + host).
16925
- * Re-derived on a zone re-point (issue #43 — the re-pointed host starts
16926
- * with fresh per-host state). */
16927
-
16928
- /** The host circuit breaker settings (issue #26; undefined when the
16929
- * breaker is off — the default, in which case the gate/recording sites
16930
- * are skipped and no store entry is ever written). */
16116
+ /** The cooldown key (`user@host`) and the maintenance-gate key (host); re-derived on a zone re-point (issue #43). */
16931
16117
 
16932
- /** The breaker key for this client's host (lowercased host of the base
16933
- * URL — host-only: vendor health is host-scoped, issue #26). Re-derived
16934
- * on a zone re-point (issue #43). */
16935
16118
 
16936
16119
 
16937
16120
  /** Response redaction policy (the INV-A1 hotfix; PII/CONFIDENTIAL default on, SECRET always).
@@ -16949,8 +16132,6 @@ var HttpClient = class {
16949
16132
  this.resolution = config.resolution;
16950
16133
  this.readCache = config.readCache;
16951
16134
  this.fieldCache = config.fieldCache;
16952
- this.resourceNameCache = config.resourceNameCache;
16953
- this.latestCountCache = new LatestCountCache(LATEST_COUNT_CACHE_TTL_MS);
16954
16135
  this.update = config.update;
16955
16136
  this.zone = config.zone;
16956
16137
  this.reResolveZoneOn401 = config.reResolveZoneOn401;
@@ -16966,21 +16147,12 @@ var HttpClient = class {
16966
16147
  this.maintenanceMaxWaitMs = config.maintenanceMaxWaitMs;
16967
16148
  this.maintenancePollMs = config.maintenancePollMs;
16968
16149
  this.maxResponseBytes = config.maxResponseBytes;
16969
- this.hourly = _optionalChain([config, 'access', _76 => _76.rateLimit, 'optionalAccess', _77 => _77.perHour]) === void 0 ? void 0 : new HourlyBudget(config.rateLimit.perHour, config.profile);
16970
- this.tenantKey = buildTenantKey(config.userName, config.baseUrl);
16971
- this.cooldownStore = config.cooldown.enabled ? _nullishCoalesce(config.cooldown.store, () => ( defaultCooldownStore())) : void 0;
16150
+ this.hourly = _optionalChain([config, 'access', _53 => _53.rateLimit, 'optionalAccess', _54 => _54.perHour]) === void 0 ? void 0 : new HourlyBudget(config.rateLimit.perHour, config.profile);
16151
+ this.tenantKey = tenantKey(config.userName, config.baseUrl);
16152
+ this.hostKey = hostKey(config.baseUrl);
16153
+ this.cooldownEnabled = config.cooldown.enabled;
16972
16154
  this.cooldownMaxMs = config.cooldown.maxMs;
16973
16155
  this.cooldownDefaultMs = config.cooldown.defaultMs;
16974
- this.breakerKey = buildBreakerKey(config.baseUrl);
16975
- this.breaker = config.circuitBreaker.enabled ? {
16976
- failureWindowMs: config.circuitBreaker.failureWindowMs,
16977
- failureThreshold: config.circuitBreaker.failureThreshold,
16978
- openTimeoutMs: config.circuitBreaker.openTimeoutMs,
16979
- successesToClose: config.circuitBreaker.successesToClose,
16980
- halfOpenProbes: config.circuitBreaker.halfOpenProbes,
16981
- store: _nullishCoalesce(config.circuitBreaker.store, () => ( defaultCircuitBreakerStore())),
16982
- isFailure: _nullishCoalesce(config.circuitBreaker.isFailure, () => ( isBreakerFailure))
16983
- } : void 0;
16984
16156
  this.maxConcurrent = config.maxConcurrent;
16985
16157
  this.semaphore = new Semaphore(config.maxConcurrent);
16986
16158
  this.timeoutMs = config.timeoutMs;
@@ -16996,7 +16168,7 @@ var HttpClient = class {
16996
16168
  * `thresholdInformation.get()`.
16997
16169
  */
16998
16170
  hourlyBudget() {
16999
- return _optionalChain([this, 'access', _78 => _78.hourly, 'optionalAccess', _79 => _79.snapshot, 'call', _80 => _80()]);
16171
+ return _optionalChain([this, 'access', _55 => _55.hourly, 'optionalAccess', _56 => _56.snapshot, 'call', _57 => _57()]);
17000
16172
  }
17001
16173
  /**
17002
16174
  * One audit event per settled request (section 19.3): the correlation id,
@@ -17012,11 +16184,9 @@ var HttpClient = class {
17012
16184
  }
17013
16185
  /**
17014
16186
  * Issue #43: re-point the client to the tenant's newly resolved zone
17015
- * base. The transport's PER-HOST state is RE-DERIVED, so the re-pointed
17016
- * host starts FRESH: no cooldown/breaker/tenant-rate inheritance from
17017
- * the stale host (conservative — the stale host's armed entries stay in
17018
- * the shared stores for the other clients that still talk to it; nothing
17019
- * is copied across). The configured base (config.baseUrl) is untouched;
16187
+ * base. The gate keys are RE-DERIVED, so the re-pointed host starts
16188
+ * fresh: no cooldown/maintenance inheritance from the stale host (its
16189
+ * entries stay for the other clients that still talk to it). The configured base (config.baseUrl) is untouched;
17020
16190
  * the LIVE base is `baseUrl` (the getter over liveBaseUrl), and the
17021
16191
  * re-point is logged (a transport fact — the success path is unchanged,
17022
16192
  * no envelope change).
@@ -17024,58 +16194,18 @@ var HttpClient = class {
17024
16194
  repointZoneBase(to) {
17025
16195
  const from = this.liveBaseUrl;
17026
16196
  this.liveBaseUrl = to;
17027
- this.tenantKey = buildTenantKey(this.auth.userName, to);
17028
- this.breakerKey = buildBreakerKey(to);
17029
- _optionalChain([this, 'access', _81 => _81.logger, 'access', _82 => _82.warn, 'optionalCall', _83 => _83(
16197
+ this.tenantKey = tenantKey(this.auth.userName, to);
16198
+ this.hostKey = hostKey(to);
16199
+ _optionalChain([this, 'access', _58 => _58.logger, 'access', _59 => _59.warn, 'optionalCall', _60 => _60(
17030
16200
  `[autotask] zone re-point ${from} -> ${to} (a 401 on the stale zone base; the vendor's unauthenticated zone discovery named the tenant's current zone) \u2014 retrying the request once against the new base`,
17031
16201
  void 0
17032
16202
  )]);
17033
16203
  }
17034
- /**
17035
- * Breaker success recording (issue #26). A probe admitted on one host must
17036
- * never record onto another: if the call re-pointed (issue #43) the
17037
- * outcome was measured on a different host — void the probe (the admitted
17038
- * budget on the original host dangles for the rest of the open period;
17039
- * bounded, conservative, nothing crosses hosts).
17040
- */
17041
- recordBreakerSuccess(s) {
17042
- const { breakerProbe: probe, breakerProbeKey: probeKey, breakerProbeWindow: probeWindow } = s;
17043
- const breaker = this.breaker;
17044
- if (breaker === void 0) return;
17045
- if (probe && (probeKey === void 0 || probeKey !== this.breakerKey)) return;
17046
- recordSuccess(breaker, probe && probeKey !== void 0 ? probeKey : this.breakerKey, probe, probeWindow, this.logger);
17047
- }
17048
- /** Breaker failure recording (issue #26); same host-pinning as the success recorder (issue #43). */
17049
- recordBreakerFailure(s) {
17050
- const { breakerProbe: probe, breakerProbeKey: probeKey, breakerProbeWindow: probeWindow, correlationId } = s;
17051
- const breaker = this.breaker;
17052
- if (breaker === void 0) return;
17053
- if (probe && (probeKey === void 0 || probeKey !== this.breakerKey)) return;
17054
- recordFailure(breaker, probe && probeKey !== void 0 ? probeKey : this.breakerKey, probe, probeWindow, this.logger, correlationId);
17055
- }
17056
- /**
17057
- * Breaker probe settlement for a NON-COUNTED outcome (issue #262): the
17058
- * failure predicate said the outcome is not a service failure (any 4xx,
17059
- * ABORTED, EMPTY_RESPONSE, INVALID_RESPONSE, REDIRECT_BLOCKED, the
17060
- * deterministic validation 500), so nothing is counted and the open
17061
- * period is NOT re-armed — but the probe SETTLED, so its admitted
17062
- * half-open slot is released (the contract in `circuit-breaker.ts`: every
17063
- * probe completion decrements `probes`). Same host-pinning as the
17064
- * recorders (issue #43): a re-pointed probe is voided, never settled
17065
- * cross-host.
17066
- */
17067
- settleBreakerProbe(s) {
17068
- const { breakerProbe: probe, breakerProbeKey: probeKey, breakerProbeWindow: probeWindow } = s;
17069
- const breaker = this.breaker;
17070
- if (breaker === void 0 || !probe) return;
17071
- if (probeKey === void 0 || probeKey !== this.breakerKey) return;
17072
- settleProbe(breaker, probeKey, probeWindow);
17073
- }
17074
16204
  // ---- per-call helpers ---------------------------------------------------
17075
16205
  /** The caller's signal wins over the deadline when both fired. */
17076
16206
  interrupted(s) {
17077
16207
  const { method, opts, ctx } = s;
17078
- return _optionalChain([s, 'access', _84 => _84.callerSignal, 'optionalAccess', _85 => _85.aborted]) ? new AutotaskAbortError(`Request aborted: ${method} ${opts.path}`, { ...ctx, suggestedAction: "The caller aborted; re-issue the call if still needed." }) : new AutotaskTimeoutError(`Request timed out after ${s.timeoutMs}ms: ${method} ${opts.path}`, ctx);
16208
+ return _optionalChain([s, 'access', _61 => _61.callerSignal, 'optionalAccess', _62 => _62.aborted]) ? new AutotaskAbortError(`Request aborted: ${method} ${opts.path}`, { ...ctx, suggestedAction: "The caller aborted; re-issue the call if still needed." }) : new AutotaskTimeoutError(`Request timed out after ${s.timeoutMs}ms: ${method} ${opts.path}`, ctx);
17079
16209
  }
17080
16210
  /** A retry only runs when its wait fits inside what is left of the call budget. */
17081
16211
  canWait(s, delay) {
@@ -17096,7 +16226,7 @@ var HttpClient = class {
17096
16226
  /** If the retry's wait fits the budget: log, count the attempt, pause, and report true (loop again). */
17097
16227
  async retryAfterDelay(s, delay, what) {
17098
16228
  if (!this.canWait(s, delay)) return false;
17099
- _optionalChain([this, 'access', _86 => _86.logger, 'access', _87 => _87.warn, 'optionalCall', _88 => _88(
16229
+ _optionalChain([this, 'access', _63 => _63.logger, 'access', _64 => _64.warn, 'optionalCall', _65 => _65(
17100
16230
  `[autotask] ${what}; retrying in ${delay}ms (attempt ${s.attempt + 1}/${s.maxRetries})`,
17101
16231
  s.correlationId
17102
16232
  )]);
@@ -17109,10 +16239,18 @@ var HttpClient = class {
17109
16239
  * `maintenanceMaxWaitMs` from the FIRST such response (one budget shared
17110
16240
  * by the 2xx-page and any-status sites), polling `maintenancePollMs`, a
17111
16241
  * fresh per-attempt deadline after each wait; does not consume
17112
- * `maxRetries`. True when a wait happened (loop again); false when the
17113
- * budget is spent (`maintenanceBudgetSpent` is set).
16242
+ * `maxRetries`. Only the call that owns the zone's probe slot waits (one
16243
+ * prober per zone); any other call gets false at once. True when a wait
16244
+ * happened (loop again); false when the budget is spent
16245
+ * (`maintenanceBudgetSpent` is set) or another call owns the slot.
17114
16246
  */
17115
16247
  async waitOutMaintenance(s, statusLabel) {
16248
+ if (s.maintenanceMaxWaitMs <= 0) return false;
16249
+ if (s.maintenanceProbe === void 0) {
16250
+ const gate = claimMaintenanceProbe(this.hostKey);
16251
+ if (gate === void 0) return false;
16252
+ s.maintenanceProbe = { host: this.hostKey, gate };
16253
+ }
17116
16254
  const now = Date.now();
17117
16255
  s.firstMaintenanceAt ??= now;
17118
16256
  const remaining = s.maintenanceMaxWaitMs - (now - s.firstMaintenanceAt);
@@ -17120,7 +16258,7 @@ var HttpClient = class {
17120
16258
  const wait = Math.min(s.maintenancePollMs, remaining);
17121
16259
  if (wait >= remaining) s.maintenanceBudgetSpent = true;
17122
16260
  s.maintenanceWaits += 1;
17123
- _optionalChain([this, 'access', _89 => _89.logger, 'access', _90 => _90.warn, 'optionalCall', _91 => _91(
16261
+ _optionalChain([this, 'access', _66 => _66.logger, 'access', _67 => _67.warn, 'optionalCall', _68 => _68(
17124
16262
  `[autotask] maintenance window on ${s.method} ${s.opts.path}${statusLabel}; waiting ${Math.round(wait / 1e3)}s (retry ${s.maintenanceWaits}, up to ${Math.round(s.maintenanceMaxWaitMs / 6e4)} min)`,
17125
16263
  s.correlationId
17126
16264
  )]);
@@ -17148,6 +16286,41 @@ var HttpClient = class {
17148
16286
  err.suggestedAction = maintenanceExhaustedAction(s.maintenanceMaxWaitMs, s.maintenanceWaits);
17149
16287
  }
17150
16288
  }
16289
+ /**
16290
+ * Issue #285: the call itself received the maintenance page and will not
16291
+ * (or may not) wait it out — the same typed `MAINTENANCE` the gate's
16292
+ * refusals carry, so a consumer maps one code. Unlike a refusal the call
16293
+ * WAS sent (`httpStatus` set, no `notSent`). A mutation answered at a
16294
+ * non-2xx status (review F1) is not retryable: a proxy can serve that page
16295
+ * after forwarding the write, so the outcome is unknown.
16296
+ */
16297
+ maintenancePageError(s, status, contentType, text) {
16298
+ const { method, opts, ctx } = s;
16299
+ const since = new Date(_nullishCoalesce(maintenanceSince(this.hostKey), () => ( Date.now()))).toISOString();
16300
+ const unknownWrite = (status < 200 || status >= 300) && !s.retriable5xx;
16301
+ const err = new MaintenanceError(
16302
+ `Zone ${this.hostKey} answered ${method} ${opts.path} with its maintenance page (HTTP ${status}; window since ${since})`,
16303
+ {
16304
+ ...ctx,
16305
+ httpStatus: status,
16306
+ retryable: !unknownWrite,
16307
+ details: {
16308
+ since,
16309
+ retryAfterMs: s.maintenancePollMs,
16310
+ httpStatus: status,
16311
+ contentType,
16312
+ bodySnippet: bodySnippet(text, this.redactPersonalData),
16313
+ maintenanceDetected: true
16314
+ },
16315
+ suggestedAction: unknownWrite ? `A maintenance-shaped page answered ${method} ${opts.path} (HTTP ${status}); it was NOT waited out or re-sent \u2014 a non-2xx edge page can arrive after the request was forwarded, so verify the record before re-issuing (only a GET or an explicitly idempotent-flagged call waits this window out).` : `The Autotask zone is in its maintenance window (since ${since}); retry after the window (windows usually last 30-90 min).`
16316
+ }
16317
+ );
16318
+ err.retryAfter = Math.max(1, Math.ceil(s.maintenancePollMs / 1e3));
16319
+ const action = err.suggestedAction;
16320
+ this.stampMaintenance(s, err);
16321
+ if (unknownWrite) err.suggestedAction = action;
16322
+ return err;
16323
+ }
17151
16324
  // ---- pre-send gates -----------------------------------------------------
17152
16325
  auditNotSent(s, errorCode) {
17153
16326
  this.emitAudit({
@@ -17162,39 +16335,23 @@ var HttpClient = class {
17162
16335
  });
17163
16336
  }
17164
16337
  /**
17165
- * Tenant 429 cooldown gate (issue #6): a 429 armed the gate for this
17166
- * tenant — fail LOCALLY (before the semaphore, the hourly limiter and
17167
- * fetch: zero upstream calls) until the deadline passes.
16338
+ * The pre-send gates (OUTSIDE the retry loop, before the hourly limiter,
16339
+ * the semaphore and fetch: a refusal makes zero fetches). The tenant 429
16340
+ * cooldown (issue #6; `thresholdInformation.get()` bypasses it), then the
16341
+ * zone maintenance gate (issue #285), which admits one probe per poll
16342
+ * interval while open. An already-aborted caller still gets the abort error.
17168
16343
  */
17169
- gateCooldown(s) {
17170
- if (this.cooldownStore === void 0 || s.opts.bypassCooldown) return;
17171
- const err = cooldownRefusal(this.cooldownStore, this.tenantKey, s.ctx);
17172
- if (err === void 0) return;
17173
- if (_optionalChain([s, 'access', _92 => _92.callerSignal, 'optionalAccess', _93 => _93.aborted])) throw this.interrupted(s);
17174
- this.auditNotSent(s, "RATE_LIMITED_COOLDOWN");
17175
- throw err;
17176
- }
17177
- /**
17178
- * Host circuit breaker gate (issue #26): the SAME pre-send slot as the
17179
- * #6 cooldown gate (cooldown first): OUTSIDE the retry loop, before the
17180
- * hourly limiter, the semaphore and fetch — a refusal makes zero fetches,
17181
- * consumes no hourly stamp and no concurrency slot.
17182
- * `thresholdInformation.get()` bypasses the REFUSAL
17183
- * (`bypassCircuitBreaker`) but its outcome is still recorded.
17184
- */
17185
- gateBreaker(s) {
17186
- if (this.breaker === void 0 || s.opts.bypassCircuitBreaker) return;
17187
- if (_optionalChain([s, 'access', _94 => _94.callerSignal, 'optionalAccess', _95 => _95.aborted])) throw this.interrupted(s);
17188
- const verdict = evaluateBreakerGate(this.breaker, this.breakerKey, s.ctx);
17189
- if (verdict.refusal !== void 0) {
17190
- this.auditNotSent(s, "CIRCUIT_BREAKER_OPEN");
17191
- throw verdict.refusal;
17192
- }
17193
- if (verdict.probe) {
17194
- s.breakerProbe = true;
17195
- s.breakerProbeKey = this.breakerKey;
17196
- s.breakerProbeWindow = verdict.probeWindow;
16344
+ gate(s) {
16345
+ const cooldown = this.cooldownEnabled && !s.opts.bypassCooldown ? cooldownRefusal(this.tenantKey, s.ctx) : void 0;
16346
+ const verdict = _nullishCoalesce(cooldown, () => ( admitMaintenance(this.hostKey, s.ctx)));
16347
+ if (verdict === "send") return;
16348
+ if (!(verdict instanceof AutotaskError)) {
16349
+ s.maintenanceProbe = { host: this.hostKey, gate: verdict };
16350
+ return;
17197
16351
  }
16352
+ if (_optionalChain([s, 'access', _69 => _69.callerSignal, 'optionalAccess', _70 => _70.aborted])) throw this.interrupted(s);
16353
+ this.auditNotSent(s, verdict.code);
16354
+ throw verdict;
17198
16355
  }
17199
16356
  // ---- the attempt --------------------------------------------------------
17200
16357
  /**
@@ -17205,7 +16362,7 @@ var HttpClient = class {
17205
16362
  async sendOnce(s) {
17206
16363
  const { method, opts, ctx } = s;
17207
16364
  try {
17208
- await _optionalChain([this, 'access', _96 => _96.hourly, 'optionalAccess', _97 => _97.take, 'call', _98 => _98(s.deadlineAt, s.signal, ctx)]);
16365
+ await _optionalChain([this, 'access', _71 => _71.hourly, 'optionalAccess', _72 => _72.take, 'call', _73 => _73(s.deadlineAt, s.signal, ctx)]);
17209
16366
  } catch (err) {
17210
16367
  if (s.signal.aborted) throw this.interrupted(s);
17211
16368
  throw err;
@@ -17231,7 +16388,7 @@ var HttpClient = class {
17231
16388
  try {
17232
16389
  status = res.status;
17233
16390
  if (status >= 300 && status < 400) {
17234
- await _optionalChain([res, 'access', _99 => _99.body, 'optionalAccess', _100 => _100.cancel, 'call', _101 => _101(), 'access', _102 => _102.catch, 'call', _103 => _103(() => void 0)]);
16391
+ await _optionalChain([res, 'access', _74 => _74.body, 'optionalAccess', _75 => _75.cancel, 'call', _76 => _76(), 'access', _77 => _77.catch, 'call', _78 => _78(() => void 0)]);
17235
16392
  throw new ResponseRejectedError(
17236
16393
  `Refused to follow a ${status} redirect for ${method} ${opts.path}`,
17237
16394
  { ...ctx, code: "REDIRECT_BLOCKED", httpStatus: status, suggestedAction: "Check the configured zone/baseUrl; the API does not redirect." }
@@ -17254,7 +16411,7 @@ var HttpClient = class {
17254
16411
  /** Classify a 2xx: empty body, JSON (redacted), or a non-JSON page (maintenance wait / retry / typed error). */
17255
16412
  async handleSuccess(s, res, status, text) {
17256
16413
  const { method, opts, ctx } = s;
17257
- if (this.cooldownStore !== void 0) this.cooldownStore.delete(this.tenantKey);
16414
+ if (this.cooldownEnabled) clearCooldown(this.tenantKey);
17258
16415
  if (text.length === 0) {
17259
16416
  if (s.expectBody) {
17260
16417
  const err = new EmptyResponseError(`Empty ${status} response for ${method} ${opts.path}`, {
@@ -17305,19 +16462,19 @@ var HttpClient = class {
17305
16462
  httpStatus: status,
17306
16463
  suggestedAction: "The zone returned a non-JSON 2xx body; verify baseUrl points at the REST API."
17307
16464
  });
17308
- if (err.code === "UNEXPECTED_CONTENT_TYPE" && s.maintenanceMaxWaitMs > 0 && looksLikeMaintenanceText(text)) {
16465
+ if (s.maintenancePage) {
17309
16466
  if (await this.waitOutMaintenance(s, "")) return RETRY;
17310
- }
17311
- if (err.code === "UNEXPECTED_CONTENT_TYPE" && retriable5xx && s.attempt < s.maxRetries) {
16467
+ throw this.maintenancePageError(s, status, res.headers.get("Content-Type"), text);
16468
+ } else if (err.code === "UNEXPECTED_CONTENT_TYPE" && retriable5xx && s.attempt < s.maxRetries) {
17312
16469
  const retryAfter = parseRetryAfter(res.headers.get("Retry-After"));
17313
16470
  const delay = retryAfter !== void 0 ? retryAfter * 1e3 : backoffDelay(s.attempt);
17314
16471
  if (await this.retryAfterDelay(s, delay, `non-JSON 2xx on ${method} ${opts.path}`)) return RETRY;
17315
16472
  }
17316
- const pageSnippet = typeof _optionalChain([err, 'access', _104 => _104.details, 'optionalAccess', _105 => _105.bodySnippet]) === "string" ? err.details.bodySnippet : "";
16473
+ const pageSnippet = typeof _optionalChain([err, 'access', _79 => _79.details, 'optionalAccess', _80 => _80.bodySnippet]) === "string" ? err.details.bodySnippet : "";
17317
16474
  this.stampMaintenance(s, err);
17318
- if (err.code === "UNEXPECTED_CONTENT_TYPE" && !looksLikeMaintenanceText(text) && isHtmlLike(res.headers.get("Content-Type"), text)) {
16475
+ if (err.code === "UNEXPECTED_CONTENT_TYPE" && !s.maintenancePage && isHtmlLike(res.headers.get("Content-Type"), text)) {
17319
16476
  err.details = { ..._nullishCoalesce(err.details, () => ( {})), maintenancePatternMiss: true };
17320
- _optionalChain([this, 'access', _106 => _106.logger, 'access', _107 => _107.debug, 'optionalCall', _108 => _108(
16477
+ _optionalChain([this, 'access', _81 => _81.logger, 'access', _82 => _82.debug, 'optionalCall', _83 => _83(
17321
16478
  `[autotask] non-JSON body did not match the maintenance vocabulary on ${method} ${opts.path}: ${pageSnippet}`,
17322
16479
  s.correlationId
17323
16480
  )]);
@@ -17343,15 +16500,15 @@ var HttpClient = class {
17343
16500
  }
17344
16501
  }
17345
16502
  const nonJsonSnippet = nonJsonBody === void 0 ? void 0 : bodySnippet(nonJsonBody, this.redactPersonalData);
17346
- const nonJsonIsMaintenance = nonJsonBody !== void 0 && looksLikeMaintenanceText(nonJsonBody);
16503
+ const nonJsonIsMaintenance = s.maintenancePage;
17347
16504
  const nonJsonIsHtmlCandidate = nonJsonBody !== void 0 && isHtmlLike(res.headers.get("Content-Type"), nonJsonBody);
17348
- const nonJsonMaintenanceMayWait = nonJsonIsMaintenance && s.retriable5xx;
17349
- if (nonJsonBody !== void 0 && s.maintenanceMaxWaitMs > 0 && nonJsonMaintenanceMayWait) {
17350
- if (await this.waitOutMaintenance(s, ` (HTTP ${status})`)) return RETRY;
16505
+ if (nonJsonIsMaintenance) {
16506
+ if (s.retriable5xx && await this.waitOutMaintenance(s, ` (HTTP ${status})`)) return RETRY;
16507
+ throw this.maintenancePageError(s, status, res.headers.get("Content-Type"), text);
17351
16508
  }
17352
16509
  const deterministic500 = status >= 500 && isDeterministicVendor500(parseVendorErrors(body));
17353
16510
  const retryableStatus = status === 429 || status >= 500;
17354
- if (retryableStatus && !deterministic500 && s.attempt < s.maxRetries && (status === 429 ? s.retriable : s.retriable5xx)) {
16511
+ if (retryableStatus && !deterministic500 && !nonJsonIsMaintenance && s.attempt < s.maxRetries && (status === 429 ? s.retriable : s.retriable5xx)) {
17355
16512
  const retryAfter = parseRetryAfter(res.headers.get("Retry-After"));
17356
16513
  const delay = retryAfter !== void 0 ? retryAfter * 1e3 : backoffDelay(s.attempt);
17357
16514
  if (await this.retryAfterDelay(s, delay, `${status} on ${method} ${opts.path}`)) return RETRY;
@@ -17360,7 +16517,7 @@ var HttpClient = class {
17360
16517
  const target = await discoverZoneBase(this.userName, this.maxResponseBytes, s.signal, s.ctx);
17361
16518
  const targetViolation = target === void 0 || this.hostPolicy === void 0 ? null : decideHostPolicy(target, this.hostPolicy);
17362
16519
  if (targetViolation !== null) {
17363
- _optionalChain([this, 'access', _109 => _109.logger, 'access', _110 => _110.warn, 'optionalCall', _111 => _111(
16520
+ _optionalChain([this, 'access', _84 => _84.logger, 'access', _85 => _85.warn, 'optionalCall', _86 => _86(
17364
16521
  `[autotask] zone re-point refused by hostPolicy (${targetViolation.bound}): ${targetViolation.origin} \u2014 the original 401 propagates, zero wire to the refused host`,
17365
16522
  void 0
17366
16523
  )]);
@@ -17384,12 +16541,9 @@ var HttpClient = class {
17384
16541
  err.correlationId = s.correlationId;
17385
16542
  this.stampMaintenance(s, err);
17386
16543
  if (nonJsonBody !== void 0) {
17387
- if (nonJsonIsMaintenance && !s.retriable5xx) {
17388
- err.details = { ..._nullishCoalesce(err.details, () => ( {})), maintenanceDetected: true };
17389
- err.suggestedAction = `A maintenance-shaped page answered ${method} ${opts.path} (HTTP ${status}); it was NOT waited out or re-sent \u2014 a non-2xx edge page can arrive after the request was forwarded, so verify the record before re-issuing (only a GET or an explicitly idempotent-flagged call waits this window out).`;
17390
- } else if (!nonJsonIsMaintenance && nonJsonIsHtmlCandidate) {
16544
+ if (nonJsonIsHtmlCandidate) {
17391
16545
  err.details = { ..._nullishCoalesce(err.details, () => ( {})), maintenancePatternMiss: true };
17392
- _optionalChain([this, 'access', _112 => _112.logger, 'access', _113 => _113.debug, 'optionalCall', _114 => _114(
16546
+ _optionalChain([this, 'access', _87 => _87.logger, 'access', _88 => _88.debug, 'optionalCall', _89 => _89(
17393
16547
  `[autotask] non-JSON body did not match the maintenance vocabulary on ${method} ${opts.path}: ${nonJsonSnippet}`,
17394
16548
  s.correlationId
17395
16549
  )]);
@@ -17403,15 +16557,15 @@ var HttpClient = class {
17403
16557
  }
17404
16558
  attachEmptyReasonDetails(err);
17405
16559
  if (err instanceof RateLimitError && retryAfterSecs !== void 0) err.retryAfter = retryAfterSecs;
17406
- if (status === 429 && this.cooldownStore !== void 0) {
16560
+ if (status === 429 && this.cooldownEnabled) {
17407
16561
  const threadThreshold = err.code.toLowerCase() === "itgenatr005";
17408
16562
  const durationMs = cooldownDurationMs(retryAfterSecs, {
17409
16563
  maxMs: this.cooldownMaxMs,
17410
16564
  defaultMs: this.cooldownDefaultMs,
17411
16565
  threadThreshold
17412
16566
  });
17413
- this.cooldownStore.set(this.tenantKey, Date.now() + durationMs);
17414
- _optionalChain([this, 'access', _115 => _115.logger, 'access', _116 => _116.warn, 'optionalCall', _117 => _117(
16567
+ armCooldown(this.tenantKey, Date.now() + durationMs);
16568
+ _optionalChain([this, 'access', _90 => _90.logger, 'access', _91 => _91.warn, 'optionalCall', _92 => _92(
17415
16569
  `[autotask] 429 on ${method} ${opts.path} surfaces after ${s.attempt + 1} attempt(s); tenant cooldown armed for ${Math.round(durationMs / 1e3)}s`,
17416
16570
  s.correlationId
17417
16571
  )]);
@@ -17429,25 +16583,9 @@ var HttpClient = class {
17429
16583
  durationMs: Date.now() - s.started,
17430
16584
  body: s.auditBody
17431
16585
  });
17432
- if (this.breaker !== void 0) {
17433
- try {
17434
- this.recordBreakerSuccess(s);
17435
- } catch (e14) {
17436
- }
17437
- }
17438
16586
  }
17439
16587
  onFailure(s, err) {
17440
16588
  const { opts } = s;
17441
- if (this.breaker !== void 0 && err instanceof AutotaskError && err.notSent !== true) {
17442
- try {
17443
- if (this.breaker.isFailure(err)) {
17444
- this.recordBreakerFailure(s);
17445
- } else if (s.breakerProbe) {
17446
- this.settleBreakerProbe(s);
17447
- }
17448
- } catch (e15) {
17449
- }
17450
- }
17451
16589
  this.emitAudit({
17452
16590
  correlationId: s.correlationId,
17453
16591
  operation: opts.operation,
@@ -17461,23 +16599,17 @@ var HttpClient = class {
17461
16599
  if (this.fieldCache !== void 0 && err instanceof AutotaskError) {
17462
16600
  const op = typeof opts.operation === "string" ? opts.operation : void 0;
17463
16601
  const verb = op !== void 0 ? op.slice(op.lastIndexOf(".") + 1) : "";
17464
- const drift = _optionalChain([err, 'access', _118 => _118.details, 'optionalAccess', _119 => _119.deterministic]) === true || err.code === "INVALID_RESPONSE" && META_TRIO_VERBS.has(verb);
16602
+ const drift = _optionalChain([err, 'access', _93 => _93.details, 'optionalAccess', _94 => _94.deterministic]) === true || err.code === "INVALID_RESPONSE" && META_TRIO_VERBS.has(verb);
17465
16603
  if (drift && op !== void 0) {
17466
16604
  const dot = op.indexOf(".");
17467
- if (dot > 0) this.fieldCache.invalidate(op.slice(0, dot));
16605
+ if (dot > 0) this.fieldCache.invalidate(`${op.slice(0, dot)}:`);
17468
16606
  }
17469
16607
  }
17470
16608
  }
17471
16609
  /** A real write (even a failed one — it may have partly applied) voids cached reads. */
17472
16610
  invalidateAfterWrite(opts) {
17473
16611
  if (opts.method === "GET" || opts.idempotent === true && /\/query(\/|\?|$)/.test(opts.path)) return;
17474
- _optionalChain([this, 'access', _120 => _120.readCache, 'optionalAccess', _121 => _121.clear, 'call', _122 => _122()]);
17475
- this.latestCountCache.clear();
17476
- const nameOp = typeof opts.operation === "string" ? opts.operation : void 0;
17477
- const nameDot = nameOp !== void 0 ? nameOp.indexOf(".") : -1;
17478
- if (this.resourceNameCache !== void 0 && nameOp !== void 0 && nameDot > 0 && nameOp.slice(0, nameDot) === "resources") {
17479
- this.resourceNameCache.clear();
17480
- }
16612
+ _optionalChain([this, 'access', _95 => _95.readCache, 'optionalAccess', _96 => _96.clear, 'call', _97 => _97()]);
17481
16613
  }
17482
16614
  /**
17483
16615
  * One request: build URL/headers, apply the whole-call deadline, retry
@@ -17544,22 +16676,23 @@ var HttpClient = class {
17544
16676
  lastStatus: void 0,
17545
16677
  zoneRetry: void 0,
17546
16678
  attempt: 0,
17547
- breakerProbe: false,
17548
- breakerProbeKey: void 0,
17549
- breakerProbeWindow: void 0,
16679
+ maintenanceProbe: void 0,
16680
+ maintenancePage: false,
17550
16681
  // Armed LAST in the literal: nothing after this can throw, and the single
17551
16682
  // try/finally below clears it on every exit path (#190).
17552
16683
  deadlineTimer: setTimeout(() => deadline.abort(), timeoutMs)
17553
16684
  };
17554
16685
  try {
17555
- this.gateCooldown(s);
17556
- this.gateBreaker(s);
17557
- _optionalChain([this, 'access', _123 => _123.logger, 'access', _124 => _124.debug, 'optionalCall', _125 => _125(`[autotask] ${method} ${opts.path}`, correlationId)]);
16686
+ this.gate(s);
16687
+ _optionalChain([this, 'access', _98 => _98.logger, 'access', _99 => _99.debug, 'optionalCall', _100 => _100(`[autotask] ${method} ${opts.path}`, correlationId)]);
17558
16688
  try {
17559
16689
  for (; ; ) {
17560
16690
  const sent = await this.sendOnce(s);
17561
16691
  if (sent.retry) continue;
17562
16692
  const { res, status, text } = sent;
16693
+ s.maintenancePage = isMaintenancePage(status, res.headers.get("Content-Type"), text);
16694
+ if (s.maintenancePage) openMaintenance(this.hostKey, s.maintenancePollMs, this.logger, correlationId);
16695
+ else closeMaintenance(this.hostKey, this.logger, correlationId);
17563
16696
  const step = status >= 200 && status < 300 ? await this.handleSuccess(s, res, status, text) : await this.handleFailureStatus(s, res, status, text);
17564
16697
  if (step.done) {
17565
16698
  this.onSuccess(s, step.status);
@@ -17574,19 +16707,30 @@ var HttpClient = class {
17574
16707
  }
17575
16708
  } finally {
17576
16709
  clearTimeout(s.deadlineTimer);
16710
+ if (s.maintenanceProbe !== void 0) releaseMaintenanceProbe(s.maintenanceProbe.host, s.maintenanceProbe.gate);
17577
16711
  }
17578
16712
  }
17579
16713
  };
16714
+ function isMaintenancePage(status, contentType, text) {
16715
+ if (text.length === 0 || !looksLikeMaintenanceText(text)) return false;
16716
+ if (status >= 200 && status < 300 && (contentType === null || contentType === "" || isJsonMediaType(contentType))) return false;
16717
+ try {
16718
+ JSON.parse(text);
16719
+ return false;
16720
+ } catch (e14) {
16721
+ return true;
16722
+ }
16723
+ }
17580
16724
  function assertNoQueryRelationLeaf(opts) {
17581
16725
  const operation = _nullishCoalesce(opts.operation, () => ( opts.path));
17582
16726
  const body = opts.body;
17583
16727
  if (body !== null && typeof body === "object") assertNoRelationLeaf(body.filter, operation);
17584
- const search = _optionalChain([opts, 'access', _126 => _126.query, 'optionalAccess', _127 => _127["search"]]);
16728
+ const search = _optionalChain([opts, 'access', _101 => _101.query, 'optionalAccess', _102 => _102["search"]]);
17585
16729
  if (typeof search !== "string") return;
17586
16730
  let parsed;
17587
16731
  try {
17588
16732
  parsed = JSON.parse(search);
17589
- } catch (e16) {
16733
+ } catch (e15) {
17590
16734
  return;
17591
16735
  }
17592
16736
  if (parsed !== null && typeof parsed === "object") assertNoRelationLeaf(parsed.filter, operation);
@@ -17594,23 +16738,23 @@ function assertNoQueryRelationLeaf(opts) {
17594
16738
 
17595
16739
  // src/operations/requiredFields.ts
17596
16740
  function effectiveRequiredFields(record, zoneFacts) {
17597
- const guidance = _optionalChain([zoneFacts, 'optionalAccess', _128 => _128.createGuidance]);
17598
- const prefix = _optionalChain([guidance, 'optionalAccess', _129 => _129.liveProvenPrefix]);
16741
+ const guidance = _optionalChain([zoneFacts, 'optionalAccess', _103 => _103.createGuidance]);
16742
+ const prefix = _optionalChain([guidance, 'optionalAccess', _104 => _104.liveProvenPrefix]);
17599
16743
  if (Array.isArray(prefix) && prefix.length > 0) {
17600
- const gates = _optionalChain([guidance, 'optionalAccess', _130 => _130.requiresByValue]);
16744
+ const gates = _optionalChain([guidance, 'optionalAccess', _105 => _105.requiresByValue]);
17601
16745
  const gated = new Set((_nullishCoalesce(gates, () => ( []))).flatMap((g) => [..._nullishCoalesce(g.requires.allOf, () => ( [])), ..._nullishCoalesce(g.requires.anyOf, () => ( []))]));
17602
16746
  return {
17603
16747
  fields: prefix.filter((f) => !gated.has(f)),
17604
16748
  from: "live-proven",
17605
- unproven: _optionalChain([guidance, 'optionalAccess', _131 => _131.unproven]) === true,
16749
+ unproven: _optionalChain([guidance, 'optionalAccess', _106 => _106.unproven]) === true,
17606
16750
  ...Array.isArray(gates) && gates.length > 0 ? { conditional: gates } : {}
17607
16751
  };
17608
16752
  }
17609
- const zoneSet = _optionalChain([zoneFacts, 'optionalAccess', _132 => _132.createRequiredSet]);
16753
+ const zoneSet = _optionalChain([zoneFacts, 'optionalAccess', _107 => _107.createRequiredSet]);
17610
16754
  if (Array.isArray(zoneSet) && zoneSet.length > 0) {
17611
- return { fields: [...zoneSet], from: "zone", unproven: _optionalChain([guidance, 'optionalAccess', _133 => _133.unproven]) === true };
16755
+ return { fields: [...zoneSet], from: "zone", unproven: _optionalChain([guidance, 'optionalAccess', _108 => _108.unproven]) === true };
17612
16756
  }
17613
- const spec = _optionalChain([record, 'optionalAccess', _134 => _134.requiredFields]);
16757
+ const spec = _optionalChain([record, 'optionalAccess', _109 => _109.requiredFields]);
17614
16758
  return { fields: Array.isArray(spec) ? [...spec] : [], from: "spec", unproven: true };
17615
16759
  }
17616
16760
 
@@ -17707,12 +16851,165 @@ function resolveFieldsPreset(name, resourceKey, explicitIncludeFields) {
17707
16851
  `fieldsPreset: unknown preset "${name}" \u2014 the preset list is closed (valid names: ${FIELD_PRESET_NAMES.join(", ")})`
17708
16852
  );
17709
16853
  }
17710
- if (preset.resource !== resourceKey) {
17711
- throw new AutotaskConfigError(
17712
- `fieldsPreset: "${name}" is a ${preset.resource} preset \u2014 it cannot be resolved on ${resourceKey} (the preset names its resource; the valid names are: ${FIELD_PRESET_NAMES.join(", ")})`
17713
- );
16854
+ if (preset.resource !== resourceKey) {
16855
+ throw new AutotaskConfigError(
16856
+ `fieldsPreset: "${name}" is a ${preset.resource} preset \u2014 it cannot be resolved on ${resourceKey} (the preset names its resource; the valid names are: ${FIELD_PRESET_NAMES.join(", ")})`
16857
+ );
16858
+ }
16859
+ return [...preset.fields];
16860
+ }
16861
+
16862
+ // src/resources/base/helpers.ts
16863
+ function describeResponseShape(body) {
16864
+ if (body === null) return "null";
16865
+ if (Array.isArray(body)) return `array(len=${body.length})`;
16866
+ if (typeof body !== "object") return typeof body;
16867
+ const rec = body;
16868
+ const keys = Object.keys(rec).sort();
16869
+ const typeOf = (v) => v === null ? "null" : Array.isArray(v) ? "array" : typeof v;
16870
+ const parts = keys.slice(0, 16).map((k) => `${k}:${typeOf(rec[k])}`);
16871
+ const more = keys.length > 16 ? `, +${keys.length - 16} more` : "";
16872
+ return `{ ${parts.join(", ")}${more} }`;
16873
+ }
16874
+ function defaultMarkerToken(data) {
16875
+ for (const field of ["name", "title"]) {
16876
+ const v = data[field];
16877
+ if (typeof v === "string" && v.trim() !== "") return v.trim();
16878
+ }
16879
+ return null;
16880
+ }
16881
+ var CONTEXT_ABSENT_MARKER = "#context-absent";
16882
+ function contextAbsentKey(path) {
16883
+ return path + "\0context-absent";
16884
+ }
16885
+ function noteCharsTruncationMarker(charsRemoved) {
16886
+ return `\u2026 [+${charsRemoved} chars truncated]`;
16887
+ }
16888
+ function truncateNoteChars(items, cap, fields) {
16889
+ let truncated = 0;
16890
+ const out = items.map((item) => {
16891
+ if (!isRecord2(item)) return item;
16892
+ let changed = false;
16893
+ const copy = { ...item };
16894
+ for (const field of fields) {
16895
+ const v = copy[field];
16896
+ if (typeof v === "string" && v.length > cap) {
16897
+ const end = cap > 0 && (v.charCodeAt(cap - 1) & 64512) === 55296 ? cap - 1 : cap;
16898
+ copy[field] = v.slice(0, end) + noteCharsTruncationMarker(v.length - end);
16899
+ truncated += 1;
16900
+ changed = true;
16901
+ }
16902
+ }
16903
+ return changed ? copy : item;
16904
+ });
16905
+ return [out, truncated];
16906
+ }
16907
+ function blindEmptyReason(stamp) {
16908
+ return {
16909
+ surface: stamp.surface,
16910
+ kind: "blind-surface",
16911
+ note: stamp.note,
16912
+ authority: stamp.authority,
16913
+ remedy: "the note is the remedy \u2014 the authority op answers for a record"
16914
+ };
16915
+ }
16916
+ function attachUpdateTargetMissReason(err, key) {
16917
+ const pinned = _optionalChain([EMPTY_REASON_STAMPS, 'access', _110 => _110[key], 'optionalAccess', _111 => _111.update]);
16918
+ if (pinned === void 0) return;
16919
+ err.details = { ..._nullishCoalesce(err.details, () => ( {})), emptyReason: pinned };
16920
+ }
16921
+ function buildDryRunResult(spec, policy = { redactPersonalData: true }) {
16922
+ if (spec.operation === "") {
16923
+ throw new AutotaskConfigError("dry-run: the operation name is required");
16924
+ }
16925
+ const wouldApply = spec.checks.every((check) => check.ok);
16926
+ const resource = _nullishCoalesce(spec.operation.split(".")[0], () => ( spec.operation));
16927
+ const diffPart = spec.diff !== void 0 ? {
16928
+ diff: spec.diff.map((entry) => {
16929
+ if (entry.to === void 0) return entry;
16930
+ const to = isRedactedFieldName(entry.field, policy) ? REDACTED : redactResponseBody(entry.to, policy);
16931
+ return { ...entry, to };
16932
+ })
16933
+ } : {};
16934
+ const dataPart = spec.data !== void 0 ? { data: redactResponseBody(spec.data, policy) } : {};
16935
+ return {
16936
+ operation: spec.operation,
16937
+ wouldApply,
16938
+ target: { resource, ids: spec.ids },
16939
+ request: { method: spec.method, path: spec.path },
16940
+ ...diffPart,
16941
+ checks: spec.checks,
16942
+ impact: spec.impact,
16943
+ simulated: true,
16944
+ warnings: _nullishCoalesce(spec.warnings, () => ( [])),
16945
+ ...dataPart,
16946
+ ...spec.emptyReason !== void 0 ? { emptyReason: spec.emptyReason } : {}
16947
+ };
16948
+ }
16949
+ function computeFieldDiff(current, data) {
16950
+ const out = [];
16951
+ for (const [field, to] of Object.entries(data)) {
16952
+ if (field === "id" || to === void 0) continue;
16953
+ const from = current[field];
16954
+ if (from !== void 0 && JSON.stringify(from) === JSON.stringify(to)) continue;
16955
+ out.push({ field, from: from === void 0 ? null : from, to });
16956
+ }
16957
+ return out;
16958
+ }
16959
+ function mergePartialBody(current, data) {
16960
+ const merged = { ...current };
16961
+ for (const [key, value] of Object.entries(data)) {
16962
+ if (value === void 0) continue;
16963
+ merged[key] = value;
16964
+ }
16965
+ return merged;
16966
+ }
16967
+ function normalizeUdfInput(value) {
16968
+ if (Array.isArray(value)) {
16969
+ return value.map((entry) => {
16970
+ if (!isRecord2(entry) || typeof entry.name !== "string" || entry.name === "") {
16971
+ throw new AutotaskConfigError("userDefinedFields array entries must be { name, value } objects");
16972
+ }
16973
+ return { name: entry.name, value: entry.value };
16974
+ });
16975
+ }
16976
+ if (isRecord2(value)) {
16977
+ return Object.entries(value).map(([name, entryValue]) => ({ name, value: entryValue }));
16978
+ }
16979
+ throw new AutotaskConfigError("userDefinedFields must be an array of { name, value } entries or a name-to-value map");
16980
+ }
16981
+ function blindStampFor(stamp, filter) {
16982
+ if (stamp === void 0) return void 0;
16983
+ if (stamp.kind !== "unfiltered-blind") return stamp;
16984
+ const conditions = normalizedConditions(filter);
16985
+ return conditions === void 0 || conditions.length === 0 ? stamp : void 0;
16986
+ }
16987
+ function decodeFileData(body) {
16988
+ if (isRecord2(body)) {
16989
+ const data = body.data;
16990
+ if (typeof data === "string") return Buffer.from(data, "base64");
16991
+ if (Array.isArray(data) && data.every((b) => typeof b === "number")) {
16992
+ return Buffer.from(data);
16993
+ }
16994
+ }
16995
+ if (typeof body === "string") return Buffer.from(body, "base64");
16996
+ throw new AutotaskError(
16997
+ "Expected the file envelope FileQueryResultModel { data } (or a base64 string); got a different shape",
16998
+ { code: "INVALID_RESPONSE", category: "server" }
16999
+ );
17000
+ }
17001
+ var DEPLURAL_EXCEPTIONS = {
17002
+ tag_aliase: "tag_alias"
17003
+ };
17004
+ function entityNameFromPath(resourcePath) {
17005
+ const segment = resourcePath.slice(resourcePath.lastIndexOf("/") + 1);
17006
+ const snake = segment.replace(/([A-Z])/g, (_m, c, i) => (i > 0 ? `_` : "") + c.toLowerCase()).replace(/_{2,}/g, "_");
17007
+ if (snake.endsWith("ies")) return `${snake.slice(0, -3)}y`;
17008
+ if (snake.endsWith("s")) {
17009
+ const stripped = snake.slice(0, -1);
17010
+ return _nullishCoalesce(DEPLURAL_EXCEPTIONS[stripped], () => ( stripped));
17714
17011
  }
17715
- return [...preset.fields];
17012
+ return snake;
17716
17013
  }
17717
17014
 
17718
17015
  // src/operations/expandResourceNames.ts
@@ -17749,7 +17046,7 @@ async function expandResourceNames(client, ids2, opts = {}) {
17749
17046
  const raw = ids2[i];
17750
17047
  try {
17751
17048
  canonical[i] = parseIdentifier(raw, at);
17752
- } catch (e17) {
17049
+ } catch (e16) {
17753
17050
  if (typeof raw === "string") {
17754
17051
  problems.push(`${at}: "${raw}" is not a vendor numeric resource id \u2014 non-numeric string identifiers are operations.resolveAny territory (the resource's findBy* helpers)`);
17755
17052
  } else if (typeof raw === "number") {
@@ -17775,54 +17072,35 @@ async function expandResourceNames(client, ids2, opts = {}) {
17775
17072
  }
17776
17073
  }
17777
17074
  const holder = client.resources;
17778
- const cache = client.http.resourceNameCache;
17075
+ const cache = client.http.readCache;
17779
17076
  const fresh = opts.fresh === true;
17780
17077
  const byId = /* @__PURE__ */ new Map();
17781
17078
  for (const id of unique) {
17782
17079
  const k = String(id);
17783
- if (cache !== void 0 && !fresh) {
17784
- const shared = cache.inflight(id);
17785
- if (shared !== void 0) {
17786
- let joined;
17787
- try {
17788
- joined = await shared;
17789
- } catch (err) {
17790
- if (!isAutotaskError(err) || err.code !== "ABORTED") throw err;
17791
- }
17792
- if (joined !== void 0) {
17793
- byId.set(k, joined);
17794
- continue;
17795
- }
17080
+ const absentKey = contextAbsentKey(`/V1.0/Resources/${k}`);
17081
+ if (cache !== void 0 && !fresh && cache.get(absentKey) !== void 0) {
17082
+ byId.set(k, { name: null, blind: true });
17083
+ continue;
17084
+ }
17085
+ const epoch = _nullishCoalesce(_optionalChain([cache, 'optionalAccess', _112 => _112.epoch]), () => ( 0));
17086
+ try {
17087
+ const record = await holder.get(id, { fresh });
17088
+ _optionalChain([cache, 'optionalAccess', _113 => _113.delete, 'call', _114 => _114(absentKey)]);
17089
+ byId.set(k, { name: composeName(record), blind: false });
17090
+ } catch (err) {
17091
+ if (err instanceof NotFoundError) {
17092
+ _optionalChain([cache, 'optionalAccess', _115 => _115.set, 'call', _116 => _116(absentKey, CONTEXT_ABSENT_MARKER, epoch)]);
17093
+ byId.set(k, { name: null, blind: true });
17094
+ continue;
17796
17095
  }
17797
- const hit = cache.get(id);
17798
- if (hit !== void 0) {
17799
- byId.set(k, { name: hit.name, blind: hit.blind });
17096
+ if (isAutotaskError(err) && err.code !== "ABORTED") {
17097
+ const e = { code: err.code, message: err.message };
17098
+ if (err.httpStatus !== void 0) e.httpStatus = err.httpStatus;
17099
+ byId.set(k, { name: null, blind: false, error: e });
17800
17100
  continue;
17801
17101
  }
17102
+ throw err;
17802
17103
  }
17803
- const epoch = _nullishCoalesce(_optionalChain([cache, 'optionalAccess', _135 => _135.epoch]), () => ( 0));
17804
- const work = (async () => {
17805
- try {
17806
- const record = await holder.get(id, { fresh });
17807
- const name = composeName(record);
17808
- if (cache !== void 0 && name !== null) cache.set(id, { name, blind: false }, epoch);
17809
- return { name, blind: false };
17810
- } catch (err) {
17811
- if (err instanceof NotFoundError) {
17812
- const entry = { name: null, blind: true };
17813
- if (cache !== void 0) cache.set(id, entry, epoch);
17814
- return { name: null, blind: true };
17815
- }
17816
- if (isAutotaskError(err) && err.code !== "ABORTED") {
17817
- const e = { code: err.code, message: err.message };
17818
- if (err.httpStatus !== void 0) e.httpStatus = err.httpStatus;
17819
- return { name: null, blind: false, error: e };
17820
- }
17821
- throw err;
17822
- }
17823
- })();
17824
- const settled = cache !== void 0 ? await cache.track(id, work) : await work;
17825
- byId.set(k, settled);
17826
17104
  }
17827
17105
  const results = [];
17828
17106
  for (const id of canonical) {
@@ -17854,7 +17132,7 @@ function buildEntityTypeMap() {
17854
17132
  }
17855
17133
  var ENTITY_TYPE_TO_RESOURCE = buildEntityTypeMap();
17856
17134
  var ENUMERABLE_RESOURCES = new Set(
17857
- _chunkVR6RYXTDcjs.CAPABILITIES.filter((rec) => rec.resource !== "operations" && (rec.operation === "query" || rec.operation === "get")).map(
17135
+ _chunkEVDVESQMcjs.CAPABILITIES.filter((rec) => rec.resource !== "operations" && (rec.operation === "query" || rec.operation === "get")).map(
17858
17136
  (rec) => rec.resource
17859
17137
  )
17860
17138
  );
@@ -17942,7 +17220,7 @@ function capabilitySets() {
17942
17220
  const fieldOps = /* @__PURE__ */ new Map();
17943
17221
  const udfCapable = /* @__PURE__ */ new Set();
17944
17222
  const resources = /* @__PURE__ */ new Set();
17945
- for (const rec of _chunkVR6RYXTDcjs.CAPABILITIES) {
17223
+ for (const rec of _chunkEVDVESQMcjs.CAPABILITIES) {
17946
17224
  if (rec.resource === "operations") continue;
17947
17225
  resources.add(rec.resource);
17948
17226
  if (rec.operation === "getFieldInfo") fieldOps.set(rec.resource, rec);
@@ -17954,12 +17232,12 @@ var derivedIndex = null;
17954
17232
  function derivedFieldsFor(resource) {
17955
17233
  if (derivedIndex === null) {
17956
17234
  derivedIndex = /* @__PURE__ */ new Map();
17957
- for (const rec of _chunkVR6RYXTDcjs.CAPABILITIES) {
17958
- const declared = _optionalChain([rec, 'access', _136 => _136.metadata, 'optionalAccess', _137 => _137.derivedFields]);
17235
+ for (const rec of _chunkEVDVESQMcjs.CAPABILITIES) {
17236
+ const declared = _optionalChain([rec, 'access', _117 => _117.metadata, 'optionalAccess', _118 => _118.derivedFields]);
17959
17237
  if (!Array.isArray(declared) || derivedIndex.has(rec.resource)) continue;
17960
17238
  const rows = [];
17961
17239
  for (const row of declared) {
17962
- if (typeof _optionalChain([row, 'optionalAccess', _138 => _138.field]) !== "string" || typeof row.rule !== "string") continue;
17240
+ if (typeof _optionalChain([row, 'optionalAccess', _119 => _119.field]) !== "string" || typeof row.rule !== "string") continue;
17963
17241
  rows.push({
17964
17242
  field: row.field,
17965
17243
  rule: row.rule,
@@ -17997,7 +17275,7 @@ function validateCatalogOptsShape(opts) {
17997
17275
  }
17998
17276
  }
17999
17277
  function resolveInclude(opts) {
18000
- const includeRaw = _nullishCoalesce(_optionalChain([opts, 'optionalAccess', _139 => _139.include]), () => ( ["fields"]));
17278
+ const includeRaw = _nullishCoalesce(_optionalChain([opts, 'optionalAccess', _120 => _120.include]), () => ( ["fields"]));
18001
17279
  if (!Array.isArray(includeRaw) || includeRaw.length === 0) {
18002
17280
  throw new AutotaskConfigError("fieldCatalog: opts.include must be a non-empty array of (info|fields|udf)");
18003
17281
  }
@@ -18014,7 +17292,7 @@ function resolveInclude(opts) {
18014
17292
  return include;
18015
17293
  }
18016
17294
  function resolveTargets(opts, fieldOps, allResources) {
18017
- const resourcesRaw = _nullishCoalesce(_optionalChain([opts, 'optionalAccess', _140 => _140.resources]), () => ( WORKFLOW_RESOURCES));
17295
+ const resourcesRaw = _nullishCoalesce(_optionalChain([opts, 'optionalAccess', _121 => _121.resources]), () => ( WORKFLOW_RESOURCES));
18018
17296
  if (!Array.isArray(resourcesRaw) || resourcesRaw.length === 0) {
18019
17297
  throw new AutotaskConfigError("fieldCatalog: opts.resources must be a non-empty array of plan resource keys");
18020
17298
  }
@@ -18048,7 +17326,7 @@ function resolveTargets(opts, fieldOps, allResources) {
18048
17326
  return targets;
18049
17327
  }
18050
17328
  function resolveFieldsFilter(opts) {
18051
- const fieldsRaw = _optionalChain([opts, 'optionalAccess', _141 => _141.fields]);
17329
+ const fieldsRaw = _optionalChain([opts, 'optionalAccess', _122 => _122.fields]);
18052
17330
  if (fieldsRaw === void 0) return void 0;
18053
17331
  if (!Array.isArray(fieldsRaw) || fieldsRaw.length === 0) {
18054
17332
  throw new AutotaskConfigError("fieldCatalog: opts.fields must be a non-empty array of vendor field name strings");
@@ -18071,19 +17349,19 @@ function resolveLabels(opts) {
18071
17349
  `fieldCatalog: opts.labels must be a boolean (got ${typeof opts.labels})`
18072
17350
  );
18073
17351
  }
18074
- return _optionalChain([opts, 'optionalAccess', _142 => _142.labels]) === true;
17352
+ return _optionalChain([opts, 'optionalAccess', _123 => _123.labels]) === true;
18075
17353
  }
18076
17354
  function resolveSizeOpts(opts) {
18077
- if (_optionalChain([opts, 'optionalAccess', _143 => _143.compact]) !== void 0 && typeof opts.compact !== "boolean") {
17355
+ if (_optionalChain([opts, 'optionalAccess', _124 => _124.compact]) !== void 0 && typeof opts.compact !== "boolean") {
18078
17356
  throw new AutotaskConfigError(`fieldCatalog: opts.compact must be a boolean (got ${typeof opts.compact})`);
18079
17357
  }
18080
- const maxBytes = _optionalChain([opts, 'optionalAccess', _144 => _144.maxBytes]);
17358
+ const maxBytes = _optionalChain([opts, 'optionalAccess', _125 => _125.maxBytes]);
18081
17359
  if (maxBytes !== void 0 && (typeof maxBytes !== "number" || !Number.isInteger(maxBytes) || maxBytes < MIN_FIELD_CATALOG_MAX_BYTES)) {
18082
17360
  throw new AutotaskConfigError(
18083
17361
  `fieldCatalog: opts.maxBytes must be an integer >= ${MIN_FIELD_CATALOG_MAX_BYTES} (got ${String(maxBytes)})`
18084
17362
  );
18085
17363
  }
18086
- return { compact: _optionalChain([opts, 'optionalAccess', _145 => _145.compact]) === true, maxBytes };
17364
+ return { compact: _optionalChain([opts, 'optionalAccess', _126 => _126.compact]) === true, maxBytes };
18087
17365
  }
18088
17366
  var jsonBytes = (v) => Buffer.byteLength(JSON.stringify(v), "utf8");
18089
17367
  var TRIM_KINDS = ["udf", "fields", "picklistFields", "queryable", "readOnly", "required"];
@@ -18139,9 +17417,9 @@ function assertWithinWireCap(targetCount, kindCount) {
18139
17417
  }
18140
17418
  function kindFetches(holder, resource) {
18141
17419
  return {
18142
- info: () => _nullishCoalesce(_optionalChain([holder, 'optionalAccess', _146 => _146.getEntityInfo, 'optionalCall', _147 => _147()]), () => ( Promise.reject(new AutotaskError(`fieldCatalog: the client exposes no getEntityInfo read for "${resource}"`, { code: "INVALID_RESPONSE", category: "server" })))),
18143
- fields: () => _nullishCoalesce(_optionalChain([holder, 'optionalAccess', _148 => _148.getFieldInfo, 'optionalCall', _149 => _149()]), () => ( Promise.reject(new AutotaskError(`fieldCatalog: the client exposes no getFieldInfo read for "${resource}"`, { code: "INVALID_RESPONSE", category: "server" })))),
18144
- udf: () => _nullishCoalesce(_optionalChain([holder, 'optionalAccess', _150 => _150.getUdfInfo, 'optionalCall', _151 => _151()]), () => ( Promise.reject(new AutotaskError(`fieldCatalog: the client exposes no getUdfInfo read for "${resource}"`, { code: "INVALID_RESPONSE", category: "server" }))))
17420
+ info: () => _nullishCoalesce(_optionalChain([holder, 'optionalAccess', _127 => _127.getEntityInfo, 'optionalCall', _128 => _128()]), () => ( Promise.reject(new AutotaskError(`fieldCatalog: the client exposes no getEntityInfo read for "${resource}"`, { code: "INVALID_RESPONSE", category: "server" })))),
17421
+ fields: () => _nullishCoalesce(_optionalChain([holder, 'optionalAccess', _129 => _129.getFieldInfo, 'optionalCall', _130 => _130()]), () => ( Promise.reject(new AutotaskError(`fieldCatalog: the client exposes no getFieldInfo read for "${resource}"`, { code: "INVALID_RESPONSE", category: "server" })))),
17422
+ udf: () => _nullishCoalesce(_optionalChain([holder, 'optionalAccess', _131 => _131.getUdfInfo, 'optionalCall', _132 => _132()]), () => ( Promise.reject(new AutotaskError(`fieldCatalog: the client exposes no getUdfInfo read for "${resource}"`, { code: "INVALID_RESPONSE", category: "server" }))))
18145
17423
  };
18146
17424
  }
18147
17425
  async function sweepResource(ctx, resource, tally) {
@@ -18167,7 +17445,7 @@ async function sweepResource(ctx, resource, tally) {
18167
17445
  error: null
18168
17446
  };
18169
17447
  const fetches = kindFetches(holder, resource);
18170
- if (fresh) _optionalChain([cache, 'optionalAccess', _152 => _152.invalidate, 'call', _153 => _153(resource)]);
17448
+ if (fresh) _optionalChain([cache, 'optionalAccess', _133 => _133.invalidate, 'call', _134 => _134(`${resource}:`)]);
18171
17449
  const projectList = (kind, list) => {
18172
17450
  const projected = list.map((f) => projectFieldRow(f, labels, kind === "udf"));
18173
17451
  if (kind === "fields") applyFieldsFilter(projected, fieldsFilter, row);
@@ -18177,10 +17455,10 @@ async function sweepResource(ctx, resource, tally) {
18177
17455
  let allCached = true;
18178
17456
  const ages = [];
18179
17457
  for (const kind of include) {
18180
- const peek = cache !== void 0 && !fresh ? cache.peek(resource, kind) : { hit: false, age: null };
17458
+ const peek = cache !== void 0 && !fresh ? cache.peek(fieldKey(resource, kind)) : { hit: false, age: null };
18181
17459
  if (peek.hit !== true) allCached = false;
18182
17460
  if (peek.hit === true && cache !== void 0) {
18183
- const value2 = cache.get(resource, kind);
17461
+ const value2 = cache.get(fieldKey(resource, kind));
18184
17462
  if (kind === "info") row.info = _nullishCoalesce(value2, () => ( null));
18185
17463
  else projectList(kind, Array.isArray(value2) ? value2 : []);
18186
17464
  if (peek.age !== null) ages.push(peek.age);
@@ -18242,7 +17520,7 @@ async function fieldCatalog(client, opts) {
18242
17520
  const zone = _nullishCoalesce(client.config.zone, () => ( "unknown"));
18243
17521
  const rows = [];
18244
17522
  const tally = { wireUsed: 0, anyPicklistValues: false };
18245
- const ctx = { client, include, fieldsFilter, labels, compact, fresh: _optionalChain([opts, 'optionalAccess', _154 => _154.fresh]) === true, fieldOps, udfCapable };
17523
+ const ctx = { client, include, fieldsFilter, labels, compact, fresh: _optionalChain([opts, 'optionalAccess', _135 => _135.fresh]) === true, fieldOps, udfCapable };
18246
17524
  for (const resource of targets) {
18247
17525
  const row = await sweepResource(ctx, resource, tally);
18248
17526
  if (row !== void 0) rows.push(row);
@@ -18253,7 +17531,7 @@ async function fieldCatalog(client, opts) {
18253
17531
  cached: cache !== void 0,
18254
17532
  labels,
18255
17533
  compact,
18256
- ttlMs: _nullishCoalesce(_optionalChain([cache, 'optionalAccess', _155 => _155.ttlMs]), () => ( null)),
17534
+ ttlMs: _nullishCoalesce(_optionalChain([cache, 'optionalAccess', _136 => _136.ttlMs]), () => ( null)),
18257
17535
  fetchedAt: (/* @__PURE__ */ new Date()).toISOString(),
18258
17536
  rows,
18259
17537
  wireUsed: tally.wireUsed,
@@ -18471,159 +17749,6 @@ var BOUNDED_QUERY_MAX_LIMIT = 2500;
18471
17749
  var LATEST_MAX_COUNT_PROBES = 64;
18472
17750
  var MARKER_RECOVERY_LIMIT = 5;
18473
17751
 
18474
- // src/resources/base/helpers.ts
18475
- function describeResponseShape(body) {
18476
- if (body === null) return "null";
18477
- if (Array.isArray(body)) return `array(len=${body.length})`;
18478
- if (typeof body !== "object") return typeof body;
18479
- const rec = body;
18480
- const keys = Object.keys(rec).sort();
18481
- const typeOf = (v) => v === null ? "null" : Array.isArray(v) ? "array" : typeof v;
18482
- const parts = keys.slice(0, 16).map((k) => `${k}:${typeOf(rec[k])}`);
18483
- const more = keys.length > 16 ? `, +${keys.length - 16} more` : "";
18484
- return `{ ${parts.join(", ")}${more} }`;
18485
- }
18486
- function defaultMarkerToken(data) {
18487
- for (const field of ["name", "title"]) {
18488
- const v = data[field];
18489
- if (typeof v === "string" && v.trim() !== "") return v.trim();
18490
- }
18491
- return null;
18492
- }
18493
- var CONTEXT_ABSENT_MARKER = "#context-absent";
18494
- function contextAbsentKey(path) {
18495
- return path + "\0context-absent";
18496
- }
18497
- function noteCharsTruncationMarker(charsRemoved) {
18498
- return `\u2026 [+${charsRemoved} chars truncated]`;
18499
- }
18500
- function truncateNoteChars(items, cap, fields) {
18501
- let truncated = 0;
18502
- const out = items.map((item) => {
18503
- if (!isRecord2(item)) return item;
18504
- let changed = false;
18505
- const copy = { ...item };
18506
- for (const field of fields) {
18507
- const v = copy[field];
18508
- if (typeof v === "string" && v.length > cap) {
18509
- const end = cap > 0 && (v.charCodeAt(cap - 1) & 64512) === 55296 ? cap - 1 : cap;
18510
- copy[field] = v.slice(0, end) + noteCharsTruncationMarker(v.length - end);
18511
- truncated += 1;
18512
- changed = true;
18513
- }
18514
- }
18515
- return changed ? copy : item;
18516
- });
18517
- return [out, truncated];
18518
- }
18519
- function blindEmptyReason(stamp) {
18520
- return {
18521
- surface: stamp.surface,
18522
- kind: "blind-surface",
18523
- note: stamp.note,
18524
- authority: stamp.authority,
18525
- remedy: "the note is the remedy \u2014 the authority op answers for a record"
18526
- };
18527
- }
18528
- function attachUpdateTargetMissReason(err, key) {
18529
- const pinned = _optionalChain([EMPTY_REASON_STAMPS, 'access', _156 => _156[key], 'optionalAccess', _157 => _157.update]);
18530
- if (pinned === void 0) return;
18531
- err.details = { ..._nullishCoalesce(err.details, () => ( {})), emptyReason: pinned };
18532
- }
18533
- function buildDryRunResult(spec, policy = { redactPersonalData: true }) {
18534
- if (spec.operation === "") {
18535
- throw new AutotaskConfigError("dry-run: the operation name is required");
18536
- }
18537
- const wouldApply = spec.checks.every((check) => check.ok);
18538
- const resource = _nullishCoalesce(spec.operation.split(".")[0], () => ( spec.operation));
18539
- const diffPart = spec.diff !== void 0 ? {
18540
- diff: spec.diff.map((entry) => {
18541
- if (entry.to === void 0) return entry;
18542
- const to = isRedactedFieldName(entry.field, policy) ? REDACTED : redactResponseBody(entry.to, policy);
18543
- return { ...entry, to };
18544
- })
18545
- } : {};
18546
- const dataPart = spec.data !== void 0 ? { data: redactResponseBody(spec.data, policy) } : {};
18547
- return {
18548
- operation: spec.operation,
18549
- wouldApply,
18550
- target: { resource, ids: spec.ids },
18551
- request: { method: spec.method, path: spec.path },
18552
- ...diffPart,
18553
- checks: spec.checks,
18554
- impact: spec.impact,
18555
- simulated: true,
18556
- warnings: _nullishCoalesce(spec.warnings, () => ( [])),
18557
- ...dataPart,
18558
- ...spec.emptyReason !== void 0 ? { emptyReason: spec.emptyReason } : {}
18559
- };
18560
- }
18561
- function computeFieldDiff(current, data) {
18562
- const out = [];
18563
- for (const [field, to] of Object.entries(data)) {
18564
- if (field === "id" || to === void 0) continue;
18565
- const from = current[field];
18566
- if (from !== void 0 && JSON.stringify(from) === JSON.stringify(to)) continue;
18567
- out.push({ field, from: from === void 0 ? null : from, to });
18568
- }
18569
- return out;
18570
- }
18571
- function mergePartialBody(current, data) {
18572
- const merged = { ...current };
18573
- for (const [key, value] of Object.entries(data)) {
18574
- if (value === void 0) continue;
18575
- merged[key] = value;
18576
- }
18577
- return merged;
18578
- }
18579
- function normalizeUdfInput(value) {
18580
- if (Array.isArray(value)) {
18581
- return value.map((entry) => {
18582
- if (!isRecord2(entry) || typeof entry.name !== "string" || entry.name === "") {
18583
- throw new AutotaskConfigError("userDefinedFields array entries must be { name, value } objects");
18584
- }
18585
- return { name: entry.name, value: entry.value };
18586
- });
18587
- }
18588
- if (isRecord2(value)) {
18589
- return Object.entries(value).map(([name, entryValue]) => ({ name, value: entryValue }));
18590
- }
18591
- throw new AutotaskConfigError("userDefinedFields must be an array of { name, value } entries or a name-to-value map");
18592
- }
18593
- function blindStampFor(stamp, filter) {
18594
- if (stamp === void 0) return void 0;
18595
- if (stamp.kind !== "unfiltered-blind") return stamp;
18596
- const conditions = normalizedConditions(filter);
18597
- return conditions === void 0 || conditions.length === 0 ? stamp : void 0;
18598
- }
18599
- function decodeFileData(body) {
18600
- if (isRecord2(body)) {
18601
- const data = body.data;
18602
- if (typeof data === "string") return Buffer.from(data, "base64");
18603
- if (Array.isArray(data) && data.every((b) => typeof b === "number")) {
18604
- return Buffer.from(data);
18605
- }
18606
- }
18607
- if (typeof body === "string") return Buffer.from(body, "base64");
18608
- throw new AutotaskError(
18609
- "Expected the file envelope FileQueryResultModel { data } (or a base64 string); got a different shape",
18610
- { code: "INVALID_RESPONSE", category: "server" }
18611
- );
18612
- }
18613
- var DEPLURAL_EXCEPTIONS = {
18614
- tag_aliase: "tag_alias"
18615
- };
18616
- function entityNameFromPath(resourcePath) {
18617
- const segment = resourcePath.slice(resourcePath.lastIndexOf("/") + 1);
18618
- const snake = segment.replace(/([A-Z])/g, (_m, c, i) => (i > 0 ? `_` : "") + c.toLowerCase()).replace(/_{2,}/g, "_");
18619
- if (snake.endsWith("ies")) return `${snake.slice(0, -3)}y`;
18620
- if (snake.endsWith("s")) {
18621
- const stripped = snake.slice(0, -1);
18622
- return _nullishCoalesce(DEPLURAL_EXCEPTIONS[stripped], () => ( stripped));
18623
- }
18624
- return snake;
18625
- }
18626
-
18627
17752
  // src/resources/special/_shared/bounded-part.ts
18628
17753
  async function boundedPart(http, path, conditions, limit, operation, includeFields) {
18629
17754
  const body = { MaxRecords: limit, filter: conditions };
@@ -18829,7 +17954,7 @@ var QueryBase = class {
18829
17954
  }
18830
17955
  /** @internal */
18831
17956
  walkPageSize(params) {
18832
- return clampPageSize(_nullishCoalesce(_optionalChain([params, 'optionalAccess', _158 => _158.maxRecords]), () => ( this.defaultPageSize)));
17957
+ return clampPageSize(_nullishCoalesce(_optionalChain([params, 'optionalAccess', _137 => _137.maxRecords]), () => ( this.defaultPageSize)));
18833
17958
  }
18834
17959
  /** Issue #281: relation filters (`ticket.<field>` …) resolve before a walk,
18835
17960
  * through the same rewrite as queryBounded / count. `undefined` = no parent
@@ -18837,7 +17962,7 @@ var QueryBase = class {
18837
17962
  async relationWalkParams(params, op) {
18838
17963
  const p = _nullishCoalesce(params, () => ( {}));
18839
17964
  const relation = await rewriteRelationFilters(this.http, this.config.key, normalizedConditions(p.filter), op);
18840
- if (_optionalChain([relation, 'optionalAccess', _159 => _159.never]) === true) return void 0;
17965
+ if (_optionalChain([relation, 'optionalAccess', _138 => _138.never]) === true) return void 0;
18841
17966
  return relation === void 0 ? p : { ...p, filter: relation.conditions };
18842
17967
  }
18843
17968
  /**
@@ -18912,19 +18037,19 @@ var QueryBase = class {
18912
18037
  "queryBounded: params must be an object { filter?, limit?, latest?, includeFields? } \u2014 a bare conditions array is not accepted (wrap it: { filter: [...] })"
18913
18038
  );
18914
18039
  }
18915
- const limit = _nullishCoalesce(_optionalChain([params, 'optionalAccess', _160 => _160.limit]), () => ( BOUNDED_QUERY_DEFAULT_LIMIT));
18040
+ const limit = _nullishCoalesce(_optionalChain([params, 'optionalAccess', _139 => _139.limit]), () => ( BOUNDED_QUERY_DEFAULT_LIMIT));
18916
18041
  if (!Number.isInteger(limit) || limit < 1 || limit > BOUNDED_QUERY_MAX_LIMIT) {
18917
18042
  throw new AutotaskConfigError(
18918
18043
  `queryBounded: limit must be an integer 1..${BOUNDED_QUERY_MAX_LIMIT} (got ${String(limit)}) \u2014 it is the TOTAL record cap, not a page size`
18919
18044
  );
18920
18045
  }
18921
- const noteChars = _optionalChain([params, 'optionalAccess', _161 => _161.noteChars]);
18046
+ const noteChars = _optionalChain([params, 'optionalAccess', _140 => _140.noteChars]);
18922
18047
  if (noteChars !== void 0 && (!Number.isInteger(noteChars) || noteChars < 1)) {
18923
18048
  throw new AutotaskConfigError(
18924
18049
  `queryBounded: noteChars must be an integer >= 1 (got ${String(noteChars)}) \u2014 it is the per-field char cap on the untrusted-class free-text fields (absent = the raw, untruncated result)`
18925
18050
  );
18926
18051
  }
18927
- let walkParams = _optionalChain([params, 'optionalAccess', _162 => _162.fieldsPreset]) !== void 0 ? { ...params, includeFields: resolveFieldsPreset(params.fieldsPreset, this.config.key, params.includeFields) } : _nullishCoalesce(params, () => ( {}));
18052
+ let walkParams = _optionalChain([params, 'optionalAccess', _141 => _141.fieldsPreset]) !== void 0 ? { ...params, includeFields: resolveFieldsPreset(params.fieldsPreset, this.config.key, params.includeFields) } : _nullishCoalesce(params, () => ( {}));
18928
18053
  const latest = walkParams.latest;
18929
18054
  if (latest !== void 0) {
18930
18055
  if (!Number.isInteger(latest) || latest < 1 || latest > BOUNDED_QUERY_MAX_LIMIT) {
@@ -18932,14 +18057,14 @@ var QueryBase = class {
18932
18057
  `queryBounded: latest must be an integer 1..${BOUNDED_QUERY_MAX_LIMIT} (got ${String(latest)}) \u2014 it is the count of the highest-id rows to return`
18933
18058
  );
18934
18059
  }
18935
- if (_optionalChain([params, 'optionalAccess', _163 => _163.limit]) !== void 0 && limit < latest) {
18060
+ if (_optionalChain([params, 'optionalAccess', _142 => _142.limit]) !== void 0 && limit < latest) {
18936
18061
  throw new AutotaskConfigError(
18937
18062
  `queryBounded: limit (${String(limit)}) must be >= latest (${String(latest)}) \u2014 latest governs the returned count and limit is the walk window`
18938
18063
  );
18939
18064
  }
18940
18065
  }
18941
18066
  const relation = await rewriteRelationFilters(this.http, this.config.key, normalizedConditions(walkParams.filter), `${this.config.key}.query`);
18942
- if (_optionalChain([relation, 'optionalAccess', _164 => _164.never]) === true) return { items: [], fetched: 0, hasMore: false, pagesWalked: 0, relationNote: relation.note };
18067
+ if (_optionalChain([relation, 'optionalAccess', _143 => _143.never]) === true) return { items: [], fetched: 0, hasMore: false, pagesWalked: 0, relationNote: relation.note };
18943
18068
  if (relation !== void 0) walkParams = { ...walkParams, filter: relation.conditions };
18944
18069
  if (latest !== void 0) {
18945
18070
  return this.applyNoteChars(await this.latestWalk(walkParams, Math.max(limit, latest), latest), noteChars);
@@ -19023,8 +18148,8 @@ var QueryBase = class {
19023
18148
  * @internal */
19024
18149
  stampEmptyQueryResult(result, params) {
19025
18150
  if (result.items.length !== 0) return;
19026
- result.blindSurface = blindStampFor(_optionalChain([BLIND_SURFACE_STAMPS, 'access', _165 => _165[this.config.key], 'optionalAccess', _166 => _166.query]), _optionalChain([params, 'optionalAccess', _167 => _167.filter]));
19027
- const pinnedReason = normalizedConditions(_optionalChain([params, 'optionalAccess', _168 => _168.filter])) === void 0 ? _optionalChain([EMPTY_REASON_STAMPS, 'access', _169 => _169[this.config.key], 'optionalAccess', _170 => _170.query]) : void 0;
18151
+ result.blindSurface = blindStampFor(_optionalChain([BLIND_SURFACE_STAMPS, 'access', _144 => _144[this.config.key], 'optionalAccess', _145 => _145.query]), _optionalChain([params, 'optionalAccess', _146 => _146.filter]));
18152
+ const pinnedReason = normalizedConditions(_optionalChain([params, 'optionalAccess', _147 => _147.filter])) === void 0 ? _optionalChain([EMPTY_REASON_STAMPS, 'access', _148 => _148[this.config.key], 'optionalAccess', _149 => _149.query]) : void 0;
19028
18153
  if (result.blindSurface !== void 0 || pinnedReason !== void 0) {
19029
18154
  result.emptyReason = result.blindSurface !== void 0 ? blindEmptyReason(result.blindSurface) : pinnedReason;
19030
18155
  }
@@ -19039,59 +18164,18 @@ var QueryBase = class {
19039
18164
  const bigB = typeof b === "number" ? BigInt(String(b)) : BigInt(b);
19040
18165
  return bigA < bigB ? -1 : bigA > bigB ? 1 : 0;
19041
18166
  }
19042
- /** Issue #127: the `latest` walk's count probes through the per-client
19043
- * short-TTL count cache (`LatestCountCache` — fixed 30 s TTL, the
19044
- * memory guard; cleared on every real write through this client, the
19045
- * write-then-read re-fetch rule). Only a SUCCESSFUL plain-number vendor
19046
- * count answer is stored: the blind-surface / pinned-empty sentinels are
19047
- * RECORDS and pass through un-stored (the plan-pinned stamps stay fresh
19048
- * and re-derive on every call), and an error rejects before any store.
19049
- * The key is (resource plan key, canonical filter serialisation — the
19050
- * normalised conditions' JSON): the initial `count(F)` probe and every
19051
- * `id gt T` threshold probe are separate entries, and a re-asked
19052
- * "top N" within the TTL makes ZERO extra count calls for every CACHED
19053
- * probe (the bracket/search sequence is deterministic given the counts);
19054
- * a probe that answered 0 is never cached, so it re-wires (issue #157).
19055
- * Issue #157: two corrections to the snapshot trade. (1) A total of 0 is
19056
- * NEVER stored — a 0 is rare and cheap to re-probe, and a cached 0 fed
19057
- * the false-empty branch (a repeat after an out-of-band insert returned
19058
- * no rows without walking). (2) `fresh: true` DELETES the entry before
19059
- * the wire read and re-stores the fresh answer — the ONE re-probe the
19060
- * stale-LOW recovery in `latestWalk` is allowed. Counts are snapshots,
19061
- * not contracts: the truncation guard covers the growth-of-total
19062
- * direction; a stale-LOW total within the TTL is resolved by that one
19063
- * fresh re-probe (never by the guard alone). The plain `count` op and
19064
- * `operations.aggregate` call `countGlobal` directly and promise fresh
19065
- * totals — this wrapper is used ONLY on the latest-walk probe path.
19066
- * @internal
19067
- */
19068
- async latestProbeCount(filter, opts = {}) {
19069
- const cache = this.http.latestCountCache;
19070
- const key = LatestCountCache.key(this.config.key, normalizedConditions(filter));
19071
- if (opts.fresh === true) {
19072
- cache.delete(key);
19073
- } else {
19074
- const hit = cache.get(key);
19075
- if (hit !== void 0) return { value: hit, cached: true };
19076
- }
19077
- const epoch = cache.epoch;
19078
- const res = await this.countGlobal({ filter });
19079
- const value = res;
19080
- if (typeof value === "number" && value !== 0) cache.set(key, value, epoch);
19081
- return { value, cached: false };
19082
- }
19083
18167
  /** Issue #59: one `latest` threshold count probe — a ROWLESS
19084
18168
  * `count(F ∧ id gt T)` call (the vendor caps — ≤500 OR-conditions,
19085
18169
  * 8000 chars serialised — leave a single `gt` condition room on any
19086
- * filter). Issue #127: it rides the per-client short-TTL count cache — the same (resource, conditions) probe within the 30 s window is reused (zero vendor calls); a sentinel answer is never cached. The `BlindCountResult` sentinel (a record, not a number —
18170
+ * filter). Counts are never cached. The `BlindCountResult` sentinel (a record, not a number —
19087
18171
  * the declared `number` return is the documented under-type) unwraps to
19088
18172
  * its `count` value (always 0): at or above the max matching id the
19089
18173
  * count is 0, which is exactly the bracket-termination signal.
19090
18174
  * @internal */
19091
18175
  async countAboveId(baseConds, t) {
19092
18176
  const value = t <= BigInt(Number.MAX_SAFE_INTEGER) ? Number(t) : t.toString();
19093
- const probed = await this.latestProbeCount([...baseConds, { op: "gt", field: "id", value }]);
19094
- return typeof probed.value === "object" && probed.value !== null && "count" in probed.value ? probed.value.count : probed.value;
18177
+ const probed = await this.countGlobal({ filter: [...baseConds, { op: "gt", field: "id", value }] });
18178
+ return typeof probed === "object" && probed !== null && "count" in probed ? probed.count : probed;
19095
18179
  }
19096
18180
  /**
19097
18181
  * Issue #59: the `latest: N` mechanism — the N highest-`id` rows,
@@ -19109,10 +18193,7 @@ var QueryBase = class {
19109
18193
  * 1 + ceil(C/500) <= 6, independent of table size. A truncated walk
19110
18194
  * (the vendor's rows disagree with its own count) is
19111
18195
  * LATEST_WINDOW_INCOMPLETE — the top-N of a truncated set is
19112
- * unprovable — EXCEPT issue #157: when `C` came from the CACHE, the
19113
- * total is re-probed ONCE fresh (the stale-LOW recovery: a larger
19114
- * fresh total continues the walk against it; the refusal stands only
19115
- * when the fresh probe confirms the cached value). Sort
18196
+ * unprovable. Sort
19116
18197
  * id-descending; return the top N (a set of C < N returns itself).
19117
18198
  * - `C > 2500` → THRESHOLD mode: find the smallest id threshold `T`
19118
18199
  * with `count(F ∧ id gt T) <= N` by exponential bracketing
@@ -19140,30 +18221,15 @@ var QueryBase = class {
19140
18221
  * answer.
19141
18222
  * `includeFields` always carries `id` (the P1 id-append,
19142
18223
  * `buildQueryRequest`), which `latest` sorts on. Pure read; no mutation
19143
- * paths involved. Issue #127: every count probe of this walk (the
19144
- * initial `count(F)` + the `id gt T` threshold probes) rides the
19145
- * per-client short-TTL count cache (30 s; a real write through this
19146
- * client clears it) — a re-asked "top N" within the window reuses every
19147
- * CACHED probe (zero extra vendor count calls); sentinel answers are
19148
- * never cached (the plan-pinned stamps stay fresh), and a 0 total is
19149
- * never cached either (issue #157: a 0 is cheap to re-probe and a cached
19150
- * 0 would shadow an out-of-band insert), so a 0-answering probe re-wires.
19151
- * The stale-LOW direction is closed by the ONE fresh re-probe above. The
19152
- * stale-HIGH direction is the documented snapshot trade, NOT "safe by
19153
- * construction": the threshold probes are cache-served like every other
19154
- * probe (no fresh re-probe exists on that path), so a cached threshold
19155
- * count higher than reality can under-report the ROW COUNT within the
19156
- * TTL — the returned top-N stays correct, and full-set mode ends when the
19157
- * cursor exhausts. The plain `count` op and
19158
- * `operations.aggregate` bypass the cache by construction and promise
19159
- * fresh totals.
18224
+ * paths involved; every count probe goes to the wire (counts are never
18225
+ * cached).
19160
18226
  * @internal
19161
18227
  */
19162
18228
  async latestWalk(params, limit, latest) {
19163
18229
  const baseConds = _nullishCoalesce(normalizedConditions(params.filter), () => ( []));
19164
- const probe = await this.latestProbeCount(params.filter);
19165
- if (typeof probe.value === "object" && probe.value !== null) {
19166
- const rec = probe.value;
18230
+ const total = await this.countGlobal({ filter: params.filter });
18231
+ if (typeof total === "object" && total !== null) {
18232
+ const rec = total;
19167
18233
  if (rec.blindSurface !== void 0) {
19168
18234
  return this.latestBlindWalk(params, limit, latest);
19169
18235
  }
@@ -19171,60 +18237,46 @@ var QueryBase = class {
19171
18237
  this.stampEmptyQueryResult(result, params);
19172
18238
  return result;
19173
18239
  }
19174
- let total = probe.value;
19175
- let cacheSourced = probe.cached;
19176
- let reProbed = false;
19177
- for (; ; ) {
19178
- if (total === 0) {
19179
- const result = { items: [], fetched: 0, hasMore: false, pagesWalked: 0 };
19180
- this.stampEmptyQueryResult(result, params);
19181
- return result;
19182
- }
19183
- if (total <= BOUNDED_QUERY_MAX_LIMIT) {
19184
- const { items: items2, pages: pages2, guard: guard2, sliced: sliced2, cursor: cursor2 } = await this.walkBounded(params, total);
19185
- if (this.walkTruncated(total, { itemsLength: items2.length, guard: guard2, sliced: sliced2, cursor: cursor2 })) {
19186
- if (cacheSourced && !reProbed) {
19187
- reProbed = true;
19188
- const fresh = await this.latestProbeCount(params.filter, { fresh: true });
19189
- if (typeof fresh.value === "number" && fresh.value > total) {
19190
- total = fresh.value;
19191
- cacheSourced = false;
19192
- continue;
19193
- }
19194
- }
19195
- throw this.latestWindowIncompleteError(total);
19196
- }
19197
- return this.finishLatest(items2, latest, pages2);
19198
- }
19199
- let probes = reProbed ? 2 : 1;
19200
- const spend = () => {
19201
- probes += 1;
19202
- if (probes > LATEST_MAX_COUNT_PROBES) throw this.latestThresholdUnresolvedError();
19203
- };
19204
- let lo = 0n;
19205
- let hi = 0n;
19206
- for (let t = 1n; ; t = t << 1n) {
19207
- spend();
19208
- if (await this.countAboveId(baseConds, t) <= latest) {
19209
- hi = t;
19210
- break;
19211
- }
19212
- lo = t;
19213
- }
19214
- while (hi - lo > 1n) {
19215
- const mid = lo + hi >> 1n;
19216
- spend();
19217
- if (await this.countAboveId(baseConds, mid) <= latest) hi = mid;
19218
- else lo = mid;
18240
+ if (total === 0) {
18241
+ const result = { items: [], fetched: 0, hasMore: false, pagesWalked: 0 };
18242
+ this.stampEmptyQueryResult(result, params);
18243
+ return result;
18244
+ }
18245
+ if (total <= BOUNDED_QUERY_MAX_LIMIT) {
18246
+ const { items: items2, pages: pages2, guard: guard2, sliced: sliced2, cursor: cursor2 } = await this.walkBounded(params, total);
18247
+ if (this.walkTruncated(total, { itemsLength: items2.length, guard: guard2, sliced: sliced2, cursor: cursor2 })) {
18248
+ throw this.latestWindowIncompleteError(total);
19219
18249
  }
19220
- const windowValue = hi <= BigInt(Number.MAX_SAFE_INTEGER) ? Number(hi) : hi.toString();
19221
- const window = [...baseConds, { op: "gte", field: "id", value: windowValue }];
19222
- const { items, pages, guard, sliced, cursor } = await this.walkBounded({ ...params, filter: window }, latest + 1);
19223
- if (this.walkTruncated(latest + 1, { itemsLength: items.length, guard, sliced, cursor })) {
19224
- throw this.latestWindowIncompleteError(latest + 1);
18250
+ return this.finishLatest(items2, latest, pages2);
18251
+ }
18252
+ let probes = 1;
18253
+ const spend = () => {
18254
+ probes += 1;
18255
+ if (probes > LATEST_MAX_COUNT_PROBES) throw this.latestThresholdUnresolvedError();
18256
+ };
18257
+ let lo = 0n;
18258
+ let hi = 0n;
18259
+ for (let t = 1n; ; t = t << 1n) {
18260
+ spend();
18261
+ if (await this.countAboveId(baseConds, t) <= latest) {
18262
+ hi = t;
18263
+ break;
19225
18264
  }
19226
- return this.finishLatest(items, latest, pages);
18265
+ lo = t;
18266
+ }
18267
+ while (hi - lo > 1n) {
18268
+ const mid = lo + hi >> 1n;
18269
+ spend();
18270
+ if (await this.countAboveId(baseConds, mid) <= latest) hi = mid;
18271
+ else lo = mid;
19227
18272
  }
18273
+ const windowValue = hi <= BigInt(Number.MAX_SAFE_INTEGER) ? Number(hi) : hi.toString();
18274
+ const window = [...baseConds, { op: "gte", field: "id", value: windowValue }];
18275
+ const { items, pages, guard, sliced, cursor } = await this.walkBounded({ ...params, filter: window }, latest + 1);
18276
+ if (this.walkTruncated(latest + 1, { itemsLength: items.length, guard, sliced, cursor })) {
18277
+ throw this.latestWindowIncompleteError(latest + 1);
18278
+ }
18279
+ return this.finishLatest(items, latest, pages);
19228
18280
  }
19229
18281
  /** The shared `latest` completion: the (provably complete or
19230
18282
  * stamp-scoped) set sorted id-descending, top N taken. `hasMore: false`
@@ -19253,7 +18305,7 @@ var QueryBase = class {
19253
18305
  if (result.items.length === 0) {
19254
18306
  this.stampEmptyQueryResult(result, params);
19255
18307
  } else {
19256
- result.blindSurface = blindStampFor(_optionalChain([BLIND_SURFACE_STAMPS, 'access', _171 => _171[this.config.key], 'optionalAccess', _172 => _172.query]), params.filter);
18308
+ result.blindSurface = blindStampFor(_optionalChain([BLIND_SURFACE_STAMPS, 'access', _150 => _150[this.config.key], 'optionalAccess', _151 => _151.query]), params.filter);
19257
18309
  }
19258
18310
  return result;
19259
18311
  }
@@ -19294,9 +18346,9 @@ var QueryBase = class {
19294
18346
  * or, on a plan-pinned blind surface, the `{ count: 0, blindSurface }`
19295
18347
  * typed sentinel (S12-C T10; see `BlindCountResult`). */
19296
18348
  async countGlobal(params) {
19297
- const relation = await rewriteRelationFilters(this.http, this.config.key, normalizedConditions(_optionalChain([params, 'optionalAccess', _173 => _173.filter])), `${this.config.key}.count`);
19298
- if (_optionalChain([relation, 'optionalAccess', _174 => _174.never]) === true) return 0;
19299
- const conditions = relation !== void 0 ? relation.conditions : normalizedConditions(_optionalChain([params, 'optionalAccess', _175 => _175.filter]));
18349
+ const relation = await rewriteRelationFilters(this.http, this.config.key, normalizedConditions(_optionalChain([params, 'optionalAccess', _152 => _152.filter])), `${this.config.key}.count`);
18350
+ if (_optionalChain([relation, 'optionalAccess', _153 => _153.never]) === true) return 0;
18351
+ const conditions = relation !== void 0 ? relation.conditions : normalizedConditions(_optionalChain([params, 'optionalAccess', _154 => _154.filter]));
19300
18352
  const body = {};
19301
18353
  body.filter = conditions === void 0 ? [] : buildFilter(conditions).filter;
19302
18354
  const res = await this.http.request({
@@ -19307,9 +18359,9 @@ var QueryBase = class {
19307
18359
  idempotent: true
19308
18360
  });
19309
18361
  const count = unwrapQueryCount(res, `${this.config.key}.count`);
19310
- const stamp = blindStampFor(_optionalChain([BLIND_SURFACE_STAMPS, 'access', _176 => _176[this.config.key], 'optionalAccess', _177 => _177.count]), _optionalChain([params, 'optionalAccess', _178 => _178.filter]));
18362
+ const stamp = blindStampFor(_optionalChain([BLIND_SURFACE_STAMPS, 'access', _155 => _155[this.config.key], 'optionalAccess', _156 => _156.count]), _optionalChain([params, 'optionalAccess', _157 => _157.filter]));
19311
18363
  if (count === 0) {
19312
- const pinnedReason = conditions === void 0 ? _optionalChain([EMPTY_REASON_STAMPS, 'access', _179 => _179[this.config.key], 'optionalAccess', _180 => _180.count]) : void 0;
18364
+ const pinnedReason = conditions === void 0 ? _optionalChain([EMPTY_REASON_STAMPS, 'access', _158 => _158[this.config.key], 'optionalAccess', _159 => _159.count]) : void 0;
19313
18365
  if (stamp !== void 0 || pinnedReason !== void 0) {
19314
18366
  return {
19315
18367
  count,
@@ -19335,19 +18387,17 @@ var MetaBase = class extends QueryBase {
19335
18387
  * other callers): mutation-path reads (stale-object guard, dry-run diff)
19336
18388
  * and the composite ROOT read of a hot resource (issue #36 — the
19337
18389
  * ticket-composite root) must see the live record, never a snapshot.
19338
- * `resource` (issue #32) is the plan key of the entity this path
19339
- * points to: the stored entry's TTL is the per-resource override for
19340
- * that key when `cache.perResourceTtlMs` names it, else `ttlMs`.
18390
+ * The entry's TTL follows the path (`readTtlFor`: reference tables 1 h).
19341
18391
  * @internal */
19342
- async cachedGet(path, operation, fresh = false, resource) {
18392
+ async cachedGet(path, operation, fresh = false) {
19343
18393
  const cache = this.http.readCache;
19344
18394
  if (cache !== void 0 && !fresh) {
19345
18395
  const hit = cache.get(path);
19346
18396
  if (hit !== void 0) return hit;
19347
18397
  }
19348
- const epoch = _optionalChain([cache, 'optionalAccess', _181 => _181.epoch]);
18398
+ const epoch = _optionalChain([cache, 'optionalAccess', _160 => _160.epoch]);
19349
18399
  const res = await this.http.request({ method: "GET", path, operation });
19350
- _optionalChain([cache, 'optionalAccess', _182 => _182.set, 'call', _183 => _183(path, res, resource, epoch)]);
18400
+ _optionalChain([cache, 'optionalAccess', _161 => _161.set, 'call', _162 => _162(path, res, epoch)]);
19351
18401
  return res;
19352
18402
  }
19353
18403
  /**
@@ -19374,29 +18424,29 @@ var MetaBase = class extends QueryBase {
19374
18424
  const res = await this.http.request({ method: "GET", path, operation });
19375
18425
  return unwrap(res);
19376
18426
  }
19377
- const key = this.config.key;
18427
+ const key = fieldKey(this.config.key, kind);
19378
18428
  if (!fresh) {
19379
- const shared = cache.inflight(key, kind);
18429
+ const shared = cache.inflight(key);
19380
18430
  if (shared !== void 0) return shared;
19381
- const hit = cache.get(key, kind);
18431
+ const hit = cache.get(key);
19382
18432
  if (hit !== void 0) return hit;
19383
18433
  }
19384
18434
  const work = this.http.request({ method: "GET", path, operation }).then((body) => {
19385
18435
  const value = unwrap(body);
19386
- cache.set(key, kind, value);
18436
+ if (!isEmpty(value)) cache.set(key, value);
19387
18437
  return value;
19388
18438
  });
19389
18439
  work.catch((err) => {
19390
- if (err instanceof AutotaskError && (err.code === "INVALID_RESPONSE" || _optionalChain([err, 'access', _184 => _184.details, 'optionalAccess', _185 => _185.deterministic]) === true)) {
19391
- cache.invalidate(key);
18440
+ if (err instanceof AutotaskError && (err.code === "INVALID_RESPONSE" || _optionalChain([err, 'access', _163 => _163.details, 'optionalAccess', _164 => _164.deterministic]) === true)) {
18441
+ cache.invalidate(`${this.config.key}:`);
19392
18442
  }
19393
18443
  });
19394
- return cache.track(key, kind, work);
18444
+ return cache.track(key, work);
19395
18445
  }
19396
18446
  /** `GET /V1.0/<E>/{id}` → `{ item }` unwrapped (`item: null` → NotFoundError). */
19397
18447
  async getGlobal(id, fresh = false) {
19398
18448
  const numeric = parseIdentifier(id, "id");
19399
- const res = await this.cachedGet(`${this.resourcePath}/${numeric}`, `${this.config.key}.get`, fresh, this.config.key);
18449
+ const res = await this.cachedGet(`${this.resourcePath}/${numeric}`, `${this.config.key}.get`, fresh);
19400
18450
  return unwrapItem(res, numeric, `${this.config.key}.get`, this.entityName);
19401
18451
  }
19402
18452
  /**
@@ -19417,7 +18467,7 @@ var MetaBase = class extends QueryBase {
19417
18467
  path: `${this.resourcePath}/${numeric}`,
19418
18468
  operation: `${this.config.key}.get`,
19419
18469
  skipResponseRedaction: true,
19420
- maxRetries: _optionalChain([readOpts, 'optionalAccess', _186 => _186.maxRetries])
18470
+ maxRetries: _optionalChain([readOpts, 'optionalAccess', _165 => _165.maxRetries])
19421
18471
  });
19422
18472
  return unwrapItem(res, numeric, `${this.config.key}.get`, this.entityName);
19423
18473
  }
@@ -19436,14 +18486,14 @@ var MetaBase = class extends QueryBase {
19436
18486
  path: this.scopedItemPath(parentId, numeric),
19437
18487
  operation: `${this.config.key}.get`,
19438
18488
  skipResponseRedaction: true,
19439
- maxRetries: _optionalChain([readOpts, 'optionalAccess', _187 => _187.maxRetries])
18489
+ maxRetries: _optionalChain([readOpts, 'optionalAccess', _166 => _166.maxRetries])
19440
18490
  });
19441
18491
  return unwrapItem(res, numeric, `${this.config.key}.get`, this.entityName);
19442
18492
  }
19443
18493
  /** Parent-scoped `GET .../{parentId}/<Child>/{id}` → `{ item }` unwrapped. */
19444
18494
  async getScopedRaw(parentId, id, fresh = false) {
19445
18495
  const numeric = parseIdentifier(id, "id");
19446
- const res = await this.cachedGet(this.scopedItemPath(parentId, id), `${this.config.key}.get`, fresh, this.config.key);
18496
+ const res = await this.cachedGet(this.scopedItemPath(parentId, id), `${this.config.key}.get`, fresh);
19447
18497
  return unwrapItem(res, numeric, `${this.config.key}.get`, this.entityName);
19448
18498
  }
19449
18499
  /**
@@ -19453,7 +18503,7 @@ var MetaBase = class extends QueryBase {
19453
18503
  */
19454
18504
  async getScopedModel(parentId, id, fresh = false) {
19455
18505
  const numeric = parseIdentifier(id, "id");
19456
- const res = await this.cachedGet(this.scopedItemPath(parentId, id), `${this.config.key}.get`, fresh, this.config.key);
18506
+ const res = await this.cachedGet(this.scopedItemPath(parentId, id), `${this.config.key}.get`, fresh);
19457
18507
  return this.unwrapModelLike(res, numeric, this.entityName);
19458
18508
  }
19459
18509
  /** @internal */
@@ -19492,7 +18542,7 @@ var MetaBase = class extends QueryBase {
19492
18542
  // ------------------------------------------------------------------
19493
18543
  /** `GET /V1.0/<E>/entityInformation` → `{ info }` unwrapped.
19494
18544
  * Goes through the field cache when it is present (issue #80:
19495
- * default-on; `fieldCache: false` = the bit-for-bit uncached wire
18545
+ * default-on; `cache: false` = the bit-for-bit uncached wire
19496
18546
  * path). A fresh
19497
18547
  * (bypass + refresh) metadata read is the caller's act: drop the
19498
18548
  * entity's entries via the fieldCache's `invalidate` first — the trio
@@ -19516,7 +18566,7 @@ var MetaBase = class extends QueryBase {
19516
18566
  }
19517
18567
  /** `GET /V1.0/<E>/entityInformation/fields` → `{ fields }` unwrapped.
19518
18568
  * Goes through the field cache when it is present (issue #80:
19519
- * default-on; `fieldCache: false` = the bit-for-bit uncached wire
18569
+ * default-on; `cache: false` = the bit-for-bit uncached wire
19520
18570
  * path). A fresh
19521
18571
  * (bypass + refresh) metadata read is the caller's act: drop the
19522
18572
  * entity's entries via the fieldCache's `invalidate` first — the trio
@@ -19531,7 +18581,7 @@ var MetaBase = class extends QueryBase {
19531
18581
  }
19532
18582
  /** `GET /V1.0/<E>/entityInformation/userDefinedFields` → `{ fields }` unwrapped.
19533
18583
  * Goes through the field cache when it is present (issue #80:
19534
- * default-on; `fieldCache: false` = the bit-for-bit uncached wire
18584
+ * default-on; `cache: false` = the bit-for-bit uncached wire
19535
18585
  * path). A fresh
19536
18586
  * (bypass + refresh) metadata read is the caller's act: drop the
19537
18587
  * entity's entries via the fieldCache's `invalidate` first — the trio
@@ -19598,8 +18648,7 @@ var MetaBase = class extends QueryBase {
19598
18648
  * record, never a snapshot. */
19599
18649
  async fetchRecordRaw(entityPath, id, fresh = false) {
19600
18650
  const numeric = parseIdentifier(id, "id");
19601
- const resource = RESOURCE_KEY_BY_PATH[entityPath];
19602
- const res = await this.cachedGet(`${entityPath}/${numeric}`, `${this.config.key}.fetchRecord`, fresh, resource);
18651
+ const res = await this.cachedGet(`${entityPath}/${numeric}`, `${this.config.key}.fetchRecord`, fresh);
19603
18652
  if (this.config.attachmentGet) {
19604
18653
  return this.unwrapModelLike(res, numeric, entityNameFromPath(entityPath));
19605
18654
  }
@@ -19613,16 +18662,16 @@ var MetaBase = class extends QueryBase {
19613
18662
  *
19614
18663
  * Issue #36 (negative caching, the pinned hazard): the vendor's true
19615
18664
  * not-found (the HTTP 404 or the 200 `item: null` signal — both surface
19616
- * as `NotFoundError`) is stored as an ABSENT marker in the SAME ReadCache
18665
+ * as `NotFoundError`) is stored as an ABSENT marker in the SAME read cache
19617
18666
  * under a namespaced key the positive get path never constructs, so a
19618
18667
  * repeat composite call serves 'absent' with zero wire for that part.
19619
- * The marker expires on the READ entity's TTL (the #32 per-resource
19620
- * table / `perResourceTtlMs` override, via `set`'s resource key) and is
19621
- * cleared by any real write through the client (the ReadCache clear).
18668
+ * The marker expires on the READ entity's TTL (the key carries the read
18669
+ * path, so `readTtlFor` applies) and is
18670
+ * cleared by any real write through the client (the read cache clear).
19622
18671
  * A transient fault (5xx, shape drift, network) is NEVER stored as an
19623
18672
  * ABSENT marker — it propagates and the next call re-wires (no
19624
18673
  * poisoning; a malformed 200 body may still land under the POSITIVE
19625
- * key — pre-existing ReadCache behaviour, out of this mechanism's
18674
+ * key — pre-existing read cache behaviour, out of this mechanism's
19626
18675
  * scope). The marker is consulted BEFORE the positive lookup, but a
19627
18676
  * POSITIVE entry that exists alongside a live marker means a newer
19628
18677
  * positive outcome (this client read/created the record after the
@@ -19645,12 +18694,12 @@ var MetaBase = class extends QueryBase {
19645
18694
  return null;
19646
18695
  }
19647
18696
  }
19648
- const epoch = _optionalChain([cache, 'optionalAccess', _188 => _188.epoch]);
18697
+ const epoch = _optionalChain([cache, 'optionalAccess', _167 => _167.epoch]);
19649
18698
  try {
19650
18699
  return await this.fetchRecordRaw(entityPath, referenceId);
19651
18700
  } catch (err) {
19652
18701
  if (err instanceof NotFoundError) {
19653
- _optionalChain([cache, 'optionalAccess', _189 => _189.set, 'call', _190 => _190(absentKey, CONTEXT_ABSENT_MARKER, RESOURCE_KEY_BY_PATH[entityPath], epoch)]);
18702
+ _optionalChain([cache, 'optionalAccess', _168 => _168.set, 'call', _169 => _169(absentKey, CONTEXT_ABSENT_MARKER, epoch)]);
19654
18703
  return null;
19655
18704
  }
19656
18705
  throw err;
@@ -19767,7 +18816,7 @@ var MetaBase = class extends QueryBase {
19767
18816
  missDetail(operator, fields) {
19768
18817
  const key = this.config.key;
19769
18818
  const probe = fields.map((field) => ({ field, op: operator, value: "" }));
19770
- const blind = blindStampFor(_optionalChain([BLIND_SURFACE_STAMPS, 'access', _191 => _191[key], 'optionalAccess', _192 => _192.query]), probe);
18819
+ const blind = blindStampFor(_optionalChain([BLIND_SURFACE_STAMPS, 'access', _170 => _170[key], 'optionalAccess', _171 => _171.query]), probe);
19771
18820
  const next = [];
19772
18821
  if (blind !== void 0) {
19773
18822
  next.push(`This surface is blind to records the API user does not own, so a miss is not proof of absence: ${blind.authority} returns the record when the id is known.`);
@@ -19895,6 +18944,11 @@ var MetaBase = class extends QueryBase {
19895
18944
  });
19896
18945
  }
19897
18946
  };
18947
+ function isEmpty(value) {
18948
+ if (value === null || value === void 0) return true;
18949
+ if (Array.isArray(value)) return value.length === 0;
18950
+ return typeof value === "object" && Object.keys(value).length === 0;
18951
+ }
19898
18952
 
19899
18953
  // src/resources/base/mutations.ts
19900
18954
  var MutationBase = class extends MetaBase {
@@ -20400,7 +19454,7 @@ var MutationBase = class extends MetaBase {
20400
19454
  }
20401
19455
  }
20402
19456
  if (isRecord2(data)) {
20403
- const eff = effectiveRequiredFields(void 0, _nullishCoalesce(_optionalChain([RELATIONSHIP_MAP, 'access', _193 => _193[this.config.key], 'optionalAccess', _194 => _194.zoneFacts]), () => ( null)));
19457
+ const eff = effectiveRequiredFields(void 0, _nullishCoalesce(_optionalChain([RELATIONSHIP_MAP, 'access', _172 => _172[this.config.key], 'optionalAccess', _173 => _173.zoneFacts]), () => ( null)));
20404
19458
  const missing = eff.fields.filter((f) => f !== this.config.parentField && data[f] === void 0);
20405
19459
  if (missing.length > 0) {
20406
19460
  warnings.push(`required field(s) not in the data: ${missing.join(", ")} (${eff.from}); the vendor would likely reject the create`);
@@ -20440,7 +19494,7 @@ var MutationBase = class extends MetaBase {
20440
19494
  current = parentId === void 0 ? await this.getGlobal(id, true) : this.config.attachmentGet ? await this.getScopedModel(parentId, id, true) : await this.getScopedRaw(parentId, id, true);
20441
19495
  } catch (err) {
20442
19496
  if (err instanceof NotFoundError) {
20443
- const updateReason = _optionalChain([EMPTY_REASON_STAMPS, 'access', _195 => _195[this.config.key], 'optionalAccess', _196 => _196.update]);
19497
+ const updateReason = _optionalChain([EMPTY_REASON_STAMPS, 'access', _174 => _174[this.config.key], 'optionalAccess', _175 => _175.update]);
20444
19498
  checks.push({
20445
19499
  name: "current-record-exists",
20446
19500
  ok: false,
@@ -20543,6 +19597,7 @@ function isVendorId(value) {
20543
19597
  if (typeof value === "string") return /^\d+$/.test(value);
20544
19598
  return false;
20545
19599
  }
19600
+ var zeroIsRecord = (table) => table === "company";
20546
19601
  function createNamePlan(labelFields = [], labelResource) {
20547
19602
  const resourceIds = [];
20548
19603
  const resSeen = /* @__PURE__ */ new Set();
@@ -20570,7 +19625,7 @@ function createNamePlan(labelFields = [], labelResource) {
20570
19625
  }
20571
19626
  },
20572
19627
  addFk: (table, value) => {
20573
- if (!isVendorId(value) || Number(value) === 0) return;
19628
+ if (!isVendorId(value) || Number(value) === 0 && !zeroIsRecord(table)) return;
20574
19629
  const k = `${table}:${String(value)}`;
20575
19630
  if (!fkSeen.has(k)) {
20576
19631
  fkSeen.add(k);
@@ -20713,15 +19768,14 @@ async function resolveCompositeNames(http, plan, opts = {}) {
20713
19768
  throw new AutotaskConfigError("composite names: the transport is not wired to an AutotaskClient (the composite must run through the client)");
20714
19769
  }
20715
19770
  const readCache = http.readCache;
20716
- const nameCache = http.resourceNameCache;
20717
19771
  const section = { truncated: false, rows: {}, reasons: {} };
20718
19772
  let remaining = budget;
20719
19773
  let contractLobPointer;
20720
19774
  let contractLobUnread;
20721
19775
  const coldResource = [];
20722
19776
  for (const id of plan.resourceIds) {
20723
- if (nameCache !== void 0 && nameCache.get(id) !== void 0) continue;
20724
- if (readCache !== void 0 && readCache.get(`/V1.0/Resources/${id}`) !== void 0) continue;
19777
+ const path = `/V1.0/Resources/${id}`;
19778
+ if (readCache !== void 0 && (readCache.peek(path).hit || readCache.peek(contextAbsentKey(path)).hit)) continue;
20725
19779
  coldResource.push(id);
20726
19780
  }
20727
19781
  const withinBudget = coldResource.slice(0, remaining);
@@ -20799,11 +19853,11 @@ async function resolveCompositeNames(http, plan, opts = {}) {
20799
19853
  const row = catalog.rows.find((r) => r.resource === labelResource);
20800
19854
  if (row === void 0 || row.error !== null || row.fields === void 0) {
20801
19855
  usable = false;
20802
- failure = _nullishCoalesce(_optionalChain([row, 'optionalAccess', _197 => _197.error]), () => ( { code: "INVALID_RESPONSE", message: `fieldCatalog: no ${labelResource} row` }));
19856
+ failure = _nullishCoalesce(_optionalChain([row, 'optionalAccess', _176 => _176.error]), () => ( { code: "INVALID_RESPONSE", message: `fieldCatalog: no ${labelResource} row` }));
20803
19857
  } else {
20804
19858
  for (const f of plan.labelFields) {
20805
19859
  const frow = row.fields.find((x) => x.name === f);
20806
- picklists[f] = _optionalChain([frow, 'optionalAccess', _198 => _198.picklistValues]);
19860
+ picklists[f] = _optionalChain([frow, 'optionalAccess', _177 => _177.picklistValues]);
20807
19861
  }
20808
19862
  }
20809
19863
  } catch (err) {
@@ -20842,8 +19896,8 @@ async function resolveCompositeNames(http, plan, opts = {}) {
20842
19896
  return {
20843
19897
  section,
20844
19898
  lineOfBusiness: lineOfBusiness2,
20845
- resourceName: (id) => _nullishCoalesce(_optionalChain([section, 'access', _199 => _199.rows, 'access', _200 => _200[`resource:${id}`], 'optionalAccess', _201 => _201.name]), () => ( null)),
20846
- recordName: (table, id) => _nullishCoalesce(_optionalChain([section, 'access', _202 => _202.rows, 'access', _203 => _203[`${table}:${id}`], 'optionalAccess', _204 => _204.name]), () => ( null)),
19899
+ resourceName: (id) => _nullishCoalesce(_optionalChain([section, 'access', _178 => _178.rows, 'access', _179 => _179[`resource:${id}`], 'optionalAccess', _180 => _180.name]), () => ( null)),
19900
+ recordName: (table, id) => _nullishCoalesce(_optionalChain([section, 'access', _181 => _181.rows, 'access', _182 => _182[`${table}:${id}`], 'optionalAccess', _183 => _183.name]), () => ( null)),
20847
19901
  labelFor: (field, value) => {
20848
19902
  if (value === null || value === void 0) return null;
20849
19903
  const values = picklists[field];
@@ -20896,11 +19950,11 @@ async function labelRows(client, resourceKey, rows, resourceKeyedReasons = false
20896
19950
  const row = catalog.rows.find((r) => r.resource === resourceKey);
20897
19951
  const known = _nullishCoalesce(lastPicklists.get(client), () => ( /* @__PURE__ */ new Map()));
20898
19952
  lastPicklists.set(client, known);
20899
- if (_optionalChain([row, 'optionalAccess', _205 => _205.fields]) === void 0) {
19953
+ if (_optionalChain([row, 'optionalAccess', _184 => _184.fields]) === void 0) {
20900
19954
  const last = known.get(resourceKey);
20901
19955
  unavailable = (_nullishCoalesce(last, () => ( []))).filter((f) => candidates.includes(f));
20902
19956
  if (last === void 0 || unavailable.length > 0) {
20903
- catalogError = _nullishCoalesce(_optionalChain([row, 'optionalAccess', _206 => _206.error]), () => ( { code: "INVALID_RESPONSE", message: `fieldCatalog: no ${resourceKey} row` }));
19957
+ catalogError = _nullishCoalesce(_optionalChain([row, 'optionalAccess', _185 => _185.error]), () => ( { code: "INVALID_RESPONSE", message: `fieldCatalog: no ${resourceKey} row` }));
20904
19958
  }
20905
19959
  } else {
20906
19960
  const all = row.fields.filter((f) => f.isPickList).map((f) => f.name);
@@ -20915,15 +19969,15 @@ async function labelRows(client, resourceKey, rows, resourceKeyedReasons = false
20915
19969
  const builder = createNamePlan(picklistFields, resourceKey);
20916
19970
  for (const row of rows) {
20917
19971
  for (const [field, value] of Object.entries(row)) {
20918
- if (value === 0 || value === "0") continue;
20919
19972
  const table = ROW_FK_TABLES[field];
19973
+ if ((value === 0 || value === "0") && !zeroIsRecord(table)) continue;
20920
19974
  if (table !== void 0) builder.addFk(table, value);
20921
19975
  else if (isResourceFkField(field)) builder.addResource(value);
20922
19976
  }
20923
19977
  }
20924
19978
  const plan = builder.plan();
20925
19979
  const names = plan.resourceIds.length === 0 && plan.recordFks.length === 0 && picklistFields.length === 0 ? void 0 : await resolveCompositeNames(client.http, plan);
20926
- const secondReadFailed = _optionalChain([names, 'optionalAccess', _207 => _207.section, 'access', _208 => _208.catalogError]);
19980
+ const secondReadFailed = _optionalChain([names, 'optionalAccess', _186 => _186.section, 'access', _187 => _187.catalogError]);
20927
19981
  if (secondReadFailed !== void 0) {
20928
19982
  catalogError ??= secondReadFailed;
20929
19983
  unavailable = [.../* @__PURE__ */ new Set([...unavailable, ...picklistFields])];
@@ -20934,8 +19988,8 @@ async function labelRows(client, resourceKey, rows, resourceKeyedReasons = false
20934
19988
  const has2 = value !== null && value !== void 0 && names !== void 0;
20935
19989
  if (picklistFields.includes(field)) r[`${field}Label`] = has2 ? names.labelFor(field, value) : null;
20936
19990
  else if (unavailable.includes(field)) r[`${field}Label`] = null;
20937
- const set = has2 && value !== 0 && value !== "0";
20938
19991
  const table = ROW_FK_TABLES[field];
19992
+ const set = has2 && (zeroIsRecord(table) || value !== 0 && value !== "0");
20939
19993
  if (table !== void 0) r[`${field}Name`] = set && isVendorId(value) ? untrustedName(names.recordName(table, value)) : null;
20940
19994
  else if (isResourceFkField(field)) r[`${field}Name`] = set && isVendorId(value) ? untrustedName(names.resourceName(value)) : null;
20941
19995
  }
@@ -20943,8 +19997,8 @@ async function labelRows(client, resourceKey, rows, resourceKeyedReasons = false
20943
19997
  return r;
20944
19998
  });
20945
19999
  const capped = names === void 0 ? 0 : Object.values(names.section.rows).filter((row) => row.capped).length;
20946
- const unresolved = Object.keys(_nullishCoalesce(_optionalChain([names, 'optionalAccess', _209 => _209.section, 'access', _210 => _210.reasons]), () => ( {})));
20947
- const reasons = { ..._optionalChain([names, 'optionalAccess', _211 => _211.section, 'access', _212 => _212.reasons]) };
20000
+ const unresolved = Object.keys(_nullishCoalesce(_optionalChain([names, 'optionalAccess', _188 => _188.section, 'access', _189 => _189.reasons]), () => ( {})));
20001
+ const reasons = { ..._optionalChain([names, 'optionalAccess', _190 => _190.section, 'access', _191 => _191.reasons]) };
20948
20002
  for (const f of unavailable) reasons[f] = PICKLIST_CATALOG_UNAVAILABLE_NOTE;
20949
20003
  if (capped === 0 && unresolved.length === 0 && catalogError === void 0) return { rows: out };
20950
20004
  const parts = [];
@@ -21023,7 +20077,7 @@ var BillingCodesResource = class extends BaseResource {
21023
20077
  }
21024
20078
  /** Get one record by id (item envelope; item: null → NotFoundError). `fresh: true` bypasses the read-cache lookup and refreshes the entry (issue #250). */
21025
20079
  async get(id, opts = {}) {
21026
- return this.getGlobal(id, _optionalChain([opts, 'optionalAccess', _213 => _213.fresh]) === true);
20080
+ return this.getGlobal(id, _optionalChain([opts, 'optionalAccess', _192 => _192.fresh]) === true);
21027
20081
  }
21028
20082
  /** Resolve a BillingCode from a numeric id (or, where the vendor indexes a primary text field,
21029
20083
  * from an exact match on name). A numeric id miss throws NOT_FOUND; a complete
@@ -21038,7 +20092,7 @@ var BillingCodesResource = class extends BaseResource {
21038
20092
  /** Search BillingCode records: bounded contains on name (no vendor free-text search exists);
21039
20093
  * limit defaults to 25, max 500. */
21040
20094
  async search(query, opts) {
21041
- return this.searchBase(query, { limit: _optionalChain([opts, 'optionalAccess', _214 => _214.limit]) });
20095
+ return this.searchBase(query, { limit: _optionalChain([opts, 'optionalAccess', _193 => _193.limit]) });
21042
20096
  }
21043
20097
  };
21044
20098
 
@@ -21129,7 +20183,7 @@ function validateZone(tz, opName = "dateWindow") {
21129
20183
  }
21130
20184
  try {
21131
20185
  new Intl.DateTimeFormat("en-US", { timeZone: tz });
21132
- } catch (e18) {
20186
+ } catch (e17) {
21133
20187
  throw new AutotaskConfigError(
21134
20188
  `${opName}: tz is not a valid IANA timezone (${JSON.stringify(tz)}; e.g. 'Australia/Sydney' \u2014 omitted tz = UTC)`,
21135
20189
  { operation: opName }
@@ -21342,28 +20396,28 @@ async function customRangeQuery(http, path, field, from, to, limit, operation, z
21342
20396
 
21343
20397
  // src/resources/special/billing_items.ts
21344
20398
  async function windowQuery(_resource, kind, http, opts) {
21345
- const now = _nullishCoalesce(_optionalChain([opts, 'optionalAccess', _215 => _215.now]), () => ( /* @__PURE__ */ new Date()));
21346
- const tz = _optionalChain([opts, 'optionalAccess', _216 => _216.tz]) === void 0 ? void 0 : validateZone(opts.tz, `billing_items.${kind}`);
20399
+ const now = _nullishCoalesce(_optionalChain([opts, 'optionalAccess', _194 => _194.now]), () => ( /* @__PURE__ */ new Date()));
20400
+ const tz = _optionalChain([opts, 'optionalAccess', _195 => _195.tz]) === void 0 ? void 0 : validateZone(opts.tz, `billing_items.${kind}`);
21347
20401
  return dateWindowQuery(
21348
20402
  http,
21349
20403
  "/V1.0/BillingItems/query",
21350
20404
  "itemDate",
21351
20405
  kind,
21352
- clampWindowLimit(_optionalChain([opts, 'optionalAccess', _217 => _217.limit])),
20406
+ clampWindowLimit(_optionalChain([opts, 'optionalAccess', _196 => _196.limit])),
21353
20407
  `billing_items.${kind}`,
21354
20408
  now,
21355
20409
  tz
21356
20410
  );
21357
20411
  }
21358
20412
  async function customRange(_resource, from, to, http, opts) {
21359
- const zone = _optionalChain([opts, 'optionalAccess', _218 => _218.tz]) === void 0 ? void 0 : validateZone(opts.tz, "billing_items.customRange");
20413
+ const zone = _optionalChain([opts, 'optionalAccess', _197 => _197.tz]) === void 0 ? void 0 : validateZone(opts.tz, "billing_items.customRange");
21360
20414
  return customRangeQuery(
21361
20415
  http,
21362
20416
  "/V1.0/BillingItems/query",
21363
20417
  "itemDate",
21364
20418
  from,
21365
20419
  to,
21366
- clampWindowLimit(_optionalChain([opts, 'optionalAccess', _219 => _219.limit])),
20420
+ clampWindowLimit(_optionalChain([opts, 'optionalAccess', _198 => _198.limit])),
21367
20421
  "billing_items.customRange",
21368
20422
  zone
21369
20423
  );
@@ -21395,7 +20449,7 @@ var BillingItemsResource = class extends BaseResource {
21395
20449
  }
21396
20450
  /** Get one record by id (item envelope; item: null → NotFoundError). `fresh: true` bypasses the read-cache lookup and refreshes the entry (issue #250). */
21397
20451
  async get(id, opts = {}) {
21398
- return this.getGlobal(id, _optionalChain([opts, 'optionalAccess', _220 => _220.fresh]) === true);
20452
+ return this.getGlobal(id, _optionalChain([opts, 'optionalAccess', _199 => _199.fresh]) === true);
21399
20453
  }
21400
20454
  /** Update a record (PUT on the COLLECTION — the vendored spec's item path is GET-only; the vendor
21401
20455
  * keys the record by the id in the body). The vendor validates the FULL model on this route (V2), so a
@@ -21587,9 +20641,9 @@ async function searchByDomain(_resource, domain, opts = {}, http) {
21587
20641
  return fetchCompanies(http, ids2, limit);
21588
20642
  }
21589
20643
  async function searchByIdentity(_resource, identity, opts = {}, http) {
21590
- const name = _nullishCoalesce(_optionalChain([identity, 'access', _221 => _221.companyName, 'optionalAccess', _222 => _222.trim, 'call', _223 => _223()]), () => ( ""));
21591
- const email = _nullishCoalesce(_optionalChain([identity, 'access', _224 => _224.email, 'optionalAccess', _225 => _225.trim, 'call', _226 => _226()]), () => ( ""));
21592
- const website = _nullishCoalesce(_optionalChain([identity, 'access', _227 => _227.website, 'optionalAccess', _228 => _228.trim, 'call', _229 => _229()]), () => ( ""));
20644
+ const name = _nullishCoalesce(_optionalChain([identity, 'access', _200 => _200.companyName, 'optionalAccess', _201 => _201.trim, 'call', _202 => _202()]), () => ( ""));
20645
+ const email = _nullishCoalesce(_optionalChain([identity, 'access', _203 => _203.email, 'optionalAccess', _204 => _204.trim, 'call', _205 => _205()]), () => ( ""));
20646
+ const website = _nullishCoalesce(_optionalChain([identity, 'access', _206 => _206.website, 'optionalAccess', _207 => _207.trim, 'call', _208 => _208()]), () => ( ""));
21593
20647
  if (name === "" && email === "" && website === "") {
21594
20648
  throw new AutotaskConfigError("searchByIdentity: at least one of companyName / email / website is required");
21595
20649
  }
@@ -21728,7 +20782,7 @@ function validateUpsertInput(companyName, params) {
21728
20782
  if (typeof companyName !== "string" || companyName.trim() === "") {
21729
20783
  throw new AutotaskConfigError("upsertByName: companyName is required (a non-empty string)", { operation: OP_UPSERT_NAME });
21730
20784
  }
21731
- if (typeof _optionalChain([params, 'optionalAccess', _230 => _230.phone]) !== "string" || params.phone.trim() === "") {
20785
+ if (typeof _optionalChain([params, 'optionalAccess', _209 => _209.phone]) !== "string" || params.phone.trim() === "") {
21732
20786
  throw new AutotaskConfigError("upsertByName: params.phone is required (a non-empty string \u2014 the ww29 live-required set is { companyName, companyType, phone, ownerResourceID })", { operation: OP_UPSERT_NAME });
21733
20787
  }
21734
20788
  const companyType = _nullishCoalesce(params.companyType, () => ( DEFAULT_COMPANY_TYPE));
@@ -21953,7 +21007,7 @@ function readIncludeNames(record, includeFields) {
21953
21007
  function resolveAuthority(record, live) {
21954
21008
  if (live !== null) return { known: live, authority: "the tenant's live entity field metadata" };
21955
21009
  const vendored = MODEL_FIELD_SETS[record.resource];
21956
- const liveOnlyExempt = Array.isArray(_optionalChain([record, 'access', _231 => _231.metadata, 'optionalAccess', _232 => _232.liveOnlyFields])) ? record.metadata.liveOnlyFields : [];
21010
+ const liveOnlyExempt = Array.isArray(_optionalChain([record, 'access', _210 => _210.metadata, 'optionalAccess', _211 => _211.liveOnlyFields])) ? record.metadata.liveOnlyFields : [];
21957
21011
  if (vendored === void 0) return void 0;
21958
21012
  return {
21959
21013
  known: [...vendored, ...liveOnlyExempt],
@@ -22009,11 +21063,11 @@ var CLOSED_STATUS_LABELS = ["complete", "completed", "closed", "cancelled", "can
22009
21063
  var OP = "tickets.statusSemantics";
22010
21064
  function picklistOf(fields) {
22011
21065
  if (!Array.isArray(fields)) return [];
22012
- const status = fields.find((f) => typeof _optionalChain([f, 'optionalAccess', _233 => _233.name]) === "string" && f.name.toLowerCase() === "status");
21066
+ const status = fields.find((f) => typeof _optionalChain([f, 'optionalAccess', _212 => _212.name]) === "string" && f.name.toLowerCase() === "status");
22013
21067
  if (status === void 0 || status.isPickList !== true || !Array.isArray(status.picklistValues)) return [];
22014
21068
  const out = [];
22015
21069
  for (const v of status.picklistValues) {
22016
- const id = Number(_optionalChain([v, 'optionalAccess', _234 => _234.value]));
21070
+ const id = Number(_optionalChain([v, 'optionalAccess', _213 => _213.value]));
22017
21071
  if (!Number.isSafeInteger(id)) continue;
22018
21072
  out.push({ id, label: typeof v.label === "string" ? v.label : "" });
22019
21073
  }
@@ -22021,19 +21075,19 @@ function picklistOf(fields) {
22021
21075
  }
22022
21076
  async function readFields(http, fresh) {
22023
21077
  const cache = http.fieldCache;
22024
- const key = "tickets";
21078
+ const key = fieldKey("tickets", "fields");
22025
21079
  if (!fresh && cache !== void 0) {
22026
- const shared = cache.inflight(key, "fields");
21080
+ const shared = cache.inflight(key);
22027
21081
  if (shared !== void 0) return await shared;
22028
- const hit = cache.get(key, "fields");
21082
+ const hit = cache.get(key);
22029
21083
  if (hit !== void 0) return hit;
22030
21084
  }
22031
21085
  const res = await http.request({ method: "GET", path: "/V1.0/Tickets/entityInformation/fields", operation: OP, maxRetries: 0 });
22032
- const fields = _optionalChain([res, 'optionalAccess', _235 => _235.fields]);
21086
+ const fields = _optionalChain([res, 'optionalAccess', _214 => _214.fields]);
22033
21087
  if (!Array.isArray(fields)) {
22034
21088
  throw new AutotaskError("Expected the FieldInformationResultModel envelope { fields: [] }; got a different shape", { code: "INVALID_RESPONSE", category: "server" });
22035
21089
  }
22036
- _optionalChain([cache, 'optionalAccess', _236 => _236.set, 'call', _237 => _237(key, "fields", fields)]);
21090
+ if (fields.length > 0) _optionalChain([cache, 'optionalAccess', _215 => _215.set, 'call', _216 => _216(key, fields)]);
22037
21091
  return fields;
22038
21092
  }
22039
21093
  function assumed(allowAssumed, operation) {
@@ -22185,7 +21239,7 @@ async function readOpenTickets(http, companyID, ticketLimit, includeFields, stat
22185
21239
  closedStatus
22186
21240
  };
22187
21241
  if (openRows.length === 0) {
22188
- const stamp = blindStampFor(_optionalChain([BLIND_SURFACE_STAMPS, 'access', _238 => _238["tickets"], 'optionalAccess', _239 => _239.query]), [{ field: "companyID", op: "eq", value: companyID }, openLeaf]);
21242
+ const stamp = blindStampFor(_optionalChain([BLIND_SURFACE_STAMPS, 'access', _217 => _217["tickets"], 'optionalAccess', _218 => _218.query]), [{ field: "companyID", op: "eq", value: companyID }, openLeaf]);
22189
21243
  if (stamp !== void 0) {
22190
21244
  openTickets = {
22191
21245
  items: openRows,
@@ -22355,7 +21409,7 @@ async function prepareCrmWrite(resource, input, http) {
22355
21409
  const rows = await readRows(http, "/V1.0/CompanyLocations/query", [{ field: "companyID", op: "eq", value: companyID }]);
22356
21410
  const items = rows.map((r) => ({ id: r.id, name: str(r.name), isPrimary: bool(r.isPrimary), isActive: bool(r.isActive), city: str(r.city), state: str(r.state), postalCode: str(r.postalCode) }));
22357
21411
  const active = items.filter((l) => l.isActive !== false);
22358
- const m = active.filter((l) => has(ct.city) && _optionalChain([l, 'access', _240 => _240.city, 'optionalAccess', _241 => _241.toLowerCase, 'call', _242 => _242()]) === String(ct.city).toLowerCase() || has(ct.postalCode) && l.postalCode === String(ct.postalCode));
21412
+ const m = active.filter((l) => has(ct.city) && _optionalChain([l, 'access', _219 => _219.city, 'optionalAccess', _220 => _220.toLowerCase, 'call', _221 => _221()]) === String(ct.city).toLowerCase() || has(ct.postalCode) && l.postalCode === String(ct.postalCode));
22359
21413
  const pick = has(ct.companyLocationID) ? { rule: "matched", locationID: ct.companyLocationID } : active.length === 1 ? { rule: "single", locationID: active[0].id } : active.length > 1 && m.length === 1 ? { rule: "matched", locationID: m[0].id } : active.length > 1 ? { rule: "ask", locationID: null } : { rule: "none", locationID: null };
22360
21414
  locations = { items, pick };
22361
21415
  if (input.intent === "add_contact" && pick.locationID === null) {
@@ -22390,7 +21444,7 @@ async function prepareCrmWrite(resource, input, http) {
22390
21444
  }
22391
21445
  }
22392
21446
  if (input.intent === "create_company" && status !== "matched") {
22393
- const eff = effectiveRequiredFields(null, _optionalChain([RELATIONSHIP_MAP, 'access', _243 => _243.companies, 'optionalAccess', _244 => _244.zoneFacts]));
21447
+ const eff = effectiveRequiredFields(null, _optionalChain([RELATIONSHIP_MAP, 'access', _222 => _222.companies, 'optionalAccess', _223 => _223.zoneFacts]));
22394
21448
  const research = (f) => ["companyType", "ownerResourceID", "countryID"].includes(f) ? "lookup" : "web_if_available_else_ask";
22395
21449
  const lookups = {
22396
21450
  companyType: { via: "operations.fieldCatalog", resource: "companies", field: "companyType", labels: true, note: "map the user wording to one returned label; never hardcode ids" },
@@ -22412,16 +21466,16 @@ async function prepareCrmWrite(resource, input, http) {
22412
21466
  }
22413
21467
  let next = null;
22414
21468
  const isInactive = (d) => d.isActive === false || d.isActive === 0;
22415
- const dups = _nullishCoalesce(_optionalChain([contacts, 'optionalAccess', _245 => _245.duplicates]), () => ( []));
21469
+ const dups = _nullishCoalesce(_optionalChain([contacts, 'optionalAccess', _224 => _224.duplicates]), () => ( []));
22416
21470
  const sameCompany = companyID === null ? dups : dups.filter((d) => d.companyID === companyID);
22417
21471
  const elsewhere = companyID === null ? [] : dups.filter((d) => d.companyID !== companyID && !isInactive(d));
22418
21472
  if (input.intent === "add_contact" && sameCompany.length === 0 && elsewhere.length > 0) {
22419
- needsInput.push({ field: "contact", why: `a contact with this ${_nullishCoalesce(_optionalChain([contacts, 'optionalAccess', _246 => _246.matchedBy]), () => ( "identity"))} is active at another company: update it, move it with contacts.moveToCompany, or confirm a second record`, source: "policy", lookup: { duplicates: elsewhere }, research: "ask" });
21473
+ needsInput.push({ field: "contact", why: `a contact with this ${_nullishCoalesce(_optionalChain([contacts, 'optionalAccess', _225 => _225.matchedBy]), () => ( "identity"))} is active at another company: update it, move it with contacts.moveToCompany, or confirm a second record`, source: "policy", lookup: { duplicates: elsewhere }, research: "ask" });
22420
21474
  }
22421
21475
  if (needsInput.length === 0) {
22422
21476
  const dup = sameCompany[0];
22423
21477
  if (input.intent === "create_company") next = status === "matched" ? { operation: "companies.update", note: "the company already exists: update it instead of creating" } : { operation: "companies.create", note: 'then re-run with intent add_contact: the vendor auto-creates a "Primary Address" location (rename it with company_locations.update; locations cannot be deleted via the API)' };
22424
- else if (input.intent === "add_contact") next = dup ? { operation: "contacts.update", note: `a contact already matches by ${_nullishCoalesce(_optionalChain([contacts, 'optionalAccess', _247 => _247.matchedBy]), () => ( "id"))} (id ${String(dup.id)}): update it, or move it with contacts.moveToCompany if it belongs to another company` } : { operation: "contacts.create", note: `set companyID ${String(companyID)} and companyLocationID ${String(_optionalChain([locations, 'optionalAccess', _248 => _248.pick, 'access', _249 => _249.locationID]))}` };
21478
+ else if (input.intent === "add_contact") next = dup ? { operation: "contacts.update", note: `a contact already matches by ${_nullishCoalesce(_optionalChain([contacts, 'optionalAccess', _226 => _226.matchedBy]), () => ( "id"))} (id ${String(dup.id)}): update it, or move it with contacts.moveToCompany if it belongs to another company` } : { operation: "contacts.create", note: `set companyID ${String(companyID)} and companyLocationID ${String(_optionalChain([locations, 'optionalAccess', _227 => _227.pick, 'access', _228 => _228.locationID]))}` };
22425
21479
  else if (input.intent === "update_contact") next = { operation: "contacts.update", note: "send only the changed fields" };
22426
21480
  else next = { operation: "companies.update", note: "send only the changed fields" };
22427
21481
  }
@@ -22541,7 +21595,7 @@ function validateUnbilledWorkOptions(companyId, opts) {
22541
21595
  var OP3 = "companies.unbilledWork";
22542
21596
  function unbilledWorkPartStatus(key, conditions, items) {
22543
21597
  if (items.length !== 0) return { status: "ok" };
22544
- const stamp = blindStampFor(_optionalChain([BLIND_SURFACE_STAMPS, 'access', _250 => _250[key], 'optionalAccess', _251 => _251.query]), conditions);
21598
+ const stamp = blindStampFor(_optionalChain([BLIND_SURFACE_STAMPS, 'access', _229 => _229[key], 'optionalAccess', _230 => _230.query]), conditions);
22545
21599
  if (stamp !== void 0) return { status: "blind", blindSurface: stamp, note: stamp.note };
22546
21600
  return {
22547
21601
  status: "ok",
@@ -22791,7 +21845,7 @@ var CompaniesResource = class extends BaseResource {
22791
21845
  }
22792
21846
  /** Get one record by id (item envelope; item: null → NotFoundError). `fresh: true` bypasses the read-cache lookup and refreshes the entry (issue #250). */
22793
21847
  async get(id, opts = {}) {
22794
- return this.getGlobal(id, _optionalChain([opts, 'optionalAccess', _252 => _252.fresh]) === true);
21848
+ return this.getGlobal(id, _optionalChain([opts, 'optionalAccess', _231 => _231.fresh]) === true);
22795
21849
  }
22796
21850
  /** Create a record (OperationResultModel → { id}). `dryRun: true` returns the
22797
21851
  * pinned envelope (section 19.3) and never issues the create. A 2xx that is not the
@@ -22929,7 +21983,7 @@ function validateFetchManyItems(items, cap, opName = "fetchMany", capRef = `FETC
22929
21983
  }
22930
21984
  try {
22931
21985
  parseIdentifier(item.id, `${at}.id`);
22932
- } catch (e19) {
21986
+ } catch (e18) {
22933
21987
  if (typeof item.id === "string") {
22934
21988
  problems.push(
22935
21989
  `${at}.id: "${item.id}" is not a vendor numeric id \u2014 non-numeric string identifiers are operations.resolveAny territory (the resource's findBy* helpers)`
@@ -22948,8 +22002,8 @@ function validateFetchManyItems(items, cap, opName = "fetchMany", capRef = `FETC
22948
22002
  problems.push(`${at}.fields: must be an array of exact vendor field names (discover them via operations.fieldCatalog)`);
22949
22003
  continue;
22950
22004
  }
22951
- if (_optionalChain([validation, 'optionalAccess', _253 => _253.fieldCheck]) !== false) {
22952
- const authoritative = _nullishCoalesce(_optionalChain([validation, 'optionalAccess', _254 => _254.fieldSetsByResource, 'optionalAccess', _255 => _255.get, 'call', _256 => _256(resourceKey)]), () => ( fields));
22005
+ if (_optionalChain([validation, 'optionalAccess', _232 => _232.fieldCheck]) !== false) {
22006
+ const authoritative = _nullishCoalesce(_optionalChain([validation, 'optionalAccess', _233 => _233.fieldSetsByResource, 'optionalAccess', _234 => _234.get, 'call', _235 => _235(resourceKey)]), () => ( fields));
22953
22007
  for (const f of item.fields) {
22954
22008
  if (!authoritative.includes(f)) {
22955
22009
  problems.push(
@@ -23055,7 +22109,7 @@ function project3(value, fields) {
23055
22109
  return out;
23056
22110
  }
23057
22111
  function resourcePathOf(key, holder) {
23058
- const path = _optionalChain([holder, 'optionalAccess', _257 => _257.resourcePath]);
22112
+ const path = _optionalChain([holder, 'optionalAccess', _236 => _236.resourcePath]);
23059
22113
  if (typeof path !== "string" || path === "") {
23060
22114
  throw new AutotaskConfigError(`fetchByIds: the client exposes no ${key} resource path (registry drift)`, {
23061
22115
  suggestedAction: "Rebuild the client from the SDK build that ships this registry (the resource set is pinned by the plan)."
@@ -23668,7 +22722,7 @@ var stampPart = (key, raw, conditions, extraNotes) => {
23668
22722
  status: st.status,
23669
22723
  blindSurface: st.blindSurface,
23670
22724
  note: [st.note, ..._nullishCoalesce(extraNotes, () => ( []))].filter(Boolean).join(" | ") || void 0,
23671
- emptyReason: raw.items.length === 0 && st.status === "ok" ? _optionalChain([EMPTY_REASON_STAMPS, 'access', _258 => _258[key], 'optionalAccess', _259 => _259.query]) : void 0
22725
+ emptyReason: raw.items.length === 0 && st.status === "ok" ? _optionalChain([EMPTY_REASON_STAMPS, 'access', _237 => _237[key], 'optionalAccess', _238 => _238.query]) : void 0
23672
22726
  };
23673
22727
  };
23674
22728
  function appendWiresNote(wires, note) {
@@ -23733,7 +22787,7 @@ async function resolveContractType(ctx, contractType) {
23733
22787
  if (contractType > 0) {
23734
22788
  ctx.wires.issued += 1;
23735
22789
  try {
23736
- contractTypeLabel = await _asyncNullishCoalesce(await _asyncOptionalChain([(await readPicklists(ctx.http, "/V1.0/Contracts", ctx.OP)), 'access', async _260 => _260.get, 'call', async _261 => _261("contractType"), 'optionalAccess', async _262 => _262.get, 'call', async _263 => _263(contractType)]), async () => ( null));
22790
+ contractTypeLabel = await _asyncNullishCoalesce(await _asyncOptionalChain([(await readPicklists(ctx.http, "/V1.0/Contracts", ctx.OP)), 'access', async _239 => _239.get, 'call', async _240 => _240("contractType"), 'optionalAccess', async _241 => _241.get, 'call', async _242 => _242(contractType)]), async () => ( null));
23737
22791
  } catch (err) {
23738
22792
  if (!(err instanceof AutotaskError)) throw err;
23739
22793
  }
@@ -23744,7 +22798,7 @@ async function resolveContractType(ctx, contractType) {
23744
22798
  return { contractTypeLabel, contractTypeKnown, isBlockHours, isTimeAndMaterials };
23745
22799
  }
23746
22800
  async function fetchRoot(ctx, resource) {
23747
- const contractCached = _optionalChain([ctx, 'access', _264 => _264.rawHttp, 'access', _265 => _265.readCache, 'optionalAccess', _266 => _266.get, 'call', _267 => _267(`/V1.0/Contracts/${ctx.id}`)]) !== void 0;
22801
+ const contractCached = _optionalChain([ctx, 'access', _243 => _243.rawHttp, 'access', _244 => _244.readCache, 'optionalAccess', _245 => _245.get, 'call', _246 => _246(`/V1.0/Contracts/${ctx.id}`)]) !== void 0;
23748
22802
  const contract = await resource.get(ctx.id);
23749
22803
  ctx.wires.issued += 1;
23750
22804
  if (!contractCached) ctx.wires.requests += 1;
@@ -23959,10 +23013,10 @@ async function fetchSubscriptions(ctx, cis) {
23959
23013
  const labels = await readSubscriptionLabels(ctx, subscriptions);
23960
23014
  for (const x of subscriptions) {
23961
23015
  const sid = num(x["id"]);
23962
- const periodLabel = _optionalChain([labels, 'optionalAccess', _268 => _268.get, 'call', _269 => _269("periodType"), 'optionalAccess', _270 => _270.get, 'call', _271 => _271(num(x["periodType"]))]);
23016
+ const periodLabel = _optionalChain([labels, 'optionalAccess', _247 => _247.get, 'call', _248 => _248("periodType"), 'optionalAccess', _249 => _249.get, 'call', _250 => _250(num(x["periodType"]))]);
23963
23017
  subFactor.set(sid, periodMonthlyFactor(x["periodType"], periodLabel));
23964
23018
  if (isOneTimePeriod(x["periodType"], periodLabel)) oneTimeSubs.add(sid);
23965
- const statusLabel = _optionalChain([labels, 'optionalAccess', _272 => _272.get, 'call', _273 => _273("status"), 'optionalAccess', _274 => _274.get, 'call', _275 => _275(num(x["status"]))]);
23019
+ const statusLabel = _optionalChain([labels, 'optionalAccess', _251 => _251.get, 'call', _252 => _252("status"), 'optionalAccess', _253 => _253.get, 'call', _254 => _254(num(x["status"]))]);
23966
23020
  const notActive = statusLabel !== void 0 ? NOT_ACTIVE_LABEL_RE.test(statusLabel) : labels === null && SUBSCRIPTION_NOT_ACTIVE_STATUS_IDS.has(num(x["status"]));
23967
23021
  if (notActive) cancelledSubs.add(sid);
23968
23022
  }
@@ -24663,7 +23717,7 @@ var ContractsResource = class extends BaseResource {
24663
23717
  }
24664
23718
  /** Get one record by id (item envelope; item: null → NotFoundError). `fresh: true` bypasses the read-cache lookup and refreshes the entry (issue #250). */
24665
23719
  async get(id, opts = {}) {
24666
- return this.getGlobal(id, _optionalChain([opts, 'optionalAccess', _276 => _276.fresh]) === true);
23720
+ return this.getGlobal(id, _optionalChain([opts, 'optionalAccess', _255 => _255.fresh]) === true);
24667
23721
  }
24668
23722
  /** Create a record (OperationResultModel → { id}). `dryRun: true` returns the
24669
23723
  * pinned envelope (section 19.3) and never issues the create. A 2xx that is not the
@@ -24699,7 +23753,7 @@ var ContractsResource = class extends BaseResource {
24699
23753
  /** Search Contract records: bounded contains on contractName (no vendor free-text search exists);
24700
23754
  * limit defaults to 25, max 500. */
24701
23755
  async search(query, opts) {
24702
- return this.searchBase(query, { limit: _optionalChain([opts, 'optionalAccess', _277 => _277.limit]) });
23756
+ return this.searchBase(query, { limit: _optionalChain([opts, 'optionalAccess', _256 => _256.limit]) });
24703
23757
  }
24704
23758
  /** Node-parity special op (hand-written in src/resources/special/contracts.ts). */
24705
23759
  profitability(id, opts) {
@@ -24739,28 +23793,28 @@ async function pdf(_resource, id, http) {
24739
23793
  return decodeFileData(res);
24740
23794
  }
24741
23795
  async function windowQuery2(_resource, kind, http, opts) {
24742
- const now = _nullishCoalesce(_optionalChain([opts, 'optionalAccess', _278 => _278.now]), () => ( /* @__PURE__ */ new Date()));
24743
- const tz = _optionalChain([opts, 'optionalAccess', _279 => _279.tz]) === void 0 ? void 0 : validateZone(opts.tz, `invoices.${kind}`);
23796
+ const now = _nullishCoalesce(_optionalChain([opts, 'optionalAccess', _257 => _257.now]), () => ( /* @__PURE__ */ new Date()));
23797
+ const tz = _optionalChain([opts, 'optionalAccess', _258 => _258.tz]) === void 0 ? void 0 : validateZone(opts.tz, `invoices.${kind}`);
24744
23798
  return dateWindowQuery(
24745
23799
  http,
24746
23800
  "/V1.0/Invoices/query",
24747
23801
  "invoiceDateTime",
24748
23802
  kind,
24749
- clampWindowLimit(_optionalChain([opts, 'optionalAccess', _280 => _280.limit])),
23803
+ clampWindowLimit(_optionalChain([opts, 'optionalAccess', _259 => _259.limit])),
24750
23804
  `invoices.${kind}`,
24751
23805
  now,
24752
23806
  tz
24753
23807
  );
24754
23808
  }
24755
23809
  async function customRange2(_resource, from, to, http, opts) {
24756
- const zone = _optionalChain([opts, 'optionalAccess', _281 => _281.tz]) === void 0 ? void 0 : validateZone(opts.tz, "invoices.customRange");
23810
+ const zone = _optionalChain([opts, 'optionalAccess', _260 => _260.tz]) === void 0 ? void 0 : validateZone(opts.tz, "invoices.customRange");
24757
23811
  return customRangeQuery(
24758
23812
  http,
24759
23813
  "/V1.0/Invoices/query",
24760
23814
  "invoiceDateTime",
24761
23815
  from,
24762
23816
  to,
24763
- clampWindowLimit(_optionalChain([opts, 'optionalAccess', _282 => _282.limit])),
23817
+ clampWindowLimit(_optionalChain([opts, 'optionalAccess', _261 => _261.limit])),
24764
23818
  "invoices.customRange",
24765
23819
  zone
24766
23820
  );
@@ -24792,7 +23846,7 @@ var InvoicesResource = class extends BaseResource {
24792
23846
  }
24793
23847
  /** Get one record by id (item envelope; item: null → NotFoundError). `fresh: true` bypasses the read-cache lookup and refreshes the entry (issue #250). */
24794
23848
  async get(id, opts = {}) {
24795
- return this.getGlobal(id, _optionalChain([opts, 'optionalAccess', _283 => _283.fresh]) === true);
23849
+ return this.getGlobal(id, _optionalChain([opts, 'optionalAccess', _262 => _262.fresh]) === true);
24796
23850
  }
24797
23851
  /** Update a record (PUT on the COLLECTION — the vendored spec's item path is GET-only; the vendor
24798
23852
  * keys the record by the id in the body). The vendor validates the FULL model on this route (V2), so a
@@ -24906,7 +23960,7 @@ var OrganizationalLevel1sResource = class extends BaseResource {
24906
23960
  }
24907
23961
  /** Get one record by id (item envelope; item: null → NotFoundError). `fresh: true` bypasses the read-cache lookup and refreshes the entry (issue #250). */
24908
23962
  async get(id, opts = {}) {
24909
- return this.getGlobal(id, _optionalChain([opts, 'optionalAccess', _284 => _284.fresh]) === true);
23963
+ return this.getGlobal(id, _optionalChain([opts, 'optionalAccess', _263 => _263.fresh]) === true);
24910
23964
  }
24911
23965
  /** Create a record (OperationResultModel → { id}). `dryRun: true` returns the
24912
23966
  * pinned envelope (section 19.3) and never issues the create. A 2xx that is not the
@@ -24957,7 +24011,7 @@ var OrganizationalLevel2sResource = class extends BaseResource {
24957
24011
  }
24958
24012
  /** Get one record by id (item envelope; item: null → NotFoundError). `fresh: true` bypasses the read-cache lookup and refreshes the entry (issue #250). */
24959
24013
  async get(id, opts = {}) {
24960
- return this.getGlobal(id, _optionalChain([opts, 'optionalAccess', _285 => _285.fresh]) === true);
24014
+ return this.getGlobal(id, _optionalChain([opts, 'optionalAccess', _264 => _264.fresh]) === true);
24961
24015
  }
24962
24016
  /** Create a record (OperationResultModel → { id}). `dryRun: true` returns the
24963
24017
  * pinned envelope (section 19.3) and never issues the create. A 2xx that is not the
@@ -25008,7 +24062,7 @@ var OrganizationalLevelAssociationsResource = class extends BaseResource {
25008
24062
  }
25009
24063
  /** Get one record by id (item envelope; item: null → NotFoundError). `fresh: true` bypasses the read-cache lookup and refreshes the entry (issue #250). */
25010
24064
  async get(id, opts = {}) {
25011
- return this.getGlobal(id, _optionalChain([opts, 'optionalAccess', _286 => _286.fresh]) === true);
24065
+ return this.getGlobal(id, _optionalChain([opts, 'optionalAccess', _265 => _265.fresh]) === true);
25012
24066
  }
25013
24067
  /** Create a record (OperationResultModel → { id}). `dryRun: true` returns the
25014
24068
  * pinned envelope (section 19.3) and never issues the create. A 2xx that is not the
@@ -25080,7 +24134,7 @@ async function transferOwnership(_resource, companyIds, destinationResourceId, h
25080
24134
  if (typeof destination === "number" && destination < 1) {
25081
24135
  throw new AutotaskConfigError("transferOwnership: destinationResourceId must be a positive integer");
25082
24136
  }
25083
- const dryRun = _nullishCoalesce(_optionalChain([opts, 'optionalAccess', _287 => _287.dryRun]), () => ( true));
24137
+ const dryRun = _nullishCoalesce(_optionalChain([opts, 'optionalAccess', _266 => _266.dryRun]), () => ( true));
25084
24138
  const changes = [];
25085
24139
  const applied = [];
25086
24140
  for (const id of ids2) {
@@ -25089,7 +24143,7 @@ async function transferOwnership(_resource, companyIds, destinationResourceId, h
25089
24143
  method: "GET",
25090
24144
  path: `/V1.0/Companies/${id}`,
25091
24145
  operation: "resources.transferOwnership",
25092
- maxRetries: _optionalChain([opts, 'optionalAccess', _288 => _288.maxRetries])
24146
+ maxRetries: _optionalChain([opts, 'optionalAccess', _267 => _267.maxRetries])
25093
24147
  });
25094
24148
  const company = unwrapItem(res, id, void 0, "company");
25095
24149
  const current = typeof company.ownerResourceID === "number" ? company.ownerResourceID : null;
@@ -25100,7 +24154,7 @@ async function transferOwnership(_resource, companyIds, destinationResourceId, h
25100
24154
  path: "/V1.0/Companies",
25101
24155
  body: { id, ownerResourceID: destination },
25102
24156
  operation: "resources.transferOwnership",
25103
- maxRetries: _optionalChain([opts, 'optionalAccess', _289 => _289.maxRetries])
24157
+ maxRetries: _optionalChain([opts, 'optionalAccess', _268 => _268.maxRetries])
25104
24158
  });
25105
24159
  unwrapOperation(update);
25106
24160
  applied.push(id);
@@ -25169,7 +24223,7 @@ var ResourcesResource = class extends BaseResource {
25169
24223
  }
25170
24224
  /** Get one record by id (item envelope; item: null → NotFoundError). `fresh: true` bypasses the read-cache lookup and refreshes the entry (issue #250). */
25171
24225
  async get(id, opts = {}) {
25172
- return this.getGlobal(id, _optionalChain([opts, 'optionalAccess', _290 => _290.fresh]) === true);
24226
+ return this.getGlobal(id, _optionalChain([opts, 'optionalAccess', _269 => _269.fresh]) === true);
25173
24227
  }
25174
24228
  /** Update a record (PUT on the COLLECTION — the vendored spec's item path is GET-only; the vendor
25175
24229
  * keys the record by the id in the body). The vendor validates the FULL model on this route (V2), so a
@@ -25197,7 +24251,7 @@ var ResourcesResource = class extends BaseResource {
25197
24251
  /** Search Resource records: bounded contains on firstName + lastName (no vendor free-text search exists);
25198
24252
  * limit defaults to 25, max 500. */
25199
24253
  async search(query, opts) {
25200
- return this.searchBase(query, { limit: _optionalChain([opts, 'optionalAccess', _291 => _291.limit]) });
24254
+ return this.searchBase(query, { limit: _optionalChain([opts, 'optionalAccess', _270 => _270.limit]) });
25201
24255
  }
25202
24256
  /** Node-parity special op (hand-written in src/resources/special/resources.ts). */
25203
24257
  transferOwnership(sourceResourceIds, destinationResourceId, opts = {}) {
@@ -25229,10 +24283,10 @@ function uiHostFromBase(baseUrl) {
25229
24283
  let hostname;
25230
24284
  try {
25231
24285
  hostname = new URL(baseUrl).hostname.toLowerCase();
25232
- } catch (e20) {
24286
+ } catch (e19) {
25233
24287
  return void 0;
25234
24288
  }
25235
- const n = _optionalChain([REST_HOST, 'access', _292 => _292.exec, 'call', _293 => _293(hostname), 'optionalAccess', _294 => _294[1]]);
24289
+ const n = _optionalChain([REST_HOST, 'access', _271 => _271.exec, 'call', _272 => _272(hostname), 'optionalAccess', _273 => _273[1]]);
25236
24290
  return n === void 0 ? void 0 : `ww${n}.autotask.net`;
25237
24291
  }
25238
24292
  function buildUiLink(input) {
@@ -25285,7 +24339,7 @@ async function postedStateOf(http, entries, op, beforeRequest) {
25285
24339
  let incomplete = false;
25286
24340
  for (let c = 0; c < ids2.length; c += FETCH_BY_IDS_CHUNK_SIZE) {
25287
24341
  const chunk = ids2.slice(c, c + FETCH_BY_IDS_CHUNK_SIZE);
25288
- _optionalChain([beforeRequest, 'optionalCall', _295 => _295()]);
24342
+ _optionalChain([beforeRequest, 'optionalCall', _274 => _274()]);
25289
24343
  const res = await http.request({
25290
24344
  method: "POST",
25291
24345
  idempotent: true,
@@ -25332,11 +24386,11 @@ async function queryTimeEntries(_resource, filter, limit, operation, http, curso
25332
24386
  return { items, hasMore: pageDetails.nextPageUrl != null, nextCursor: _nullishCoalesce(pageDetails.nextPageUrl, () => ( null)) };
25333
24387
  }
25334
24388
  function pagingOpts(opts, OP9) {
25335
- if (_optionalChain([opts, 'optionalAccess', _296 => _296.countOnly]) !== void 0 && typeof opts.countOnly !== "boolean") {
24389
+ if (_optionalChain([opts, 'optionalAccess', _275 => _275.countOnly]) !== void 0 && typeof opts.countOnly !== "boolean") {
25336
24390
  throw new AutotaskConfigError(`${OP9}: countOnly must be a boolean`, { operation: OP9 });
25337
24391
  }
25338
- const countOnly = _optionalChain([opts, 'optionalAccess', _297 => _297.countOnly]) === true;
25339
- const cursor = _optionalChain([opts, 'optionalAccess', _298 => _298.cursor]);
24392
+ const countOnly = _optionalChain([opts, 'optionalAccess', _276 => _276.countOnly]) === true;
24393
+ const cursor = _optionalChain([opts, 'optionalAccess', _277 => _277.cursor]);
25340
24394
  if (cursor !== void 0 && (typeof cursor !== "string" || cursor.trim() === "")) {
25341
24395
  throw new AutotaskConfigError(`${OP9}: cursor must be the non-empty nextCursor of a previous call`, { operation: OP9 });
25342
24396
  }
@@ -25346,9 +24400,9 @@ function pagingOpts(opts, OP9) {
25346
24400
  if (cursor !== void 0 && countOnly) {
25347
24401
  throw new AutotaskConfigError(`${OP9}: cursor does not apply with countOnly (countOnly reads every page itself)`, { operation: OP9 });
25348
24402
  }
25349
- const budget = _nullishCoalesce(_optionalChain([opts, 'optionalAccess', _299 => _299.maxVendorCalls]), () => ( COUNT_BUDGET_DEFAULT));
24403
+ const budget = _nullishCoalesce(_optionalChain([opts, 'optionalAccess', _278 => _278.maxVendorCalls]), () => ( COUNT_BUDGET_DEFAULT));
25350
24404
  if (!Number.isInteger(budget) || budget < 1 || budget > COUNT_BUDGET_MAX) {
25351
- throw new AutotaskConfigError(`${OP9}: maxVendorCalls must be an integer 1-${COUNT_BUDGET_MAX} (got ${String(_optionalChain([opts, 'optionalAccess', _300 => _300.maxVendorCalls]))})`, { operation: OP9 });
24405
+ throw new AutotaskConfigError(`${OP9}: maxVendorCalls must be an integer 1-${COUNT_BUDGET_MAX} (got ${String(_optionalChain([opts, 'optionalAccess', _279 => _279.maxVendorCalls]))})`, { operation: OP9 });
25352
24406
  }
25353
24407
  return { ...cursor === void 0 ? {} : { cursor }, countOnly, budget };
25354
24408
  }
@@ -25419,18 +24473,18 @@ async function countPostedState(http, filter, OP9, budget, window) {
25419
24473
  return { window, total: states.size, postedCount, unpostedCount: states.size - postedCount, vendorCalls: issued, estimate, budget };
25420
24474
  }
25421
24475
  async function windowQuery3(_resource, kind, http, opts) {
25422
- const now = _nullishCoalesce(_optionalChain([opts, 'optionalAccess', _301 => _301.now]), () => ( /* @__PURE__ */ new Date()));
25423
- const tz = _optionalChain([opts, 'optionalAccess', _302 => _302.tz]) === void 0 ? void 0 : validateZone(opts.tz, `time_entries.${kind}`);
24476
+ const now = _nullishCoalesce(_optionalChain([opts, 'optionalAccess', _280 => _280.now]), () => ( /* @__PURE__ */ new Date()));
24477
+ const tz = _optionalChain([opts, 'optionalAccess', _281 => _281.tz]) === void 0 ? void 0 : validateZone(opts.tz, `time_entries.${kind}`);
25424
24478
  const { from, to } = dateOnlyWindowBounds(kind, now, tz);
25425
24479
  const filter = [
25426
24480
  { field: "dateWorked", op: "gte", value: from },
25427
24481
  { field: "dateWorked", op: "lte", value: to }
25428
24482
  ];
25429
- const { items } = await queryTimeEntries(_resource, filter, clampWindowLimit(_optionalChain([opts, 'optionalAccess', _303 => _303.limit])), `time_entries.${kind}`, http);
24483
+ const { items } = await queryTimeEntries(_resource, filter, clampWindowLimit(_optionalChain([opts, 'optionalAccess', _282 => _282.limit])), `time_entries.${kind}`, http);
25430
24484
  return items;
25431
24485
  }
25432
24486
  async function customRange3(_resource, from, to, http, opts) {
25433
- if (_optionalChain([opts, 'optionalAccess', _304 => _304.tz]) !== void 0) validateZone(opts.tz, "time_entries.customRange");
24487
+ if (_optionalChain([opts, 'optionalAccess', _283 => _283.tz]) !== void 0) validateZone(opts.tz, "time_entries.customRange");
25434
24488
  const fromB = customRangeDay(from, "from", void 0, "customRange");
25435
24489
  const toB = customRangeDay(to, "to", void 0, "customRange");
25436
24490
  if (fromB.ms > toB.ms) {
@@ -25441,7 +24495,7 @@ async function customRange3(_resource, from, to, http, opts) {
25441
24495
  { field: "dateWorked", op: "gte", value: iso2(fromB.ms) },
25442
24496
  { field: "dateWorked", op: "lte", value: iso2(toEndMs) }
25443
24497
  ];
25444
- const { items } = await queryTimeEntries(_resource, filter, clampWindowLimit(_optionalChain([opts, 'optionalAccess', _305 => _305.limit])), "time_entries.customRange", http);
24498
+ const { items } = await queryTimeEntries(_resource, filter, clampWindowLimit(_optionalChain([opts, 'optionalAccess', _284 => _284.limit])), "time_entries.customRange", http);
25445
24499
  return items;
25446
24500
  }
25447
24501
  async function postedStateQuery(_resource, mode, http, opts) {
@@ -25449,16 +24503,16 @@ async function postedStateQuery(_resource, mode, http, opts) {
25449
24503
  const paging = pagingOpts(opts, operation);
25450
24504
  const window = { from: null, to: null };
25451
24505
  const filter = [];
25452
- const fromB = _optionalChain([opts, 'optionalAccess', _306 => _306.dateFrom]) === void 0 ? void 0 : customRangeDay(opts.dateFrom, "from", void 0, operation);
25453
- const toB = _optionalChain([opts, 'optionalAccess', _307 => _307.dateTo]) === void 0 ? void 0 : customRangeDay(opts.dateTo, "to", void 0, operation);
24506
+ const fromB = _optionalChain([opts, 'optionalAccess', _285 => _285.dateFrom]) === void 0 ? void 0 : customRangeDay(opts.dateFrom, "from", void 0, operation);
24507
+ const toB = _optionalChain([opts, 'optionalAccess', _286 => _286.dateTo]) === void 0 ? void 0 : customRangeDay(opts.dateTo, "to", void 0, operation);
25454
24508
  if (fromB && toB && fromB.ms > toB.ms) {
25455
24509
  throw new AutotaskConfigError(`${operation}: dateFrom (${iso2(fromB.ms)}) must not be after dateTo (${iso2(toB.ms)})`, { operation });
25456
24510
  }
25457
24511
  if (fromB) filter.push({ field: "dateWorked", op: "gte", value: window.from = iso2(fromB.ms) });
25458
24512
  if (toB) filter.push({ field: "dateWorked", op: "lte", value: window.to = iso2(toB.ms + DAY_MS2 - 1) });
25459
- if (_optionalChain([opts, 'optionalAccess', _308 => _308.ticketID]) !== void 0) filter.push({ field: "ticketID", op: "eq", value: opts.ticketID });
24513
+ if (_optionalChain([opts, 'optionalAccess', _287 => _287.ticketID]) !== void 0) filter.push({ field: "ticketID", op: "eq", value: opts.ticketID });
25460
24514
  if (paging.countOnly) return countPostedState(http, filter, operation, paging.budget, window);
25461
- const read = await queryTimeEntries(_resource, filter, clampWindowLimit(_optionalChain([opts, 'optionalAccess', _309 => _309.limit])), operation, http, paging.cursor);
24515
+ const read = await queryTimeEntries(_resource, filter, clampWindowLimit(_optionalChain([opts, 'optionalAccess', _288 => _288.limit])), operation, http, paging.cursor);
25462
24516
  const { states, incomplete } = await postedStateOf(http, read.items, operation);
25463
24517
  const items = read.items.filter((e) => states.get(e.id) === mode);
25464
24518
  const approvedCount = items.filter(isApproved).length;
@@ -25468,7 +24522,7 @@ async function postedStateQuery(_resource, mode, http, opts) {
25468
24522
  async function postedWithin(_resource, from, to, http, opts, opLabel = "time_entries.postedWithin") {
25469
24523
  const OP9 = opLabel;
25470
24524
  const paging = pagingOpts(opts, OP9);
25471
- if (_optionalChain([opts, 'optionalAccess', _310 => _310.tz]) !== void 0) validateZone(opts.tz, OP9);
24525
+ if (_optionalChain([opts, 'optionalAccess', _289 => _289.tz]) !== void 0) validateZone(opts.tz, OP9);
25472
24526
  const fromB = customRangeDay(from, "from", void 0, OP9);
25473
24527
  const toB = customRangeDay(to, "to", void 0, OP9);
25474
24528
  if (fromB.ms > toB.ms) {
@@ -25479,11 +24533,11 @@ async function postedWithin(_resource, from, to, http, opts, opLabel = "time_ent
25479
24533
  { field: "dateWorked", op: "gte", value: iso2(fromB.ms) },
25480
24534
  { field: "dateWorked", op: "lte", value: iso2(windowToMs) }
25481
24535
  ];
25482
- if (_optionalChain([opts, 'optionalAccess', _311 => _311.ticketID]) !== void 0) filter.push({ field: "ticketID", op: "eq", value: opts.ticketID });
25483
- if (_optionalChain([opts, 'optionalAccess', _312 => _312.resourceID]) !== void 0) filter.push({ field: "resourceID", op: "eq", value: opts.resourceID });
24536
+ if (_optionalChain([opts, 'optionalAccess', _290 => _290.ticketID]) !== void 0) filter.push({ field: "ticketID", op: "eq", value: opts.ticketID });
24537
+ if (_optionalChain([opts, 'optionalAccess', _291 => _291.resourceID]) !== void 0) filter.push({ field: "resourceID", op: "eq", value: opts.resourceID });
25484
24538
  const window = { from: iso2(fromB.ms), to: iso2(windowToMs) };
25485
24539
  if (paging.countOnly) return countPostedState(http, filter, OP9, paging.budget, window);
25486
- const { items, hasMore, nextCursor } = await queryTimeEntries(_resource, filter, clampWindowLimit(_optionalChain([opts, 'optionalAccess', _313 => _313.limit])), OP9, http, paging.cursor);
24540
+ const { items, hasMore, nextCursor } = await queryTimeEntries(_resource, filter, clampWindowLimit(_optionalChain([opts, 'optionalAccess', _292 => _292.limit])), OP9, http, paging.cursor);
25487
24541
  const { states, incomplete } = await postedStateOf(http, items, OP9);
25488
24542
  let postedCount = 0;
25489
24543
  let unpostedCount = 0;
@@ -25491,7 +24545,7 @@ async function postedWithin(_resource, from, to, http, opts, opLabel = "time_ent
25491
24545
  if (state === "posted") postedCount += 1;
25492
24546
  else unpostedCount += 1;
25493
24547
  }
25494
- const keep = _optionalChain([opts, 'optionalAccess', _314 => _314.posted]) === void 0 ? void 0 : opts.posted ? "posted" : "unposted";
24548
+ const keep = _optionalChain([opts, 'optionalAccess', _293 => _293.posted]) === void 0 ? void 0 : opts.posted ? "posted" : "unposted";
25495
24549
  return {
25496
24550
  items: keep === void 0 ? items : items.filter((e) => states.get(e.id) === keep),
25497
24551
  postedCount,
@@ -25941,7 +24995,7 @@ function sameValue(a, b) {
25941
24995
  async function readTicket(http, id, retries, operation) {
25942
24996
  try {
25943
24997
  const res = await http.request({ method: "GET", path: `/V1.0/Tickets/${id}`, operation, maxRetries: retries });
25944
- const item = _optionalChain([res, 'optionalAccess', _315 => _315.item]);
24998
+ const item = _optionalChain([res, 'optionalAccess', _294 => _294.item]);
25945
24999
  return _nullishCoalesce(item, () => ( null));
25946
25000
  } catch (e) {
25947
25001
  if (isAutotaskError(e) && (e.httpStatus === 404 || e.code === "NOT_FOUND")) return null;
@@ -25986,7 +25040,7 @@ async function preview(http, spec, sourceId, toTicketId, opts) {
25986
25040
  const facts = {
25987
25041
  closedStatus: { closedIds: closedStatus.closedIds, source: closedStatus.source, note: closedStatus.note },
25988
25042
  sourceTicketId,
25989
- sourceCompanyID: _nullishCoalesce(_optionalChain([origin, 'optionalAccess', _316 => _316.companyID]), () => ( null)),
25043
+ sourceCompanyID: _nullishCoalesce(_optionalChain([origin, 'optionalAccess', _295 => _295.companyID]), () => ( null)),
25990
25044
  targetCompanyID: _nullishCoalesce(target.companyID, () => ( null)),
25991
25045
  companyChanged: origin === null ? null : origin.companyID !== target.companyID,
25992
25046
  contractChanged: origin === null ? null : (_nullishCoalesce(origin.contractID, () => ( null))) !== (_nullishCoalesce(target.contractID, () => ( null)))
@@ -26104,7 +25158,7 @@ var CONTRACT_SCOPED = ["contractID", "contractServiceID", "contractServiceBundle
26104
25158
  async function readTimeEntry(http, id, retries) {
26105
25159
  try {
26106
25160
  const res = await http.request({ method: "GET", path: `/V1.0/TimeEntries/${id}`, operation: OP6, maxRetries: retries });
26107
- return _nullishCoalesce(_optionalChain([res, 'optionalAccess', _317 => _317.item]), () => ( null));
25161
+ return _nullishCoalesce(_optionalChain([res, 'optionalAccess', _296 => _296.item]), () => ( null));
26108
25162
  } catch (e) {
26109
25163
  if (isAutotaskError(e) && (e.httpStatus === 404 || e.code === "NOT_FOUND")) return null;
26110
25164
  throw e;
@@ -26121,7 +25175,7 @@ async function checkSource({ http, source, sourceId, toTicketId, retries }) {
26121
25175
  let billing;
26122
25176
  try {
26123
25177
  billing = await queryRows(http, "BillingItems", [{ field: "timeEntryID", op: "eq", value: sourceId }], OP6, retries);
26124
- } catch (e21) {
25178
+ } catch (e20) {
26125
25179
  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" } });
26126
25180
  }
26127
25181
  if (billing.some((b) => b.invoiceID || b.postedDate)) {
@@ -26323,17 +25377,17 @@ async function recentTickets(_resource, from, to, http, opts) {
26323
25377
  for (const [ticketID, g] of groups) {
26324
25378
  const t = tickets.get(ticketID);
26325
25379
  if (t === void 0) unresolvedTicketIDs.push(ticketID);
26326
- if (companyID !== void 0 && _optionalChain([t, 'optionalAccess', _318 => _318.companyID]) !== companyID) {
25380
+ if (companyID !== void 0 && _optionalChain([t, 'optionalAccess', _297 => _297.companyID]) !== companyID) {
26327
25381
  if (t !== void 0) excludedTickets += 1;
26328
25382
  continue;
26329
25383
  }
26330
25384
  items.push({
26331
25385
  ticketID,
26332
- ..._optionalChain([t, 'optionalAccess', _319 => _319.ticketNumber]) !== void 0 ? { ticketNumber: t.ticketNumber } : {},
26333
- ..._optionalChain([t, 'optionalAccess', _320 => _320.title]) !== void 0 ? { title: t.title } : {},
26334
- ..._optionalChain([t, 'optionalAccess', _321 => _321.status]) !== void 0 ? { status: t.status } : {},
26335
- ..._optionalChain([t, 'optionalAccess', _322 => _322.assignedResourceID]) !== void 0 ? { assignedResourceID: t.assignedResourceID } : {},
26336
- ..._optionalChain([t, 'optionalAccess', _323 => _323.companyID]) !== void 0 ? { companyID: t.companyID } : {},
25386
+ ..._optionalChain([t, 'optionalAccess', _298 => _298.ticketNumber]) !== void 0 ? { ticketNumber: t.ticketNumber } : {},
25387
+ ..._optionalChain([t, 'optionalAccess', _299 => _299.title]) !== void 0 ? { title: t.title } : {},
25388
+ ..._optionalChain([t, 'optionalAccess', _300 => _300.status]) !== void 0 ? { status: t.status } : {},
25389
+ ..._optionalChain([t, 'optionalAccess', _301 => _301.assignedResourceID]) !== void 0 ? { assignedResourceID: t.assignedResourceID } : {},
25390
+ ..._optionalChain([t, 'optionalAccess', _302 => _302.companyID]) !== void 0 ? { companyID: t.companyID } : {},
26337
25391
  entryCount: g.entryCount,
26338
25392
  totalHours: Math.round(g.totalHours * 100) / 100,
26339
25393
  lastWorked: g.lastWorked
@@ -26377,7 +25431,7 @@ var TimeEntriesResource = class extends BaseResource {
26377
25431
  }
26378
25432
  /** Get one record by id (item envelope; item: null → NotFoundError). `fresh: true` bypasses the read-cache lookup and refreshes the entry (issue #250). */
26379
25433
  async get(id, opts = {}) {
26380
- return this.getGlobal(id, _optionalChain([opts, 'optionalAccess', _324 => _324.fresh]) === true);
25434
+ return this.getGlobal(id, _optionalChain([opts, 'optionalAccess', _303 => _303.fresh]) === true);
26381
25435
  }
26382
25436
  /** Create a record (OperationResultModel → { id}). `dryRun: true` returns the
26383
25437
  * pinned envelope (section 19.3) and never issues the create. A 2xx that is not the
@@ -26697,19 +25751,4 @@ var TimeEntriesResource = class extends BaseResource {
26697
25751
 
26698
25752
 
26699
25753
 
26700
-
26701
-
26702
-
26703
-
26704
-
26705
-
26706
-
26707
-
26708
-
26709
-
26710
-
26711
-
26712
-
26713
-
26714
-
26715
- exports.AUTH_HEADERS = AUTH_HEADERS; exports.buildAuthHeaders = buildAuthHeaders; exports.withAuthHeaders = withAuthHeaders; exports.AutotaskError = AutotaskError; exports.AutotaskConfigError = AutotaskConfigError; exports.isAutotaskError = isAutotaskError; exports.AutotaskNetworkError = AutotaskNetworkError; exports.AutotaskTimeoutError = AutotaskTimeoutError; exports.AutotaskAbortError = AutotaskAbortError; exports.ResponseRejectedError = ResponseRejectedError; exports.BadRequestError = BadRequestError; exports.UnauthorizedError = UnauthorizedError; exports.ForbiddenError = ForbiddenError; exports.NotFoundError = NotFoundError; exports.ConflictError = ConflictError; exports.UnprocessableEntityError = UnprocessableEntityError; exports.RateLimitError = RateLimitError; exports.ServerError = ServerError; exports.UnexpectedContentTypeError = UnexpectedContentTypeError; exports.EmptyResponseError = EmptyResponseError; exports.CircuitBreakerError = CircuitBreakerError; exports.HostPolicyDeniedError = HostPolicyDeniedError; exports.ResolutionError = ResolutionError; exports.PaginationLimitError = PaginationLimitError; exports.QueryTooLongError = QueryTooLongError; exports.parseVendorErrors = parseVendorErrors; exports.isDeterministicVendor500 = isDeterministicVendor500; exports.formatVendorDetail = formatVendorDetail; exports.NoopLogger = NoopLogger; exports.REDACTED = REDACTED; exports.CREDENTIAL_HEADER_NAMES = CREDENTIAL_HEADER_NAMES; exports.isCredentialHeaderName = isCredentialHeaderName; exports.redactRecord = redactRecord; exports.SECRET_RESPONSE_FIELDS = SECRET_RESPONSE_FIELDS; exports.PERSONAL_RESPONSE_FIELDS = PERSONAL_RESPONSE_FIELDS; exports.CONFIDENTIAL_RESPONSE_FIELDS = CONFIDENTIAL_RESPONSE_FIELDS; exports.isRedactedFieldName = isRedactedFieldName; exports.redactResponseBody = redactResponseBody; exports.CREDENTIAL_SHAPE_POINTER = CREDENTIAL_SHAPE_POINTER; exports.mergeDetails = mergeDetails; exports.errorFromStatus = errorFromStatus; exports.StaleObjectError = StaleObjectError; exports.ConfirmationRequiredError = ConfirmationRequiredError; exports.DeleteBoundaryError = DeleteBoundaryError; exports.MarkerUnsafeIdError = MarkerUnsafeIdError; exports.RecoveryTruncatedError = RecoveryTruncatedError; exports.MoveRefusedError = MoveRefusedError; exports.READ_CACHE_MAX_ENTRIES = READ_CACHE_MAX_ENTRIES; exports.ReadCache = ReadCache; exports.ZONE_CATALOG = ZONE_CATALOG; exports.ZONES = ZONES; exports.DEFAULT_ZONE = DEFAULT_ZONE; exports.zoneUrl = zoneUrl; exports.resolveBaseUrl = resolveBaseUrl; exports.AGENT_PROFILE = AGENT_PROFILE; exports.DEFAULT_PAGE_SIZE = DEFAULT_PAGE_SIZE; exports.MAX_PAGE_SIZE = MAX_PAGE_SIZE; exports.DEFAULT_TIMEOUT_MS = DEFAULT_TIMEOUT_MS; exports.DEFAULT_MAX_RETRIES = DEFAULT_MAX_RETRIES; exports.DEFAULT_MAINTENANCE_MAX_WAIT_MS = DEFAULT_MAINTENANCE_MAX_WAIT_MS; exports.DEFAULT_MAINTENANCE_POLL_MS = DEFAULT_MAINTENANCE_POLL_MS; exports.MAINTENANCE_WINDOW_MAX_MS = MAINTENANCE_WINDOW_MAX_MS; exports.DEFAULT_MAX_CONCURRENT = DEFAULT_MAX_CONCURRENT; exports.DEFAULT_MAX_RESPONSE_BYTES = DEFAULT_MAX_RESPONSE_BYTES; exports.DEFAULT_MAX_SCAN_RECORDS = DEFAULT_MAX_SCAN_RECORDS; exports.DEFAULT_MAX_SCAN_PAGES = DEFAULT_MAX_SCAN_PAGES; exports.DEFAULT_READ_CACHE_TTL_MS = DEFAULT_READ_CACHE_TTL_MS; exports.DEFAULT_FIELD_CACHE_TTL_MS = DEFAULT_FIELD_CACHE_TTL_MS; exports.DEFAULT_RESOURCE_NAME_CACHE_TTL_MS = DEFAULT_RESOURCE_NAME_CACHE_TTL_MS; exports.hasSurroundingQuotes = hasSurroundingQuotes; exports.credentialShapeProblem = credentialShapeProblem; exports.CREDENTIAL_ENV_LOAD_ACTION = CREDENTIAL_ENV_LOAD_ACTION; exports.MIN_PREFIX_SUMMARY_LENGTH = MIN_PREFIX_SUMMARY_LENGTH; exports.credentialValueSummary = credentialValueSummary; exports.FIELD_CACHE_KINDS = FIELD_CACHE_KINDS; exports.DEFAULT_CIRCUIT_BREAKER_WINDOW_MS = DEFAULT_CIRCUIT_BREAKER_WINDOW_MS; exports.DEFAULT_CIRCUIT_BREAKER_FAILURE_THRESHOLD = DEFAULT_CIRCUIT_BREAKER_FAILURE_THRESHOLD; exports.DEFAULT_CIRCUIT_BREAKER_OPEN_TIMEOUT_MS = DEFAULT_CIRCUIT_BREAKER_OPEN_TIMEOUT_MS; exports.DEFAULT_CIRCUIT_BREAKER_SUCCESSES_TO_CLOSE = DEFAULT_CIRCUIT_BREAKER_SUCCESSES_TO_CLOSE; exports.DEFAULT_CIRCUIT_BREAKER_MAX_ENTRIES = DEFAULT_CIRCUIT_BREAKER_MAX_ENTRIES; exports.InMemoryCircuitBreakerStore = InMemoryCircuitBreakerStore; exports.defaultCircuitBreakerStore = defaultCircuitBreakerStore; exports.resetDefaultCircuitBreakerStore = resetDefaultCircuitBreakerStore; exports.buildBreakerKey = buildBreakerKey; exports.isBreakerFailure = isBreakerFailure; exports.DEFAULT_COOLDOWN_MAX_MS = DEFAULT_COOLDOWN_MAX_MS; exports.DEFAULT_COOLDOWN_DEFAULT_MS = DEFAULT_COOLDOWN_DEFAULT_MS; exports.DEFAULT_COOLDOWN_MAX_ENTRIES = DEFAULT_COOLDOWN_MAX_ENTRIES; exports.InMemoryCooldownStore = InMemoryCooldownStore; exports.defaultCooldownStore = defaultCooldownStore; exports.resetDefaultCooldownStore = resetDefaultCooldownStore; exports.buildTenantKey = buildTenantKey; exports.cooldownDurationMs = cooldownDurationMs; exports.resolveCredentials = resolveCredentials; exports.buildReadCache = buildReadCache; exports.resolveConfig = resolveConfig; exports.IDEMPOTENT_METHODS = IDEMPOTENT_METHODS; exports.isRecord = isRecord2; exports.assertRecord = assertRecord; exports.deepMergeParams = deepMergeParams; exports.isIdentifierCandidate = isIdentifierCandidate; exports.parseIdentifier = parseIdentifier; exports.flattenUdfs = flattenUdfs; exports.newCorrelationId = newCorrelationId; exports.redactAuditBody = redactAuditBody; exports.camelKey = camelKey; exports.FILTER_OPERATORS = FILTER_OPERATORS; exports.GROUPING_OPERATORS = GROUPING_OPERATORS; exports.MAX_OR_CONDITIONS = MAX_OR_CONDITIONS; exports.MAX_UDF_PER_QUERY = MAX_UDF_PER_QUERY; exports.MAX_QUERY_LENGTH = MAX_QUERY_LENGTH; exports.MAX_GET_LENGTH = MAX_GET_LENGTH; exports.MAX_FIELD_LENGTH = MAX_FIELD_LENGTH; exports.isFilterOperator = isFilterOperator; exports.serializeFilter = serializeFilter; exports.validateFilter = validateFilter; exports.buildFilter = buildFilter; exports.normalizedConditions = normalizedConditions; exports.normalizeInValues = normalizeInValues; exports.assertNoRelationLeaf = assertNoRelationLeaf; exports.buildUrl = buildUrl; exports.cursorToPath = cursorToPath; exports.buildQueryBody = buildQueryBody; exports.buildQueryRequest = buildQueryRequest; exports.runWithSignal = runWithSignal; exports.RELATIONSHIP_ZONE = RELATIONSHIP_ZONE; exports.RELATIONSHIP_MAP = RELATIONSHIP_MAP; exports.DELETE_BOUNDARY_NOTES = DELETE_BOUNDARY_NOTES; exports.MARKER_FIELDS = MARKER_FIELDS; exports.BLIND_SURFACE_INVENTORY = BLIND_SURFACE_INVENTORY; exports.REACHABILITY_PROBE_DEFAULTS = REACHABILITY_PROBE_DEFAULTS; exports.CREATE_GUIDANCE = CREATE_GUIDANCE; exports.unwrapItems = unwrapItems; exports.unwrapItem = unwrapItem; exports.unwrapQueryCount = unwrapQueryCount; exports.unwrapOperation = unwrapOperation; exports.passthrough = passthrough; exports.HttpClient = HttpClient; exports.BOUNDED_QUERY_MAX_LIMIT = BOUNDED_QUERY_MAX_LIMIT; exports.buildDryRunResult = buildDryRunResult; exports.mergePartialBody = mergePartialBody; exports.effectiveRequiredFields = effectiveRequiredFields; exports.MAX_PAGES = MAX_PAGES; exports.clampPageSize = clampPageSize; exports.totalPagesFor = totalPagesFor; exports.paginate = paginate; exports.paginateItems = paginateItems; exports.collectAll = collectAll; exports.toArray = toArray; exports.FIELD_PRESETS = FIELD_PRESETS; exports.FIELD_PRESET_NAMES = FIELD_PRESET_NAMES; exports.resolveFieldsPreset = resolveFieldsPreset; exports.MODEL_DATE_FIELDS = MODEL_DATE_FIELDS; exports.EXPAND_RESOURCE_NAMES_MAX_IDS = EXPAND_RESOURCE_NAMES_MAX_IDS; exports.expandResourceNames = expandResourceNames; exports.ENTITY_TYPE_TO_RESOURCE = ENTITY_TYPE_TO_RESOURCE; exports.resourceForEntityType = resourceForEntityType; exports.isEnumerableResource = isEnumerableResource; exports.WORKFLOW_RESOURCES = WORKFLOW_RESOURCES; exports.MAX_FIELD_CATALOG_WIRE = MAX_FIELD_CATALOG_WIRE; exports.FIELD_CATALOG_HONESTY_NOTE = FIELD_CATALOG_HONESTY_NOTE; exports.MIN_FIELD_CATALOG_MAX_BYTES = MIN_FIELD_CATALOG_MAX_BYTES; exports.fieldCatalog = fieldCatalog; exports.PICKLIST_VALUE_NOT_SERVED_NOTE = PICKLIST_VALUE_NOT_SERVED_NOTE; exports.PICKLIST_CATALOG_UNAVAILABLE_NOTE = PICKLIST_CATALOG_UNAVAILABLE_NOTE; exports.TICKET_LABEL_FIELDS = TICKET_LABEL_FIELDS; exports.createNamePlan = createNamePlan; exports.addNameSibling = addNameSibling; exports.addLabelSibling = addLabelSibling; exports.vendorId = vendorId; exports.resolveLineOfBusinessChain = resolveLineOfBusinessChain; exports.resolveCompositeNames = resolveCompositeNames; exports.readNamesOpt = readNamesOpt; exports.labelRows = labelRows; exports.BaseResource = BaseResource; exports.BillingCodesResource = BillingCodesResource; exports.BillingItemsResource = BillingItemsResource; exports.isDateYMD = isDateYMD; exports.monthsInWindow = monthsInWindow; exports.dayAfter = dayAfter; exports.num = num; exports.collectFilterFieldNames = collectFilterFieldNames; exports.validateQueryFieldNames = validateQueryFieldNames; exports.findUnknownFieldNames = findUnknownFieldNames; exports.resolveTicketStatusSemantics = resolveTicketStatusSemantics; exports.openStatusFilter = openStatusFilter; exports.isClosedStatus = isClosedStatus; exports.CompaniesResource = CompaniesResource; exports.PROFITABILITY_MAX_WINDOW_MONTHS = PROFITABILITY_MAX_WINDOW_MONTHS; exports.SERVICE_LINE_FKS = SERVICE_LINE_FKS; exports.CHARGE_LINE_FKS = CHARGE_LINE_FKS; exports.billingLineType = billingLineType; exports.internalAmount = internalAmount; exports.FETCH_MANY_MAX_ITEMS = FETCH_MANY_MAX_ITEMS; exports.validateFetchManyItems = validateFetchManyItems; exports.fetchMany = fetchMany; exports.FETCH_BY_IDS_MAX_ITEMS = FETCH_BY_IDS_MAX_ITEMS; exports.FETCH_BY_IDS_CHUNK_SIZE = FETCH_BY_IDS_CHUNK_SIZE; exports.fetchByIds = fetchByIds; exports.readPicklists = readPicklists; exports.ContractsResource = ContractsResource; exports.InvoicesResource = InvoicesResource; exports.OrganizationalLevel1sResource = OrganizationalLevel1sResource; exports.OrganizationalLevel2sResource = OrganizationalLevel2sResource; exports.OrganizationalLevelAssociationsResource = OrganizationalLevelAssociationsResource; exports.ResourcesResource = ResourcesResource; exports.UI_LINK_ENTITIES = UI_LINK_ENTITIES; exports.buildUiLink = buildUiLink; exports.uiLink = uiLink; exports.refuse = refuse; exports.queryRows = queryRows; exports.runMove = runMove; exports.uiLinkFor = uiLinkFor; exports.TimeEntriesResource = TimeEntriesResource;
25754
+ exports.AUTH_HEADERS = AUTH_HEADERS; exports.buildAuthHeaders = buildAuthHeaders; exports.withAuthHeaders = withAuthHeaders; exports.AutotaskError = AutotaskError; exports.AutotaskConfigError = AutotaskConfigError; exports.isAutotaskError = isAutotaskError; exports.AutotaskNetworkError = AutotaskNetworkError; exports.AutotaskTimeoutError = AutotaskTimeoutError; exports.AutotaskAbortError = AutotaskAbortError; exports.ResponseRejectedError = ResponseRejectedError; exports.BadRequestError = BadRequestError; exports.UnauthorizedError = UnauthorizedError; exports.ForbiddenError = ForbiddenError; exports.NotFoundError = NotFoundError; exports.ConflictError = ConflictError; exports.UnprocessableEntityError = UnprocessableEntityError; exports.RateLimitError = RateLimitError; exports.ServerError = ServerError; exports.UnexpectedContentTypeError = UnexpectedContentTypeError; exports.EmptyResponseError = EmptyResponseError; exports.MaintenanceError = MaintenanceError; exports.HostPolicyDeniedError = HostPolicyDeniedError; exports.ResolutionError = ResolutionError; exports.PaginationLimitError = PaginationLimitError; exports.QueryTooLongError = QueryTooLongError; exports.parseVendorErrors = parseVendorErrors; exports.isDeterministicVendor500 = isDeterministicVendor500; exports.formatVendorDetail = formatVendorDetail; exports.NoopLogger = NoopLogger; exports.REDACTED = REDACTED; exports.CREDENTIAL_HEADER_NAMES = CREDENTIAL_HEADER_NAMES; exports.isCredentialHeaderName = isCredentialHeaderName; exports.redactRecord = redactRecord; exports.SECRET_RESPONSE_FIELDS = SECRET_RESPONSE_FIELDS; exports.PERSONAL_RESPONSE_FIELDS = PERSONAL_RESPONSE_FIELDS; exports.CONFIDENTIAL_RESPONSE_FIELDS = CONFIDENTIAL_RESPONSE_FIELDS; exports.isRedactedFieldName = isRedactedFieldName; exports.redactResponseBody = redactResponseBody; exports.CREDENTIAL_SHAPE_POINTER = CREDENTIAL_SHAPE_POINTER; exports.mergeDetails = mergeDetails; exports.errorFromStatus = errorFromStatus; exports.StaleObjectError = StaleObjectError; exports.ConfirmationRequiredError = ConfirmationRequiredError; exports.DeleteBoundaryError = DeleteBoundaryError; exports.MarkerUnsafeIdError = MarkerUnsafeIdError; exports.RecoveryTruncatedError = RecoveryTruncatedError; exports.MoveRefusedError = MoveRefusedError; exports.READ_CACHE_MAX_ENTRIES = READ_CACHE_MAX_ENTRIES; exports.REFERENCE_TTL_MS = REFERENCE_TTL_MS; exports.REFERENCE_ENTITIES = REFERENCE_ENTITIES; exports.FIELD_CACHE_KINDS = FIELD_CACHE_KINDS; exports.fieldKey = fieldKey; exports.readTtlFor = readTtlFor; exports.TtlCache = TtlCache; exports.ZONE_CATALOG = ZONE_CATALOG; exports.ZONES = ZONES; exports.DEFAULT_ZONE = DEFAULT_ZONE; exports.zoneUrl = zoneUrl; exports.resolveBaseUrl = resolveBaseUrl; exports.AGENT_PROFILE = AGENT_PROFILE; exports.DEFAULT_PAGE_SIZE = DEFAULT_PAGE_SIZE; exports.MAX_PAGE_SIZE = MAX_PAGE_SIZE; exports.DEFAULT_TIMEOUT_MS = DEFAULT_TIMEOUT_MS; exports.DEFAULT_MAX_RETRIES = DEFAULT_MAX_RETRIES; exports.DEFAULT_MAINTENANCE_MAX_WAIT_MS = DEFAULT_MAINTENANCE_MAX_WAIT_MS; exports.DEFAULT_MAINTENANCE_POLL_MS = DEFAULT_MAINTENANCE_POLL_MS; exports.MAINTENANCE_WINDOW_MAX_MS = MAINTENANCE_WINDOW_MAX_MS; exports.DEFAULT_MAX_CONCURRENT = DEFAULT_MAX_CONCURRENT; exports.DEFAULT_MAX_RESPONSE_BYTES = DEFAULT_MAX_RESPONSE_BYTES; exports.DEFAULT_MAX_SCAN_RECORDS = DEFAULT_MAX_SCAN_RECORDS; exports.DEFAULT_MAX_SCAN_PAGES = DEFAULT_MAX_SCAN_PAGES; exports.DEFAULT_READ_CACHE_TTL_MS = DEFAULT_READ_CACHE_TTL_MS; exports.DEFAULT_FIELD_CACHE_TTL_MS = DEFAULT_FIELD_CACHE_TTL_MS; exports.hasSurroundingQuotes = hasSurroundingQuotes; exports.credentialShapeProblem = credentialShapeProblem; exports.CREDENTIAL_ENV_LOAD_ACTION = CREDENTIAL_ENV_LOAD_ACTION; exports.MIN_PREFIX_SUMMARY_LENGTH = MIN_PREFIX_SUMMARY_LENGTH; exports.credentialValueSummary = credentialValueSummary; exports.resolveCredentials = resolveCredentials; exports.buildReadCache = buildReadCache; exports.resolveConfig = resolveConfig; exports.IDEMPOTENT_METHODS = IDEMPOTENT_METHODS; exports.isRecord = isRecord2; exports.assertRecord = assertRecord; exports.deepMergeParams = deepMergeParams; exports.isIdentifierCandidate = isIdentifierCandidate; exports.parseIdentifier = parseIdentifier; exports.flattenUdfs = flattenUdfs; exports.newCorrelationId = newCorrelationId; exports.redactAuditBody = redactAuditBody; exports.camelKey = camelKey; exports.FILTER_OPERATORS = FILTER_OPERATORS; exports.GROUPING_OPERATORS = GROUPING_OPERATORS; exports.MAX_OR_CONDITIONS = MAX_OR_CONDITIONS; exports.MAX_UDF_PER_QUERY = MAX_UDF_PER_QUERY; exports.MAX_QUERY_LENGTH = MAX_QUERY_LENGTH; exports.MAX_GET_LENGTH = MAX_GET_LENGTH; exports.MAX_FIELD_LENGTH = MAX_FIELD_LENGTH; exports.isFilterOperator = isFilterOperator; exports.serializeFilter = serializeFilter; exports.validateFilter = validateFilter; exports.buildFilter = buildFilter; exports.normalizedConditions = normalizedConditions; exports.normalizeInValues = normalizeInValues; exports.assertNoRelationLeaf = assertNoRelationLeaf; exports.buildUrl = buildUrl; exports.cursorToPath = cursorToPath; exports.buildQueryBody = buildQueryBody; exports.buildQueryRequest = buildQueryRequest; exports.runWithSignal = runWithSignal; exports.RELATIONSHIP_ZONE = RELATIONSHIP_ZONE; exports.RELATIONSHIP_MAP = RELATIONSHIP_MAP; exports.DELETE_BOUNDARY_NOTES = DELETE_BOUNDARY_NOTES; exports.MARKER_FIELDS = MARKER_FIELDS; exports.BLIND_SURFACE_INVENTORY = BLIND_SURFACE_INVENTORY; exports.REACHABILITY_PROBE_DEFAULTS = REACHABILITY_PROBE_DEFAULTS; exports.CREATE_GUIDANCE = CREATE_GUIDANCE; exports.unwrapItems = unwrapItems; exports.unwrapItem = unwrapItem; exports.unwrapQueryCount = unwrapQueryCount; exports.unwrapOperation = unwrapOperation; exports.passthrough = passthrough; exports.HttpClient = HttpClient; exports.BOUNDED_QUERY_MAX_LIMIT = BOUNDED_QUERY_MAX_LIMIT; exports.buildDryRunResult = buildDryRunResult; exports.mergePartialBody = mergePartialBody; exports.effectiveRequiredFields = effectiveRequiredFields; exports.MAX_PAGES = MAX_PAGES; exports.clampPageSize = clampPageSize; exports.totalPagesFor = totalPagesFor; exports.paginate = paginate; exports.paginateItems = paginateItems; exports.collectAll = collectAll; exports.toArray = toArray; exports.FIELD_PRESETS = FIELD_PRESETS; exports.FIELD_PRESET_NAMES = FIELD_PRESET_NAMES; exports.resolveFieldsPreset = resolveFieldsPreset; exports.MODEL_DATE_FIELDS = MODEL_DATE_FIELDS; exports.EXPAND_RESOURCE_NAMES_MAX_IDS = EXPAND_RESOURCE_NAMES_MAX_IDS; exports.expandResourceNames = expandResourceNames; exports.ENTITY_TYPE_TO_RESOURCE = ENTITY_TYPE_TO_RESOURCE; exports.resourceForEntityType = resourceForEntityType; exports.isEnumerableResource = isEnumerableResource; exports.WORKFLOW_RESOURCES = WORKFLOW_RESOURCES; exports.MAX_FIELD_CATALOG_WIRE = MAX_FIELD_CATALOG_WIRE; exports.FIELD_CATALOG_HONESTY_NOTE = FIELD_CATALOG_HONESTY_NOTE; exports.MIN_FIELD_CATALOG_MAX_BYTES = MIN_FIELD_CATALOG_MAX_BYTES; exports.fieldCatalog = fieldCatalog; exports.PICKLIST_VALUE_NOT_SERVED_NOTE = PICKLIST_VALUE_NOT_SERVED_NOTE; exports.PICKLIST_CATALOG_UNAVAILABLE_NOTE = PICKLIST_CATALOG_UNAVAILABLE_NOTE; exports.TICKET_LABEL_FIELDS = TICKET_LABEL_FIELDS; exports.createNamePlan = createNamePlan; exports.addNameSibling = addNameSibling; exports.addLabelSibling = addLabelSibling; exports.vendorId = vendorId; exports.resolveLineOfBusinessChain = resolveLineOfBusinessChain; exports.resolveCompositeNames = resolveCompositeNames; exports.readNamesOpt = readNamesOpt; exports.labelRows = labelRows; exports.BaseResource = BaseResource; exports.BillingCodesResource = BillingCodesResource; exports.BillingItemsResource = BillingItemsResource; exports.isDateYMD = isDateYMD; exports.monthsInWindow = monthsInWindow; exports.dayAfter = dayAfter; exports.num = num; exports.collectFilterFieldNames = collectFilterFieldNames; exports.validateQueryFieldNames = validateQueryFieldNames; exports.findUnknownFieldNames = findUnknownFieldNames; exports.resolveTicketStatusSemantics = resolveTicketStatusSemantics; exports.openStatusFilter = openStatusFilter; exports.isClosedStatus = isClosedStatus; exports.CompaniesResource = CompaniesResource; exports.PROFITABILITY_MAX_WINDOW_MONTHS = PROFITABILITY_MAX_WINDOW_MONTHS; exports.SERVICE_LINE_FKS = SERVICE_LINE_FKS; exports.CHARGE_LINE_FKS = CHARGE_LINE_FKS; exports.billingLineType = billingLineType; exports.internalAmount = internalAmount; exports.FETCH_MANY_MAX_ITEMS = FETCH_MANY_MAX_ITEMS; exports.validateFetchManyItems = validateFetchManyItems; exports.fetchMany = fetchMany; exports.FETCH_BY_IDS_MAX_ITEMS = FETCH_BY_IDS_MAX_ITEMS; exports.FETCH_BY_IDS_CHUNK_SIZE = FETCH_BY_IDS_CHUNK_SIZE; exports.fetchByIds = fetchByIds; exports.readPicklists = readPicklists; exports.ContractsResource = ContractsResource; exports.InvoicesResource = InvoicesResource; exports.OrganizationalLevel1sResource = OrganizationalLevel1sResource; exports.OrganizationalLevel2sResource = OrganizationalLevel2sResource; exports.OrganizationalLevelAssociationsResource = OrganizationalLevelAssociationsResource; exports.ResourcesResource = ResourcesResource; exports.UI_LINK_ENTITIES = UI_LINK_ENTITIES; exports.buildUiLink = buildUiLink; exports.uiLink = uiLink; exports.refuse = refuse; exports.queryRows = queryRows; exports.runMove = runMove; exports.uiLinkFor = uiLinkFor; exports.TimeEntriesResource = TimeEntriesResource;