node-autotask 0.39.4 → 0.41.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +6 -2
- package/CHANGELOG.md +33 -0
- package/MCP_TOOL_CATALOG.json +10 -32
- package/MCP_TOOL_MANIFEST.md +12 -13
- package/N8N_MCP_MANIFEST.json +5 -44
- package/N8N_MCP_MANIFEST.md +7 -8
- package/README.md +29 -37
- package/capabilities.json +1346 -1535
- package/dist/base-CVx2VMSj.d.cts +103 -0
- package/dist/base-CVx2VMSj.d.ts +103 -0
- package/dist/capabilities.cjs +2 -2
- package/dist/capabilities.js +1 -1
- package/dist/{chunk-SGKGNJEI.cjs → chunk-DKAD7MOV.cjs} +771 -1835
- package/dist/{chunk-DJCRQLQE.cjs → chunk-DXEBPK55.cjs} +287 -292
- package/dist/{chunk-GHRB46YN.js → chunk-LR6FDJI3.js} +614 -1678
- package/dist/{chunk-VR6RYXTD.cjs → chunk-MJ6J74F3.cjs} +1345 -1534
- package/dist/{chunk-JBQ55HYG.js → chunk-P5655PGY.js} +2 -2
- package/dist/{chunk-DBLXPY4B.js → chunk-TCND5PFF.js} +4 -9
- package/dist/{chunk-H4S4KSXX.cjs → chunk-YAHFPOQF.cjs} +143 -143
- package/dist/{chunk-GDSCYY2G.js → chunk-ZVSSAULC.js} +1345 -1534
- package/dist/{index-DkZVRMeW.d.ts → index-CqO1HNHl.d.cts} +125 -851
- package/dist/{index-DkZVRMeW.d.cts → index-CqO1HNHl.d.ts} +125 -851
- package/dist/index.cjs +23 -51
- package/dist/index.d.cts +103 -102
- package/dist/index.d.ts +103 -102
- package/dist/index.js +19 -47
- package/dist/operations/index.cjs +178 -178
- package/dist/operations/index.d.cts +5 -4
- package/dist/operations/index.d.ts +5 -4
- package/dist/operations/index.js +3 -3
- package/dist/pool.cjs +17 -60
- package/dist/pool.d.cts +5 -13
- package/dist/pool.d.ts +5 -13
- package/dist/pool.js +13 -56
- package/dist/resources/index.cjs +4 -4
- package/dist/resources/index.d.cts +1 -1
- package/dist/resources/index.d.ts +1 -1
- package/dist/resources/index.js +3 -3
- package/dist/types/index.d.cts +1 -1
- package/dist/types/index.d.ts +1 -1
- package/docs/API.md +138 -340
- package/docs/N8N-ADAPTER.md +7 -7
- package/examples/mcp-server-http.ts +18 -18
- 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;
|
|
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
|
|
3
|
+
var _chunkMJ6J74F3cjs = require('./chunk-MJ6J74F3.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
|
|
185
|
-
/** Seconds
|
|
184
|
+
var MaintenanceError = class extends AutotaskError {
|
|
185
|
+
/** Seconds until the next probe slot. */
|
|
186
186
|
|
|
187
|
-
/** True
|
|
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: "
|
|
191
|
-
this.name = "
|
|
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
|
|
592
|
-
|
|
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.
|
|
598
|
-
|
|
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
|
|
610
|
+
throw new AutotaskConfigError(`cache ttlMs must be a positive integer (got ${String(ttlMs)})`);
|
|
601
611
|
}
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
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.
|
|
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
|
|
648
|
-
*
|
|
649
|
-
|
|
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 >=
|
|
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:
|
|
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
|
-
/**
|
|
683
|
+
/** Stored entry count (live + not-yet-purged expired). */
|
|
674
684
|
get size() {
|
|
675
685
|
return this.store.size;
|
|
676
686
|
}
|
|
677
|
-
/**
|
|
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
|
|
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 =
|
|
1079
|
-
var DEFAULT_FIELD_CACHE_TTL_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/
|
|
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
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
get(
|
|
1325
|
-
|
|
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
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
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
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
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
|
|
1359
|
-
|
|
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
|
|
1362
|
-
|
|
878
|
+
function clearCooldown(key) {
|
|
879
|
+
cooldowns.delete(key);
|
|
1363
880
|
}
|
|
1364
881
|
function cooldownDurationMs(retryAfterSecs, opts) {
|
|
1365
882
|
if (opts.threadThreshold) {
|
|
1366
|
-
const
|
|
1367
|
-
return Math.max(0,
|
|
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
|
-
|
|
1370
|
-
return Math.min(rawMs, opts.maxMs);
|
|
886
|
+
return Math.min(retryAfterSecs !== void 0 ? retryAfterSecs * 1e3 : opts.defaultMs, opts.maxMs);
|
|
1371
887
|
}
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
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
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
}
|
|
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',
|
|
1774
|
-
const cooldownMaxMs = _nullishCoalesce(_optionalChain([config, 'access',
|
|
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',
|
|
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',
|
|
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',
|
|
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',
|
|
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
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
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
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
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
|
|
1886
|
-
return new
|
|
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',
|
|
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',
|
|
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',
|
|
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|
|
|
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',
|
|
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;
|
|
@@ -16443,102 +15837,20 @@ function unwrapOperation(body, operation) {
|
|
|
16443
15837
|
attachEmptyReasonDetails(err);
|
|
16444
15838
|
throw err;
|
|
16445
15839
|
}
|
|
16446
|
-
return { id: idValue };
|
|
16447
|
-
}
|
|
16448
|
-
function toIdentifierId(value) {
|
|
16449
|
-
if (typeof value === "number" && Number.isInteger(value)) return value;
|
|
16450
|
-
if (typeof value === "string" && /^\d+$/.test(value.trim())) {
|
|
16451
|
-
const trimmed = value.trim();
|
|
16452
|
-
const parsed = Number.parseInt(trimmed, 10);
|
|
16453
|
-
return Number.isSafeInteger(parsed) ? parsed : trimmed;
|
|
16454
|
-
}
|
|
16455
|
-
return void 0;
|
|
16456
|
-
}
|
|
16457
|
-
function passthrough(body) {
|
|
16458
|
-
return body;
|
|
16459
|
-
}
|
|
16460
|
-
|
|
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();
|
|
15840
|
+
return { id: idValue };
|
|
15841
|
+
}
|
|
15842
|
+
function toIdentifierId(value) {
|
|
15843
|
+
if (typeof value === "number" && Number.isInteger(value)) return value;
|
|
15844
|
+
if (typeof value === "string" && /^\d+$/.test(value.trim())) {
|
|
15845
|
+
const trimmed = value.trim();
|
|
15846
|
+
const parsed = Number.parseInt(trimmed, 10);
|
|
15847
|
+
return Number.isSafeInteger(parsed) ? parsed : trimmed;
|
|
16540
15848
|
}
|
|
16541
|
-
|
|
15849
|
+
return void 0;
|
|
15850
|
+
}
|
|
15851
|
+
function passthrough(body) {
|
|
15852
|
+
return body;
|
|
15853
|
+
}
|
|
16542
15854
|
|
|
16543
15855
|
// src/http/body.ts
|
|
16544
15856
|
async function readBodyText(res, max, what, ctx) {
|
|
@@ -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',
|
|
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',
|
|
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/
|
|
16582
|
-
|
|
16583
|
-
|
|
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
|
-
|
|
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
|
-
},
|
|
16733
|
-
var Semaphore = (
|
|
16734
|
-
constructor(maxConcurrent) {;
|
|
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
|
-
|
|
16739
|
-
|
|
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
|
-
},
|
|
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',
|
|
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
|
-
/**
|
|
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). */
|
|
16884
|
-
|
|
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). */
|
|
16080
|
+
/** Record-read cache (absent only for `cache: false`; issue #286). */
|
|
16886
16081
|
|
|
16887
|
-
/**
|
|
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
|
|
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
|
|
16925
|
-
* Re-derived on a zone re-point (issue #43 — the re-pointed host starts
|
|
16926
|
-
* with fresh per-host state). */
|
|
16116
|
+
/** The cooldown key (`user@host`) and the maintenance-gate key (host); re-derived on a zone re-point (issue #43). */
|
|
16927
16117
|
|
|
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). */
|
|
16931
|
-
|
|
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',
|
|
16970
|
-
this.tenantKey =
|
|
16971
|
-
this.
|
|
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',
|
|
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
|
|
17016
|
-
*
|
|
17017
|
-
* the
|
|
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 =
|
|
17028
|
-
this.
|
|
17029
|
-
_optionalChain([this, 'access',
|
|
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',
|
|
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',
|
|
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`.
|
|
17113
|
-
*
|
|
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',
|
|
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
|
-
*
|
|
17166
|
-
*
|
|
17167
|
-
*
|
|
17168
|
-
|
|
17169
|
-
|
|
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.
|
|
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.
|
|
17184
16343
|
*/
|
|
17185
|
-
|
|
17186
|
-
|
|
17187
|
-
|
|
17188
|
-
|
|
17189
|
-
if (verdict
|
|
17190
|
-
this.
|
|
17191
|
-
|
|
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',
|
|
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',
|
|
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.
|
|
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 (
|
|
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',
|
|
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" && !
|
|
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',
|
|
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 =
|
|
16503
|
+
const nonJsonIsMaintenance = s.maintenancePage;
|
|
17347
16504
|
const nonJsonIsHtmlCandidate = nonJsonBody !== void 0 && isHtmlLike(res.headers.get("Content-Type"), nonJsonBody);
|
|
17348
|
-
|
|
17349
|
-
|
|
17350
|
-
|
|
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',
|
|
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 (
|
|
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',
|
|
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.
|
|
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
|
-
|
|
17414
|
-
_optionalChain([this, 'access',
|
|
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',
|
|
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',
|
|
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
|
-
|
|
17548
|
-
|
|
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.
|
|
17556
|
-
this.
|
|
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',
|
|
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 (
|
|
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',
|
|
17598
|
-
const prefix = _optionalChain([guidance, 'optionalAccess',
|
|
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',
|
|
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',
|
|
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',
|
|
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',
|
|
16755
|
+
return { fields: [...zoneSet], from: "zone", unproven: _optionalChain([guidance, 'optionalAccess', _108 => _108.unproven]) === true };
|
|
17612
16756
|
}
|
|
17613
|
-
const spec = _optionalChain([record, 'optionalAccess',
|
|
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
|
|
|
@@ -17715,6 +16859,159 @@ function resolveFieldsPreset(name, resourceKey, explicitIncludeFields) {
|
|
|
17715
16859
|
return [...preset.fields];
|
|
17716
16860
|
}
|
|
17717
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));
|
|
17011
|
+
}
|
|
17012
|
+
return snake;
|
|
17013
|
+
}
|
|
17014
|
+
|
|
17718
17015
|
// src/operations/expandResourceNames.ts
|
|
17719
17016
|
var EXPAND_RESOURCE_NAMES_MAX_IDS = 500;
|
|
17720
17017
|
function composeName(record) {
|
|
@@ -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 (
|
|
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.
|
|
17075
|
+
const cache = client.http.readCache;
|
|
17779
17076
|
const fresh = opts.fresh === true;
|
|
17780
|
-
const byId = /* @__PURE__ */ new Map();
|
|
17781
|
-
for (const id of unique) {
|
|
17782
|
-
const k = String(id);
|
|
17783
|
-
|
|
17784
|
-
|
|
17785
|
-
|
|
17786
|
-
|
|
17787
|
-
|
|
17788
|
-
|
|
17789
|
-
|
|
17790
|
-
|
|
17791
|
-
|
|
17792
|
-
|
|
17793
|
-
|
|
17794
|
-
|
|
17795
|
-
|
|
17077
|
+
const byId = /* @__PURE__ */ new Map();
|
|
17078
|
+
for (const id of unique) {
|
|
17079
|
+
const k = String(id);
|
|
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
|
-
|
|
17798
|
-
|
|
17799
|
-
|
|
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
|
-
|
|
17135
|
+
_chunkMJ6J74F3cjs.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
|
|
17223
|
+
for (const rec of _chunkMJ6J74F3cjs.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
|
|
17958
|
-
const declared = _optionalChain([rec, 'access',
|
|
17235
|
+
for (const rec of _chunkMJ6J74F3cjs.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',
|
|
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',
|
|
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',
|
|
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',
|
|
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',
|
|
17352
|
+
return _optionalChain([opts, 'optionalAccess', _123 => _123.labels]) === true;
|
|
18075
17353
|
}
|
|
18076
17354
|
function resolveSizeOpts(opts) {
|
|
18077
|
-
if (_optionalChain([opts, 'optionalAccess',
|
|
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',
|
|
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',
|
|
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',
|
|
18143
|
-
fields: () => _nullishCoalesce(_optionalChain([holder, 'optionalAccess',
|
|
18144
|
-
udf: () => _nullishCoalesce(_optionalChain([holder, 'optionalAccess',
|
|
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',
|
|
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',
|
|
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',
|
|
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',
|
|
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',
|
|
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',
|
|
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',
|
|
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',
|
|
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',
|
|
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',
|
|
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',
|
|
19027
|
-
const pinnedReason = normalizedConditions(_optionalChain([params, 'optionalAccess',
|
|
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).
|
|
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.
|
|
19094
|
-
return typeof probed
|
|
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
|
|
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
|
|
19144
|
-
*
|
|
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
|
|
19165
|
-
if (typeof
|
|
19166
|
-
const rec =
|
|
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
|
-
|
|
19175
|
-
|
|
19176
|
-
|
|
19177
|
-
|
|
19178
|
-
|
|
19179
|
-
|
|
19180
|
-
|
|
19181
|
-
|
|
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
|
-
|
|
19221
|
-
|
|
19222
|
-
|
|
19223
|
-
|
|
19224
|
-
|
|
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
|
-
|
|
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',
|
|
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',
|
|
19298
|
-
if (_optionalChain([relation, 'optionalAccess',
|
|
19299
|
-
const conditions = relation !== void 0 ? relation.conditions : normalizedConditions(_optionalChain([params, 'optionalAccess',
|
|
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',
|
|
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',
|
|
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
|
-
*
|
|
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
|
|
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',
|
|
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',
|
|
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
|
|
18429
|
+
const shared = cache.inflight(key);
|
|
19380
18430
|
if (shared !== void 0) return shared;
|
|
19381
|
-
const hit = cache.get(key
|
|
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,
|
|
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',
|
|
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,
|
|
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
|
|
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',
|
|
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',
|
|
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
|
|
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
|
|
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; `
|
|
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; `
|
|
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; `
|
|
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
|
|
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
|
|
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
|
|
19620
|
-
*
|
|
19621
|
-
* cleared by any real write through the client (the
|
|
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
|
|
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',
|
|
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',
|
|
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',
|
|
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',
|
|
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',
|
|
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,
|
|
@@ -20714,15 +19768,14 @@ async function resolveCompositeNames(http, plan, opts = {}) {
|
|
|
20714
19768
|
throw new AutotaskConfigError("composite names: the transport is not wired to an AutotaskClient (the composite must run through the client)");
|
|
20715
19769
|
}
|
|
20716
19770
|
const readCache = http.readCache;
|
|
20717
|
-
const nameCache = http.resourceNameCache;
|
|
20718
19771
|
const section = { truncated: false, rows: {}, reasons: {} };
|
|
20719
19772
|
let remaining = budget;
|
|
20720
19773
|
let contractLobPointer;
|
|
20721
19774
|
let contractLobUnread;
|
|
20722
19775
|
const coldResource = [];
|
|
20723
19776
|
for (const id of plan.resourceIds) {
|
|
20724
|
-
|
|
20725
|
-
if (readCache !== void 0 && readCache.
|
|
19777
|
+
const path = `/V1.0/Resources/${id}`;
|
|
19778
|
+
if (readCache !== void 0 && (readCache.peek(path).hit || readCache.peek(contextAbsentKey(path)).hit)) continue;
|
|
20726
19779
|
coldResource.push(id);
|
|
20727
19780
|
}
|
|
20728
19781
|
const withinBudget = coldResource.slice(0, remaining);
|
|
@@ -20800,11 +19853,11 @@ async function resolveCompositeNames(http, plan, opts = {}) {
|
|
|
20800
19853
|
const row = catalog.rows.find((r) => r.resource === labelResource);
|
|
20801
19854
|
if (row === void 0 || row.error !== null || row.fields === void 0) {
|
|
20802
19855
|
usable = false;
|
|
20803
|
-
failure = _nullishCoalesce(_optionalChain([row, 'optionalAccess',
|
|
19856
|
+
failure = _nullishCoalesce(_optionalChain([row, 'optionalAccess', _176 => _176.error]), () => ( { code: "INVALID_RESPONSE", message: `fieldCatalog: no ${labelResource} row` }));
|
|
20804
19857
|
} else {
|
|
20805
19858
|
for (const f of plan.labelFields) {
|
|
20806
19859
|
const frow = row.fields.find((x) => x.name === f);
|
|
20807
|
-
picklists[f] = _optionalChain([frow, 'optionalAccess',
|
|
19860
|
+
picklists[f] = _optionalChain([frow, 'optionalAccess', _177 => _177.picklistValues]);
|
|
20808
19861
|
}
|
|
20809
19862
|
}
|
|
20810
19863
|
} catch (err) {
|
|
@@ -20843,8 +19896,8 @@ async function resolveCompositeNames(http, plan, opts = {}) {
|
|
|
20843
19896
|
return {
|
|
20844
19897
|
section,
|
|
20845
19898
|
lineOfBusiness: lineOfBusiness2,
|
|
20846
|
-
resourceName: (id) => _nullishCoalesce(_optionalChain([section, 'access',
|
|
20847
|
-
recordName: (table, id) => _nullishCoalesce(_optionalChain([section, 'access',
|
|
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)),
|
|
20848
19901
|
labelFor: (field, value) => {
|
|
20849
19902
|
if (value === null || value === void 0) return null;
|
|
20850
19903
|
const values = picklists[field];
|
|
@@ -20897,11 +19950,11 @@ async function labelRows(client, resourceKey, rows, resourceKeyedReasons = false
|
|
|
20897
19950
|
const row = catalog.rows.find((r) => r.resource === resourceKey);
|
|
20898
19951
|
const known = _nullishCoalesce(lastPicklists.get(client), () => ( /* @__PURE__ */ new Map()));
|
|
20899
19952
|
lastPicklists.set(client, known);
|
|
20900
|
-
if (_optionalChain([row, 'optionalAccess',
|
|
19953
|
+
if (_optionalChain([row, 'optionalAccess', _184 => _184.fields]) === void 0) {
|
|
20901
19954
|
const last = known.get(resourceKey);
|
|
20902
19955
|
unavailable = (_nullishCoalesce(last, () => ( []))).filter((f) => candidates.includes(f));
|
|
20903
19956
|
if (last === void 0 || unavailable.length > 0) {
|
|
20904
|
-
catalogError = _nullishCoalesce(_optionalChain([row, 'optionalAccess',
|
|
19957
|
+
catalogError = _nullishCoalesce(_optionalChain([row, 'optionalAccess', _185 => _185.error]), () => ( { code: "INVALID_RESPONSE", message: `fieldCatalog: no ${resourceKey} row` }));
|
|
20905
19958
|
}
|
|
20906
19959
|
} else {
|
|
20907
19960
|
const all = row.fields.filter((f) => f.isPickList).map((f) => f.name);
|
|
@@ -20924,7 +19977,7 @@ async function labelRows(client, resourceKey, rows, resourceKeyedReasons = false
|
|
|
20924
19977
|
}
|
|
20925
19978
|
const plan = builder.plan();
|
|
20926
19979
|
const names = plan.resourceIds.length === 0 && plan.recordFks.length === 0 && picklistFields.length === 0 ? void 0 : await resolveCompositeNames(client.http, plan);
|
|
20927
|
-
const secondReadFailed = _optionalChain([names, 'optionalAccess',
|
|
19980
|
+
const secondReadFailed = _optionalChain([names, 'optionalAccess', _186 => _186.section, 'access', _187 => _187.catalogError]);
|
|
20928
19981
|
if (secondReadFailed !== void 0) {
|
|
20929
19982
|
catalogError ??= secondReadFailed;
|
|
20930
19983
|
unavailable = [.../* @__PURE__ */ new Set([...unavailable, ...picklistFields])];
|
|
@@ -20944,8 +19997,8 @@ async function labelRows(client, resourceKey, rows, resourceKeyedReasons = false
|
|
|
20944
19997
|
return r;
|
|
20945
19998
|
});
|
|
20946
19999
|
const capped = names === void 0 ? 0 : Object.values(names.section.rows).filter((row) => row.capped).length;
|
|
20947
|
-
const unresolved = Object.keys(_nullishCoalesce(_optionalChain([names, 'optionalAccess',
|
|
20948
|
-
const reasons = { ..._optionalChain([names, 'optionalAccess',
|
|
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]) };
|
|
20949
20002
|
for (const f of unavailable) reasons[f] = PICKLIST_CATALOG_UNAVAILABLE_NOTE;
|
|
20950
20003
|
if (capped === 0 && unresolved.length === 0 && catalogError === void 0) return { rows: out };
|
|
20951
20004
|
const parts = [];
|
|
@@ -21024,7 +20077,7 @@ var BillingCodesResource = class extends BaseResource {
|
|
|
21024
20077
|
}
|
|
21025
20078
|
/** Get one record by id (item envelope; item: null → NotFoundError). `fresh: true` bypasses the read-cache lookup and refreshes the entry (issue #250). */
|
|
21026
20079
|
async get(id, opts = {}) {
|
|
21027
|
-
return this.getGlobal(id, _optionalChain([opts, 'optionalAccess',
|
|
20080
|
+
return this.getGlobal(id, _optionalChain([opts, 'optionalAccess', _192 => _192.fresh]) === true);
|
|
21028
20081
|
}
|
|
21029
20082
|
/** Resolve a BillingCode from a numeric id (or, where the vendor indexes a primary text field,
|
|
21030
20083
|
* from an exact match on name). A numeric id miss throws NOT_FOUND; a complete
|
|
@@ -21039,7 +20092,7 @@ var BillingCodesResource = class extends BaseResource {
|
|
|
21039
20092
|
/** Search BillingCode records: bounded contains on name (no vendor free-text search exists);
|
|
21040
20093
|
* limit defaults to 25, max 500. */
|
|
21041
20094
|
async search(query, opts) {
|
|
21042
|
-
return this.searchBase(query, { limit: _optionalChain([opts, 'optionalAccess',
|
|
20095
|
+
return this.searchBase(query, { limit: _optionalChain([opts, 'optionalAccess', _193 => _193.limit]) });
|
|
21043
20096
|
}
|
|
21044
20097
|
};
|
|
21045
20098
|
|
|
@@ -21130,7 +20183,7 @@ function validateZone(tz, opName = "dateWindow") {
|
|
|
21130
20183
|
}
|
|
21131
20184
|
try {
|
|
21132
20185
|
new Intl.DateTimeFormat("en-US", { timeZone: tz });
|
|
21133
|
-
} catch (
|
|
20186
|
+
} catch (e17) {
|
|
21134
20187
|
throw new AutotaskConfigError(
|
|
21135
20188
|
`${opName}: tz is not a valid IANA timezone (${JSON.stringify(tz)}; e.g. 'Australia/Sydney' \u2014 omitted tz = UTC)`,
|
|
21136
20189
|
{ operation: opName }
|
|
@@ -21343,28 +20396,28 @@ async function customRangeQuery(http, path, field, from, to, limit, operation, z
|
|
|
21343
20396
|
|
|
21344
20397
|
// src/resources/special/billing_items.ts
|
|
21345
20398
|
async function windowQuery(_resource, kind, http, opts) {
|
|
21346
|
-
const now = _nullishCoalesce(_optionalChain([opts, 'optionalAccess',
|
|
21347
|
-
const tz = _optionalChain([opts, 'optionalAccess',
|
|
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}`);
|
|
21348
20401
|
return dateWindowQuery(
|
|
21349
20402
|
http,
|
|
21350
20403
|
"/V1.0/BillingItems/query",
|
|
21351
20404
|
"itemDate",
|
|
21352
20405
|
kind,
|
|
21353
|
-
clampWindowLimit(_optionalChain([opts, 'optionalAccess',
|
|
20406
|
+
clampWindowLimit(_optionalChain([opts, 'optionalAccess', _196 => _196.limit])),
|
|
21354
20407
|
`billing_items.${kind}`,
|
|
21355
20408
|
now,
|
|
21356
20409
|
tz
|
|
21357
20410
|
);
|
|
21358
20411
|
}
|
|
21359
20412
|
async function customRange(_resource, from, to, http, opts) {
|
|
21360
|
-
const zone = _optionalChain([opts, 'optionalAccess',
|
|
20413
|
+
const zone = _optionalChain([opts, 'optionalAccess', _197 => _197.tz]) === void 0 ? void 0 : validateZone(opts.tz, "billing_items.customRange");
|
|
21361
20414
|
return customRangeQuery(
|
|
21362
20415
|
http,
|
|
21363
20416
|
"/V1.0/BillingItems/query",
|
|
21364
20417
|
"itemDate",
|
|
21365
20418
|
from,
|
|
21366
20419
|
to,
|
|
21367
|
-
clampWindowLimit(_optionalChain([opts, 'optionalAccess',
|
|
20420
|
+
clampWindowLimit(_optionalChain([opts, 'optionalAccess', _198 => _198.limit])),
|
|
21368
20421
|
"billing_items.customRange",
|
|
21369
20422
|
zone
|
|
21370
20423
|
);
|
|
@@ -21396,7 +20449,7 @@ var BillingItemsResource = class extends BaseResource {
|
|
|
21396
20449
|
}
|
|
21397
20450
|
/** Get one record by id (item envelope; item: null → NotFoundError). `fresh: true` bypasses the read-cache lookup and refreshes the entry (issue #250). */
|
|
21398
20451
|
async get(id, opts = {}) {
|
|
21399
|
-
return this.getGlobal(id, _optionalChain([opts, 'optionalAccess',
|
|
20452
|
+
return this.getGlobal(id, _optionalChain([opts, 'optionalAccess', _199 => _199.fresh]) === true);
|
|
21400
20453
|
}
|
|
21401
20454
|
/** Update a record (PUT on the COLLECTION — the vendored spec's item path is GET-only; the vendor
|
|
21402
20455
|
* keys the record by the id in the body). The vendor validates the FULL model on this route (V2), so a
|
|
@@ -21588,9 +20641,9 @@ async function searchByDomain(_resource, domain, opts = {}, http) {
|
|
|
21588
20641
|
return fetchCompanies(http, ids2, limit);
|
|
21589
20642
|
}
|
|
21590
20643
|
async function searchByIdentity(_resource, identity, opts = {}, http) {
|
|
21591
|
-
const name = _nullishCoalesce(_optionalChain([identity, 'access',
|
|
21592
|
-
const email = _nullishCoalesce(_optionalChain([identity, 'access',
|
|
21593
|
-
const website = _nullishCoalesce(_optionalChain([identity, 'access',
|
|
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()]), () => ( ""));
|
|
21594
20647
|
if (name === "" && email === "" && website === "") {
|
|
21595
20648
|
throw new AutotaskConfigError("searchByIdentity: at least one of companyName / email / website is required");
|
|
21596
20649
|
}
|
|
@@ -21729,7 +20782,7 @@ function validateUpsertInput(companyName, params) {
|
|
|
21729
20782
|
if (typeof companyName !== "string" || companyName.trim() === "") {
|
|
21730
20783
|
throw new AutotaskConfigError("upsertByName: companyName is required (a non-empty string)", { operation: OP_UPSERT_NAME });
|
|
21731
20784
|
}
|
|
21732
|
-
if (typeof _optionalChain([params, 'optionalAccess',
|
|
20785
|
+
if (typeof _optionalChain([params, 'optionalAccess', _209 => _209.phone]) !== "string" || params.phone.trim() === "") {
|
|
21733
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 });
|
|
21734
20787
|
}
|
|
21735
20788
|
const companyType = _nullishCoalesce(params.companyType, () => ( DEFAULT_COMPANY_TYPE));
|
|
@@ -21954,7 +21007,7 @@ function readIncludeNames(record, includeFields) {
|
|
|
21954
21007
|
function resolveAuthority(record, live) {
|
|
21955
21008
|
if (live !== null) return { known: live, authority: "the tenant's live entity field metadata" };
|
|
21956
21009
|
const vendored = MODEL_FIELD_SETS[record.resource];
|
|
21957
|
-
const liveOnlyExempt = Array.isArray(_optionalChain([record, 'access',
|
|
21010
|
+
const liveOnlyExempt = Array.isArray(_optionalChain([record, 'access', _210 => _210.metadata, 'optionalAccess', _211 => _211.liveOnlyFields])) ? record.metadata.liveOnlyFields : [];
|
|
21958
21011
|
if (vendored === void 0) return void 0;
|
|
21959
21012
|
return {
|
|
21960
21013
|
known: [...vendored, ...liveOnlyExempt],
|
|
@@ -22010,11 +21063,11 @@ var CLOSED_STATUS_LABELS = ["complete", "completed", "closed", "cancelled", "can
|
|
|
22010
21063
|
var OP = "tickets.statusSemantics";
|
|
22011
21064
|
function picklistOf(fields) {
|
|
22012
21065
|
if (!Array.isArray(fields)) return [];
|
|
22013
|
-
const status = fields.find((f) => typeof _optionalChain([f, 'optionalAccess',
|
|
21066
|
+
const status = fields.find((f) => typeof _optionalChain([f, 'optionalAccess', _212 => _212.name]) === "string" && f.name.toLowerCase() === "status");
|
|
22014
21067
|
if (status === void 0 || status.isPickList !== true || !Array.isArray(status.picklistValues)) return [];
|
|
22015
21068
|
const out = [];
|
|
22016
21069
|
for (const v of status.picklistValues) {
|
|
22017
|
-
const id = Number(_optionalChain([v, 'optionalAccess',
|
|
21070
|
+
const id = Number(_optionalChain([v, 'optionalAccess', _213 => _213.value]));
|
|
22018
21071
|
if (!Number.isSafeInteger(id)) continue;
|
|
22019
21072
|
out.push({ id, label: typeof v.label === "string" ? v.label : "" });
|
|
22020
21073
|
}
|
|
@@ -22022,19 +21075,19 @@ function picklistOf(fields) {
|
|
|
22022
21075
|
}
|
|
22023
21076
|
async function readFields(http, fresh) {
|
|
22024
21077
|
const cache = http.fieldCache;
|
|
22025
|
-
const key = "tickets";
|
|
21078
|
+
const key = fieldKey("tickets", "fields");
|
|
22026
21079
|
if (!fresh && cache !== void 0) {
|
|
22027
|
-
const shared = cache.inflight(key
|
|
21080
|
+
const shared = cache.inflight(key);
|
|
22028
21081
|
if (shared !== void 0) return await shared;
|
|
22029
|
-
const hit = cache.get(key
|
|
21082
|
+
const hit = cache.get(key);
|
|
22030
21083
|
if (hit !== void 0) return hit;
|
|
22031
21084
|
}
|
|
22032
21085
|
const res = await http.request({ method: "GET", path: "/V1.0/Tickets/entityInformation/fields", operation: OP, maxRetries: 0 });
|
|
22033
|
-
const fields = _optionalChain([res, 'optionalAccess',
|
|
21086
|
+
const fields = _optionalChain([res, 'optionalAccess', _214 => _214.fields]);
|
|
22034
21087
|
if (!Array.isArray(fields)) {
|
|
22035
21088
|
throw new AutotaskError("Expected the FieldInformationResultModel envelope { fields: [] }; got a different shape", { code: "INVALID_RESPONSE", category: "server" });
|
|
22036
21089
|
}
|
|
22037
|
-
_optionalChain([cache, 'optionalAccess',
|
|
21090
|
+
if (fields.length > 0) _optionalChain([cache, 'optionalAccess', _215 => _215.set, 'call', _216 => _216(key, fields)]);
|
|
22038
21091
|
return fields;
|
|
22039
21092
|
}
|
|
22040
21093
|
function assumed(allowAssumed, operation) {
|
|
@@ -22186,7 +21239,7 @@ async function readOpenTickets(http, companyID, ticketLimit, includeFields, stat
|
|
|
22186
21239
|
closedStatus
|
|
22187
21240
|
};
|
|
22188
21241
|
if (openRows.length === 0) {
|
|
22189
|
-
const stamp = blindStampFor(_optionalChain([BLIND_SURFACE_STAMPS, 'access',
|
|
21242
|
+
const stamp = blindStampFor(_optionalChain([BLIND_SURFACE_STAMPS, 'access', _217 => _217["tickets"], 'optionalAccess', _218 => _218.query]), [{ field: "companyID", op: "eq", value: companyID }, openLeaf]);
|
|
22190
21243
|
if (stamp !== void 0) {
|
|
22191
21244
|
openTickets = {
|
|
22192
21245
|
items: openRows,
|
|
@@ -22356,7 +21409,7 @@ async function prepareCrmWrite(resource, input, http) {
|
|
|
22356
21409
|
const rows = await readRows(http, "/V1.0/CompanyLocations/query", [{ field: "companyID", op: "eq", value: companyID }]);
|
|
22357
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) }));
|
|
22358
21411
|
const active = items.filter((l) => l.isActive !== false);
|
|
22359
|
-
const m = active.filter((l) => has(ct.city) && _optionalChain([l, 'access',
|
|
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));
|
|
22360
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 };
|
|
22361
21414
|
locations = { items, pick };
|
|
22362
21415
|
if (input.intent === "add_contact" && pick.locationID === null) {
|
|
@@ -22391,7 +21444,7 @@ async function prepareCrmWrite(resource, input, http) {
|
|
|
22391
21444
|
}
|
|
22392
21445
|
}
|
|
22393
21446
|
if (input.intent === "create_company" && status !== "matched") {
|
|
22394
|
-
const eff = effectiveRequiredFields(null, _optionalChain([RELATIONSHIP_MAP, 'access',
|
|
21447
|
+
const eff = effectiveRequiredFields(null, _optionalChain([RELATIONSHIP_MAP, 'access', _222 => _222.companies, 'optionalAccess', _223 => _223.zoneFacts]));
|
|
22395
21448
|
const research = (f) => ["companyType", "ownerResourceID", "countryID"].includes(f) ? "lookup" : "web_if_available_else_ask";
|
|
22396
21449
|
const lookups = {
|
|
22397
21450
|
companyType: { via: "operations.fieldCatalog", resource: "companies", field: "companyType", labels: true, note: "map the user wording to one returned label; never hardcode ids" },
|
|
@@ -22413,16 +21466,16 @@ async function prepareCrmWrite(resource, input, http) {
|
|
|
22413
21466
|
}
|
|
22414
21467
|
let next = null;
|
|
22415
21468
|
const isInactive = (d) => d.isActive === false || d.isActive === 0;
|
|
22416
|
-
const dups = _nullishCoalesce(_optionalChain([contacts, 'optionalAccess',
|
|
21469
|
+
const dups = _nullishCoalesce(_optionalChain([contacts, 'optionalAccess', _224 => _224.duplicates]), () => ( []));
|
|
22417
21470
|
const sameCompany = companyID === null ? dups : dups.filter((d) => d.companyID === companyID);
|
|
22418
21471
|
const elsewhere = companyID === null ? [] : dups.filter((d) => d.companyID !== companyID && !isInactive(d));
|
|
22419
21472
|
if (input.intent === "add_contact" && sameCompany.length === 0 && elsewhere.length > 0) {
|
|
22420
|
-
needsInput.push({ field: "contact", why: `a contact with this ${_nullishCoalesce(_optionalChain([contacts, 'optionalAccess',
|
|
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" });
|
|
22421
21474
|
}
|
|
22422
21475
|
if (needsInput.length === 0) {
|
|
22423
21476
|
const dup = sameCompany[0];
|
|
22424
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)' };
|
|
22425
|
-
else if (input.intent === "add_contact") next = dup ? { operation: "contacts.update", note: `a contact already matches by ${_nullishCoalesce(_optionalChain([contacts, 'optionalAccess',
|
|
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]))}` };
|
|
22426
21479
|
else if (input.intent === "update_contact") next = { operation: "contacts.update", note: "send only the changed fields" };
|
|
22427
21480
|
else next = { operation: "companies.update", note: "send only the changed fields" };
|
|
22428
21481
|
}
|
|
@@ -22542,7 +21595,7 @@ function validateUnbilledWorkOptions(companyId, opts) {
|
|
|
22542
21595
|
var OP3 = "companies.unbilledWork";
|
|
22543
21596
|
function unbilledWorkPartStatus(key, conditions, items) {
|
|
22544
21597
|
if (items.length !== 0) return { status: "ok" };
|
|
22545
|
-
const stamp = blindStampFor(_optionalChain([BLIND_SURFACE_STAMPS, 'access',
|
|
21598
|
+
const stamp = blindStampFor(_optionalChain([BLIND_SURFACE_STAMPS, 'access', _229 => _229[key], 'optionalAccess', _230 => _230.query]), conditions);
|
|
22546
21599
|
if (stamp !== void 0) return { status: "blind", blindSurface: stamp, note: stamp.note };
|
|
22547
21600
|
return {
|
|
22548
21601
|
status: "ok",
|
|
@@ -22792,7 +21845,7 @@ var CompaniesResource = class extends BaseResource {
|
|
|
22792
21845
|
}
|
|
22793
21846
|
/** Get one record by id (item envelope; item: null → NotFoundError). `fresh: true` bypasses the read-cache lookup and refreshes the entry (issue #250). */
|
|
22794
21847
|
async get(id, opts = {}) {
|
|
22795
|
-
return this.getGlobal(id, _optionalChain([opts, 'optionalAccess',
|
|
21848
|
+
return this.getGlobal(id, _optionalChain([opts, 'optionalAccess', _231 => _231.fresh]) === true);
|
|
22796
21849
|
}
|
|
22797
21850
|
/** Create a record (OperationResultModel → { id}). `dryRun: true` returns the
|
|
22798
21851
|
* pinned envelope (section 19.3) and never issues the create. A 2xx that is not the
|
|
@@ -22930,7 +21983,7 @@ function validateFetchManyItems(items, cap, opName = "fetchMany", capRef = `FETC
|
|
|
22930
21983
|
}
|
|
22931
21984
|
try {
|
|
22932
21985
|
parseIdentifier(item.id, `${at}.id`);
|
|
22933
|
-
} catch (
|
|
21986
|
+
} catch (e18) {
|
|
22934
21987
|
if (typeof item.id === "string") {
|
|
22935
21988
|
problems.push(
|
|
22936
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)`
|
|
@@ -22949,8 +22002,8 @@ function validateFetchManyItems(items, cap, opName = "fetchMany", capRef = `FETC
|
|
|
22949
22002
|
problems.push(`${at}.fields: must be an array of exact vendor field names (discover them via operations.fieldCatalog)`);
|
|
22950
22003
|
continue;
|
|
22951
22004
|
}
|
|
22952
|
-
if (_optionalChain([validation, 'optionalAccess',
|
|
22953
|
-
const authoritative = _nullishCoalesce(_optionalChain([validation, 'optionalAccess',
|
|
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));
|
|
22954
22007
|
for (const f of item.fields) {
|
|
22955
22008
|
if (!authoritative.includes(f)) {
|
|
22956
22009
|
problems.push(
|
|
@@ -23056,7 +22109,7 @@ function project3(value, fields) {
|
|
|
23056
22109
|
return out;
|
|
23057
22110
|
}
|
|
23058
22111
|
function resourcePathOf(key, holder) {
|
|
23059
|
-
const path = _optionalChain([holder, 'optionalAccess',
|
|
22112
|
+
const path = _optionalChain([holder, 'optionalAccess', _236 => _236.resourcePath]);
|
|
23060
22113
|
if (typeof path !== "string" || path === "") {
|
|
23061
22114
|
throw new AutotaskConfigError(`fetchByIds: the client exposes no ${key} resource path (registry drift)`, {
|
|
23062
22115
|
suggestedAction: "Rebuild the client from the SDK build that ships this registry (the resource set is pinned by the plan)."
|
|
@@ -23669,14 +22722,14 @@ var stampPart = (key, raw, conditions, extraNotes) => {
|
|
|
23669
22722
|
status: st.status,
|
|
23670
22723
|
blindSurface: st.blindSurface,
|
|
23671
22724
|
note: [st.note, ..._nullishCoalesce(extraNotes, () => ( []))].filter(Boolean).join(" | ") || void 0,
|
|
23672
|
-
emptyReason: raw.items.length === 0 && st.status === "ok" ? _optionalChain([EMPTY_REASON_STAMPS, 'access',
|
|
22725
|
+
emptyReason: raw.items.length === 0 && st.status === "ok" ? _optionalChain([EMPTY_REASON_STAMPS, 'access', _237 => _237[key], 'optionalAccess', _238 => _238.query]) : void 0
|
|
23673
22726
|
};
|
|
23674
22727
|
};
|
|
23675
22728
|
function appendWiresNote(wires, note) {
|
|
23676
22729
|
wires.note = (wires.note ? wires.note + " | " : "") + note;
|
|
23677
22730
|
}
|
|
23678
22731
|
function makeFetchCtx(rawHttp, v, contractId, budget) {
|
|
23679
|
-
const
|
|
22732
|
+
const OP8 = PROFITABILITY_OP;
|
|
23680
22733
|
const id = parseIdentifier(contractId, "contractId");
|
|
23681
22734
|
const idNum = typeof id === "number" ? id : Number(id);
|
|
23682
22735
|
const wires = { issued: 0, requests: 0, budget };
|
|
@@ -23688,19 +22741,19 @@ function makeFetchCtx(rawHttp, v, contractId, budget) {
|
|
|
23688
22741
|
} };
|
|
23689
22742
|
const readPart = (path, conditions, fields) => {
|
|
23690
22743
|
wires.issued += 1;
|
|
23691
|
-
return degradable(() => boundedPart(http, path, conditions, v.limit,
|
|
22744
|
+
return degradable(() => boundedPart(http, path, conditions, v.limit, OP8, fields));
|
|
23692
22745
|
};
|
|
23693
22746
|
const truncatedMonths = {};
|
|
23694
22747
|
const windowedPart = async (name, key, path, cond, fields, dateField) => {
|
|
23695
22748
|
const first = await readPart(path, cond, fields);
|
|
23696
22749
|
if (first.absent !== void 0 || !first.hasMore) return { part: stampPart(key, first, cond), rows: first.items };
|
|
23697
|
-
const wp = await degradable(() => expandByMonth(http, path, cond, v.limit,
|
|
22750
|
+
const wp = await degradable(() => expandByMonth(http, path, cond, v.limit, OP8, fields, dateField, v.dateFrom, v.dateTo, wires));
|
|
23698
22751
|
if (!("truncatedMonths" in wp)) return { part: stampPart(key, wp, cond), rows: [] };
|
|
23699
22752
|
const { truncatedMonths: tm, ...part } = wp;
|
|
23700
22753
|
truncatedMonths[name] = tm;
|
|
23701
22754
|
return { part, rows: wp.items };
|
|
23702
22755
|
};
|
|
23703
|
-
return { v, OP:
|
|
22756
|
+
return { v, OP: OP8, id, idNum, rawHttp, http, wires, windowOn, readPart, windowedPart, truncatedMonths };
|
|
23704
22757
|
}
|
|
23705
22758
|
|
|
23706
22759
|
// src/resources/special/contracts/profitability.stages.ts
|
|
@@ -23734,7 +22787,7 @@ async function resolveContractType(ctx, contractType) {
|
|
|
23734
22787
|
if (contractType > 0) {
|
|
23735
22788
|
ctx.wires.issued += 1;
|
|
23736
22789
|
try {
|
|
23737
|
-
contractTypeLabel = await _asyncNullishCoalesce(await _asyncOptionalChain([(await readPicklists(ctx.http, "/V1.0/Contracts", ctx.OP)), 'access', async
|
|
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));
|
|
23738
22791
|
} catch (err) {
|
|
23739
22792
|
if (!(err instanceof AutotaskError)) throw err;
|
|
23740
22793
|
}
|
|
@@ -23745,7 +22798,7 @@ async function resolveContractType(ctx, contractType) {
|
|
|
23745
22798
|
return { contractTypeLabel, contractTypeKnown, isBlockHours, isTimeAndMaterials };
|
|
23746
22799
|
}
|
|
23747
22800
|
async function fetchRoot(ctx, resource) {
|
|
23748
|
-
const contractCached = _optionalChain([ctx, 'access',
|
|
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;
|
|
23749
22802
|
const contract = await resource.get(ctx.id);
|
|
23750
22803
|
ctx.wires.issued += 1;
|
|
23751
22804
|
if (!contractCached) ctx.wires.requests += 1;
|
|
@@ -23960,10 +23013,10 @@ async function fetchSubscriptions(ctx, cis) {
|
|
|
23960
23013
|
const labels = await readSubscriptionLabels(ctx, subscriptions);
|
|
23961
23014
|
for (const x of subscriptions) {
|
|
23962
23015
|
const sid = num(x["id"]);
|
|
23963
|
-
const periodLabel = _optionalChain([labels, 'optionalAccess',
|
|
23016
|
+
const periodLabel = _optionalChain([labels, 'optionalAccess', _247 => _247.get, 'call', _248 => _248("periodType"), 'optionalAccess', _249 => _249.get, 'call', _250 => _250(num(x["periodType"]))]);
|
|
23964
23017
|
subFactor.set(sid, periodMonthlyFactor(x["periodType"], periodLabel));
|
|
23965
23018
|
if (isOneTimePeriod(x["periodType"], periodLabel)) oneTimeSubs.add(sid);
|
|
23966
|
-
const statusLabel = _optionalChain([labels, 'optionalAccess',
|
|
23019
|
+
const statusLabel = _optionalChain([labels, 'optionalAccess', _251 => _251.get, 'call', _252 => _252("status"), 'optionalAccess', _253 => _253.get, 'call', _254 => _254(num(x["status"]))]);
|
|
23967
23020
|
const notActive = statusLabel !== void 0 ? NOT_ACTIVE_LABEL_RE.test(statusLabel) : labels === null && SUBSCRIPTION_NOT_ACTIVE_STATUS_IDS.has(num(x["status"]));
|
|
23968
23021
|
if (notActive) cancelledSubs.add(sid);
|
|
23969
23022
|
}
|
|
@@ -24664,7 +23717,7 @@ var ContractsResource = class extends BaseResource {
|
|
|
24664
23717
|
}
|
|
24665
23718
|
/** Get one record by id (item envelope; item: null → NotFoundError). `fresh: true` bypasses the read-cache lookup and refreshes the entry (issue #250). */
|
|
24666
23719
|
async get(id, opts = {}) {
|
|
24667
|
-
return this.getGlobal(id, _optionalChain([opts, 'optionalAccess',
|
|
23720
|
+
return this.getGlobal(id, _optionalChain([opts, 'optionalAccess', _255 => _255.fresh]) === true);
|
|
24668
23721
|
}
|
|
24669
23722
|
/** Create a record (OperationResultModel → { id}). `dryRun: true` returns the
|
|
24670
23723
|
* pinned envelope (section 19.3) and never issues the create. A 2xx that is not the
|
|
@@ -24700,7 +23753,7 @@ var ContractsResource = class extends BaseResource {
|
|
|
24700
23753
|
/** Search Contract records: bounded contains on contractName (no vendor free-text search exists);
|
|
24701
23754
|
* limit defaults to 25, max 500. */
|
|
24702
23755
|
async search(query, opts) {
|
|
24703
|
-
return this.searchBase(query, { limit: _optionalChain([opts, 'optionalAccess',
|
|
23756
|
+
return this.searchBase(query, { limit: _optionalChain([opts, 'optionalAccess', _256 => _256.limit]) });
|
|
24704
23757
|
}
|
|
24705
23758
|
/** Node-parity special op (hand-written in src/resources/special/contracts.ts). */
|
|
24706
23759
|
profitability(id, opts) {
|
|
@@ -24740,28 +23793,28 @@ async function pdf(_resource, id, http) {
|
|
|
24740
23793
|
return decodeFileData(res);
|
|
24741
23794
|
}
|
|
24742
23795
|
async function windowQuery2(_resource, kind, http, opts) {
|
|
24743
|
-
const now = _nullishCoalesce(_optionalChain([opts, 'optionalAccess',
|
|
24744
|
-
const tz = _optionalChain([opts, 'optionalAccess',
|
|
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}`);
|
|
24745
23798
|
return dateWindowQuery(
|
|
24746
23799
|
http,
|
|
24747
23800
|
"/V1.0/Invoices/query",
|
|
24748
23801
|
"invoiceDateTime",
|
|
24749
23802
|
kind,
|
|
24750
|
-
clampWindowLimit(_optionalChain([opts, 'optionalAccess',
|
|
23803
|
+
clampWindowLimit(_optionalChain([opts, 'optionalAccess', _259 => _259.limit])),
|
|
24751
23804
|
`invoices.${kind}`,
|
|
24752
23805
|
now,
|
|
24753
23806
|
tz
|
|
24754
23807
|
);
|
|
24755
23808
|
}
|
|
24756
23809
|
async function customRange2(_resource, from, to, http, opts) {
|
|
24757
|
-
const zone = _optionalChain([opts, 'optionalAccess',
|
|
23810
|
+
const zone = _optionalChain([opts, 'optionalAccess', _260 => _260.tz]) === void 0 ? void 0 : validateZone(opts.tz, "invoices.customRange");
|
|
24758
23811
|
return customRangeQuery(
|
|
24759
23812
|
http,
|
|
24760
23813
|
"/V1.0/Invoices/query",
|
|
24761
23814
|
"invoiceDateTime",
|
|
24762
23815
|
from,
|
|
24763
23816
|
to,
|
|
24764
|
-
clampWindowLimit(_optionalChain([opts, 'optionalAccess',
|
|
23817
|
+
clampWindowLimit(_optionalChain([opts, 'optionalAccess', _261 => _261.limit])),
|
|
24765
23818
|
"invoices.customRange",
|
|
24766
23819
|
zone
|
|
24767
23820
|
);
|
|
@@ -24793,7 +23846,7 @@ var InvoicesResource = class extends BaseResource {
|
|
|
24793
23846
|
}
|
|
24794
23847
|
/** Get one record by id (item envelope; item: null → NotFoundError). `fresh: true` bypasses the read-cache lookup and refreshes the entry (issue #250). */
|
|
24795
23848
|
async get(id, opts = {}) {
|
|
24796
|
-
return this.getGlobal(id, _optionalChain([opts, 'optionalAccess',
|
|
23849
|
+
return this.getGlobal(id, _optionalChain([opts, 'optionalAccess', _262 => _262.fresh]) === true);
|
|
24797
23850
|
}
|
|
24798
23851
|
/** Update a record (PUT on the COLLECTION — the vendored spec's item path is GET-only; the vendor
|
|
24799
23852
|
* keys the record by the id in the body). The vendor validates the FULL model on this route (V2), so a
|
|
@@ -24907,7 +23960,7 @@ var OrganizationalLevel1sResource = class extends BaseResource {
|
|
|
24907
23960
|
}
|
|
24908
23961
|
/** Get one record by id (item envelope; item: null → NotFoundError). `fresh: true` bypasses the read-cache lookup and refreshes the entry (issue #250). */
|
|
24909
23962
|
async get(id, opts = {}) {
|
|
24910
|
-
return this.getGlobal(id, _optionalChain([opts, 'optionalAccess',
|
|
23963
|
+
return this.getGlobal(id, _optionalChain([opts, 'optionalAccess', _263 => _263.fresh]) === true);
|
|
24911
23964
|
}
|
|
24912
23965
|
/** Create a record (OperationResultModel → { id}). `dryRun: true` returns the
|
|
24913
23966
|
* pinned envelope (section 19.3) and never issues the create. A 2xx that is not the
|
|
@@ -24958,7 +24011,7 @@ var OrganizationalLevel2sResource = class extends BaseResource {
|
|
|
24958
24011
|
}
|
|
24959
24012
|
/** Get one record by id (item envelope; item: null → NotFoundError). `fresh: true` bypasses the read-cache lookup and refreshes the entry (issue #250). */
|
|
24960
24013
|
async get(id, opts = {}) {
|
|
24961
|
-
return this.getGlobal(id, _optionalChain([opts, 'optionalAccess',
|
|
24014
|
+
return this.getGlobal(id, _optionalChain([opts, 'optionalAccess', _264 => _264.fresh]) === true);
|
|
24962
24015
|
}
|
|
24963
24016
|
/** Create a record (OperationResultModel → { id}). `dryRun: true` returns the
|
|
24964
24017
|
* pinned envelope (section 19.3) and never issues the create. A 2xx that is not the
|
|
@@ -25009,7 +24062,7 @@ var OrganizationalLevelAssociationsResource = class extends BaseResource {
|
|
|
25009
24062
|
}
|
|
25010
24063
|
/** Get one record by id (item envelope; item: null → NotFoundError). `fresh: true` bypasses the read-cache lookup and refreshes the entry (issue #250). */
|
|
25011
24064
|
async get(id, opts = {}) {
|
|
25012
|
-
return this.getGlobal(id, _optionalChain([opts, 'optionalAccess',
|
|
24065
|
+
return this.getGlobal(id, _optionalChain([opts, 'optionalAccess', _265 => _265.fresh]) === true);
|
|
25013
24066
|
}
|
|
25014
24067
|
/** Create a record (OperationResultModel → { id}). `dryRun: true` returns the
|
|
25015
24068
|
* pinned envelope (section 19.3) and never issues the create. A 2xx that is not the
|
|
@@ -25081,7 +24134,7 @@ async function transferOwnership(_resource, companyIds, destinationResourceId, h
|
|
|
25081
24134
|
if (typeof destination === "number" && destination < 1) {
|
|
25082
24135
|
throw new AutotaskConfigError("transferOwnership: destinationResourceId must be a positive integer");
|
|
25083
24136
|
}
|
|
25084
|
-
const dryRun = _nullishCoalesce(_optionalChain([opts, 'optionalAccess',
|
|
24137
|
+
const dryRun = _nullishCoalesce(_optionalChain([opts, 'optionalAccess', _266 => _266.dryRun]), () => ( true));
|
|
25085
24138
|
const changes = [];
|
|
25086
24139
|
const applied = [];
|
|
25087
24140
|
for (const id of ids2) {
|
|
@@ -25090,7 +24143,7 @@ async function transferOwnership(_resource, companyIds, destinationResourceId, h
|
|
|
25090
24143
|
method: "GET",
|
|
25091
24144
|
path: `/V1.0/Companies/${id}`,
|
|
25092
24145
|
operation: "resources.transferOwnership",
|
|
25093
|
-
maxRetries: _optionalChain([opts, 'optionalAccess',
|
|
24146
|
+
maxRetries: _optionalChain([opts, 'optionalAccess', _267 => _267.maxRetries])
|
|
25094
24147
|
});
|
|
25095
24148
|
const company = unwrapItem(res, id, void 0, "company");
|
|
25096
24149
|
const current = typeof company.ownerResourceID === "number" ? company.ownerResourceID : null;
|
|
@@ -25101,7 +24154,7 @@ async function transferOwnership(_resource, companyIds, destinationResourceId, h
|
|
|
25101
24154
|
path: "/V1.0/Companies",
|
|
25102
24155
|
body: { id, ownerResourceID: destination },
|
|
25103
24156
|
operation: "resources.transferOwnership",
|
|
25104
|
-
maxRetries: _optionalChain([opts, 'optionalAccess',
|
|
24157
|
+
maxRetries: _optionalChain([opts, 'optionalAccess', _268 => _268.maxRetries])
|
|
25105
24158
|
});
|
|
25106
24159
|
unwrapOperation(update);
|
|
25107
24160
|
applied.push(id);
|
|
@@ -25170,7 +24223,7 @@ var ResourcesResource = class extends BaseResource {
|
|
|
25170
24223
|
}
|
|
25171
24224
|
/** Get one record by id (item envelope; item: null → NotFoundError). `fresh: true` bypasses the read-cache lookup and refreshes the entry (issue #250). */
|
|
25172
24225
|
async get(id, opts = {}) {
|
|
25173
|
-
return this.getGlobal(id, _optionalChain([opts, 'optionalAccess',
|
|
24226
|
+
return this.getGlobal(id, _optionalChain([opts, 'optionalAccess', _269 => _269.fresh]) === true);
|
|
25174
24227
|
}
|
|
25175
24228
|
/** Update a record (PUT on the COLLECTION — the vendored spec's item path is GET-only; the vendor
|
|
25176
24229
|
* keys the record by the id in the body). The vendor validates the FULL model on this route (V2), so a
|
|
@@ -25198,7 +24251,7 @@ var ResourcesResource = class extends BaseResource {
|
|
|
25198
24251
|
/** Search Resource records: bounded contains on firstName + lastName (no vendor free-text search exists);
|
|
25199
24252
|
* limit defaults to 25, max 500. */
|
|
25200
24253
|
async search(query, opts) {
|
|
25201
|
-
return this.searchBase(query, { limit: _optionalChain([opts, 'optionalAccess',
|
|
24254
|
+
return this.searchBase(query, { limit: _optionalChain([opts, 'optionalAccess', _270 => _270.limit]) });
|
|
25202
24255
|
}
|
|
25203
24256
|
/** Node-parity special op (hand-written in src/resources/special/resources.ts). */
|
|
25204
24257
|
transferOwnership(sourceResourceIds, destinationResourceId, opts = {}) {
|
|
@@ -25230,10 +24283,10 @@ function uiHostFromBase(baseUrl) {
|
|
|
25230
24283
|
let hostname;
|
|
25231
24284
|
try {
|
|
25232
24285
|
hostname = new URL(baseUrl).hostname.toLowerCase();
|
|
25233
|
-
} catch (
|
|
24286
|
+
} catch (e19) {
|
|
25234
24287
|
return void 0;
|
|
25235
24288
|
}
|
|
25236
|
-
const n = _optionalChain([REST_HOST, 'access',
|
|
24289
|
+
const n = _optionalChain([REST_HOST, 'access', _271 => _271.exec, 'call', _272 => _272(hostname), 'optionalAccess', _273 => _273[1]]);
|
|
25237
24290
|
return n === void 0 ? void 0 : `ww${n}.autotask.net`;
|
|
25238
24291
|
}
|
|
25239
24292
|
function buildUiLink(input) {
|
|
@@ -25286,7 +24339,7 @@ async function postedStateOf(http, entries, op, beforeRequest) {
|
|
|
25286
24339
|
let incomplete = false;
|
|
25287
24340
|
for (let c = 0; c < ids2.length; c += FETCH_BY_IDS_CHUNK_SIZE) {
|
|
25288
24341
|
const chunk = ids2.slice(c, c + FETCH_BY_IDS_CHUNK_SIZE);
|
|
25289
|
-
_optionalChain([beforeRequest, 'optionalCall',
|
|
24342
|
+
_optionalChain([beforeRequest, 'optionalCall', _274 => _274()]);
|
|
25290
24343
|
const res = await http.request({
|
|
25291
24344
|
method: "POST",
|
|
25292
24345
|
idempotent: true,
|
|
@@ -25332,50 +24385,50 @@ async function queryTimeEntries(_resource, filter, limit, operation, http, curso
|
|
|
25332
24385
|
const { items, pageDetails } = unwrapItems(res);
|
|
25333
24386
|
return { items, hasMore: pageDetails.nextPageUrl != null, nextCursor: _nullishCoalesce(pageDetails.nextPageUrl, () => ( null)) };
|
|
25334
24387
|
}
|
|
25335
|
-
function pagingOpts(opts,
|
|
25336
|
-
if (_optionalChain([opts, 'optionalAccess',
|
|
25337
|
-
throw new AutotaskConfigError(`${
|
|
24388
|
+
function pagingOpts(opts, OP8) {
|
|
24389
|
+
if (_optionalChain([opts, 'optionalAccess', _275 => _275.countOnly]) !== void 0 && typeof opts.countOnly !== "boolean") {
|
|
24390
|
+
throw new AutotaskConfigError(`${OP8}: countOnly must be a boolean`, { operation: OP8 });
|
|
25338
24391
|
}
|
|
25339
|
-
const countOnly = _optionalChain([opts, 'optionalAccess',
|
|
25340
|
-
const cursor = _optionalChain([opts, 'optionalAccess',
|
|
24392
|
+
const countOnly = _optionalChain([opts, 'optionalAccess', _276 => _276.countOnly]) === true;
|
|
24393
|
+
const cursor = _optionalChain([opts, 'optionalAccess', _277 => _277.cursor]);
|
|
25341
24394
|
if (cursor !== void 0 && (typeof cursor !== "string" || cursor.trim() === "")) {
|
|
25342
|
-
throw new AutotaskConfigError(`${
|
|
24395
|
+
throw new AutotaskConfigError(`${OP8}: cursor must be the non-empty nextCursor of a previous call`, { operation: OP8 });
|
|
25343
24396
|
}
|
|
25344
24397
|
if (cursor !== void 0 && cursor.length > CURSOR_MAX) {
|
|
25345
|
-
throw new ResponseRejectedError(`${
|
|
24398
|
+
throw new ResponseRejectedError(`${OP8}: refused the pagination cursor: ${cursor.length} chars is over the ${CURSOR_MAX}-char cap (a vendor nextPageUrl is far shorter)`);
|
|
25346
24399
|
}
|
|
25347
24400
|
if (cursor !== void 0 && countOnly) {
|
|
25348
|
-
throw new AutotaskConfigError(`${
|
|
24401
|
+
throw new AutotaskConfigError(`${OP8}: cursor does not apply with countOnly (countOnly reads every page itself)`, { operation: OP8 });
|
|
25349
24402
|
}
|
|
25350
|
-
const budget = _nullishCoalesce(_optionalChain([opts, 'optionalAccess',
|
|
24403
|
+
const budget = _nullishCoalesce(_optionalChain([opts, 'optionalAccess', _278 => _278.maxVendorCalls]), () => ( COUNT_BUDGET_DEFAULT));
|
|
25351
24404
|
if (!Number.isInteger(budget) || budget < 1 || budget > COUNT_BUDGET_MAX) {
|
|
25352
|
-
throw new AutotaskConfigError(`${
|
|
24405
|
+
throw new AutotaskConfigError(`${OP8}: maxVendorCalls must be an integer 1-${COUNT_BUDGET_MAX} (got ${String(_optionalChain([opts, 'optionalAccess', _279 => _279.maxVendorCalls]))})`, { operation: OP8 });
|
|
25353
24406
|
}
|
|
25354
24407
|
return { ...cursor === void 0 ? {} : { cursor }, countOnly, budget };
|
|
25355
24408
|
}
|
|
25356
|
-
function budgetExceeded(
|
|
24409
|
+
function budgetExceeded(OP8, details, midWalk) {
|
|
25357
24410
|
const { estimate, budget, total } = details;
|
|
25358
24411
|
const raise = midWalk ? budget < COUNT_BUDGET_MAX ? `, or pass a maxVendorCalls above ${budget} for headroom past the estimate` : "" : estimate <= COUNT_BUDGET_MAX ? ` or pass maxVendorCalls >= ${estimate}` : "";
|
|
25359
24412
|
return new AutotaskError(
|
|
25360
|
-
midWalk ? `${
|
|
25361
|
-
{ code: "BUDGET_EXCEEDED", category: "policy", operation:
|
|
24413
|
+
midWalk ? `${OP8}: countOnly stopped at the ${budget}-call budget mid-walk: the walk needed more calls than the ${estimate} estimated from the ${total}-entry count (more entries than counted, or an extra page such as an empty continuation page), so the next call, which may be the closing re-count, did not fit; no partial total is returned` : `${OP8}: countOnly needs an estimated ${estimate} vendor calls for ${total} entries (1 count + 1 per ${PAGE} entries + 1 join per 250 + 1 re-count), over the ${budget}-call budget; nothing was read past the count`,
|
|
24414
|
+
{ code: "BUDGET_EXCEEDED", category: "policy", operation: OP8, details, suggestedAction: `Narrow the dateWorked window (dateFrom/dateTo)${raise}.` }
|
|
25362
24415
|
);
|
|
25363
24416
|
}
|
|
25364
|
-
async function countPostedState(http, filter,
|
|
24417
|
+
async function countPostedState(http, filter, OP8, budget, window) {
|
|
25365
24418
|
let issued = 0;
|
|
25366
24419
|
let estimate = 0;
|
|
25367
24420
|
let counted = 0;
|
|
25368
24421
|
const spend = () => {
|
|
25369
|
-
if (issued >= budget) throw budgetExceeded(
|
|
24422
|
+
if (issued >= budget) throw budgetExceeded(OP8, { estimate, budget, total: counted, issued }, true);
|
|
25370
24423
|
issued += 1;
|
|
25371
24424
|
};
|
|
25372
24425
|
spend();
|
|
25373
24426
|
counted = unwrapQueryCount(
|
|
25374
|
-
await http.request({ method: "POST", idempotent: true, path: "/V1.0/TimeEntries/query/count", body: { filter }, operation:
|
|
25375
|
-
|
|
24427
|
+
await http.request({ method: "POST", idempotent: true, path: "/V1.0/TimeEntries/query/count", body: { filter }, operation: OP8 }),
|
|
24428
|
+
OP8
|
|
25376
24429
|
);
|
|
25377
24430
|
estimate = 1 + Math.max(1, Math.ceil(counted / PAGE)) + Math.ceil(counted / 250) + 1;
|
|
25378
|
-
if (estimate > budget) throw budgetExceeded(
|
|
24431
|
+
if (estimate > budget) throw budgetExceeded(OP8, { estimate, budget, total: counted, issued }, false);
|
|
25379
24432
|
const states = /* @__PURE__ */ new Map();
|
|
25380
24433
|
let cursor = null;
|
|
25381
24434
|
do {
|
|
@@ -25385,15 +24438,15 @@ async function countPostedState(http, filter, OP9, budget, window) {
|
|
|
25385
24438
|
idempotent: true,
|
|
25386
24439
|
path: cursor === null ? "/V1.0/TimeEntries/query" : entryCursorPath(http.baseUrl, cursor),
|
|
25387
24440
|
body: { MaxRecords: PAGE, IncludeFields: ["id"], filter },
|
|
25388
|
-
operation:
|
|
24441
|
+
operation: OP8
|
|
25389
24442
|
});
|
|
25390
24443
|
const page = unwrapItems(res);
|
|
25391
|
-
const joined = await postedStateOf(http, page.items,
|
|
24444
|
+
const joined = await postedStateOf(http, page.items, OP8, spend);
|
|
25392
24445
|
if (joined.incomplete) {
|
|
25393
|
-
throw new AutotaskError(`${
|
|
24446
|
+
throw new AutotaskError(`${OP8}: countOnly refused: a billing-item join page was truncated (more than 500 billing items for 250 entries), so the posted split cannot be confirmed`, {
|
|
25394
24447
|
code: "RESOLUTION_TRUNCATED",
|
|
25395
24448
|
category: "resolution",
|
|
25396
|
-
operation:
|
|
24449
|
+
operation: OP8,
|
|
25397
24450
|
details: { issued, budget },
|
|
25398
24451
|
suggestedAction: "Narrow the dateWorked window, or page it with cursor (rows mode reports unconfirmed entries via hasMore)."
|
|
25399
24452
|
});
|
|
@@ -25403,14 +24456,14 @@ async function countPostedState(http, filter, OP9, budget, window) {
|
|
|
25403
24456
|
} while (cursor !== null);
|
|
25404
24457
|
spend();
|
|
25405
24458
|
const recounted = unwrapQueryCount(
|
|
25406
|
-
await http.request({ method: "POST", idempotent: true, path: "/V1.0/TimeEntries/query/count", body: { filter }, operation:
|
|
25407
|
-
|
|
24459
|
+
await http.request({ method: "POST", idempotent: true, path: "/V1.0/TimeEntries/query/count", body: { filter }, operation: OP8 }),
|
|
24460
|
+
OP8
|
|
25408
24461
|
);
|
|
25409
24462
|
if (states.size !== counted && states.size !== recounted) {
|
|
25410
|
-
throw new AutotaskError(`${
|
|
24463
|
+
throw new AutotaskError(`${OP8}: countOnly read ${states.size} distinct entries but the window counted ${counted} before the walk and ${recounted} after it (a page repeated or dropped entries, or the window changed mid-walk); no total is returned. Retry the call`, {
|
|
25411
24464
|
code: "INVALID_RESPONSE",
|
|
25412
24465
|
category: "server",
|
|
25413
|
-
operation:
|
|
24466
|
+
operation: OP8,
|
|
25414
24467
|
details: { reason: "count_mismatch", streamed: states.size, counted, recounted, issued, budget },
|
|
25415
24468
|
suggestedAction: "Retry the call; a window that changes during the walk can fail or pass depending on timing."
|
|
25416
24469
|
});
|
|
@@ -25420,18 +24473,18 @@ async function countPostedState(http, filter, OP9, budget, window) {
|
|
|
25420
24473
|
return { window, total: states.size, postedCount, unpostedCount: states.size - postedCount, vendorCalls: issued, estimate, budget };
|
|
25421
24474
|
}
|
|
25422
24475
|
async function windowQuery3(_resource, kind, http, opts) {
|
|
25423
|
-
const now = _nullishCoalesce(_optionalChain([opts, 'optionalAccess',
|
|
25424
|
-
const tz = _optionalChain([opts, 'optionalAccess',
|
|
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}`);
|
|
25425
24478
|
const { from, to } = dateOnlyWindowBounds(kind, now, tz);
|
|
25426
24479
|
const filter = [
|
|
25427
24480
|
{ field: "dateWorked", op: "gte", value: from },
|
|
25428
24481
|
{ field: "dateWorked", op: "lte", value: to }
|
|
25429
24482
|
];
|
|
25430
|
-
const { items } = await queryTimeEntries(_resource, filter, clampWindowLimit(_optionalChain([opts, 'optionalAccess',
|
|
24483
|
+
const { items } = await queryTimeEntries(_resource, filter, clampWindowLimit(_optionalChain([opts, 'optionalAccess', _282 => _282.limit])), `time_entries.${kind}`, http);
|
|
25431
24484
|
return items;
|
|
25432
24485
|
}
|
|
25433
24486
|
async function customRange3(_resource, from, to, http, opts) {
|
|
25434
|
-
if (_optionalChain([opts, 'optionalAccess',
|
|
24487
|
+
if (_optionalChain([opts, 'optionalAccess', _283 => _283.tz]) !== void 0) validateZone(opts.tz, "time_entries.customRange");
|
|
25435
24488
|
const fromB = customRangeDay(from, "from", void 0, "customRange");
|
|
25436
24489
|
const toB = customRangeDay(to, "to", void 0, "customRange");
|
|
25437
24490
|
if (fromB.ms > toB.ms) {
|
|
@@ -25442,7 +24495,7 @@ async function customRange3(_resource, from, to, http, opts) {
|
|
|
25442
24495
|
{ field: "dateWorked", op: "gte", value: iso2(fromB.ms) },
|
|
25443
24496
|
{ field: "dateWorked", op: "lte", value: iso2(toEndMs) }
|
|
25444
24497
|
];
|
|
25445
|
-
const { items } = await queryTimeEntries(_resource, filter, clampWindowLimit(_optionalChain([opts, 'optionalAccess',
|
|
24498
|
+
const { items } = await queryTimeEntries(_resource, filter, clampWindowLimit(_optionalChain([opts, 'optionalAccess', _284 => _284.limit])), "time_entries.customRange", http);
|
|
25446
24499
|
return items;
|
|
25447
24500
|
}
|
|
25448
24501
|
async function postedStateQuery(_resource, mode, http, opts) {
|
|
@@ -25450,16 +24503,16 @@ async function postedStateQuery(_resource, mode, http, opts) {
|
|
|
25450
24503
|
const paging = pagingOpts(opts, operation);
|
|
25451
24504
|
const window = { from: null, to: null };
|
|
25452
24505
|
const filter = [];
|
|
25453
|
-
const fromB = _optionalChain([opts, 'optionalAccess',
|
|
25454
|
-
const toB = _optionalChain([opts, 'optionalAccess',
|
|
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);
|
|
25455
24508
|
if (fromB && toB && fromB.ms > toB.ms) {
|
|
25456
24509
|
throw new AutotaskConfigError(`${operation}: dateFrom (${iso2(fromB.ms)}) must not be after dateTo (${iso2(toB.ms)})`, { operation });
|
|
25457
24510
|
}
|
|
25458
24511
|
if (fromB) filter.push({ field: "dateWorked", op: "gte", value: window.from = iso2(fromB.ms) });
|
|
25459
24512
|
if (toB) filter.push({ field: "dateWorked", op: "lte", value: window.to = iso2(toB.ms + DAY_MS2 - 1) });
|
|
25460
|
-
if (_optionalChain([opts, 'optionalAccess',
|
|
24513
|
+
if (_optionalChain([opts, 'optionalAccess', _287 => _287.ticketID]) !== void 0) filter.push({ field: "ticketID", op: "eq", value: opts.ticketID });
|
|
25461
24514
|
if (paging.countOnly) return countPostedState(http, filter, operation, paging.budget, window);
|
|
25462
|
-
const read = await queryTimeEntries(_resource, filter, clampWindowLimit(_optionalChain([opts, 'optionalAccess',
|
|
24515
|
+
const read = await queryTimeEntries(_resource, filter, clampWindowLimit(_optionalChain([opts, 'optionalAccess', _288 => _288.limit])), operation, http, paging.cursor);
|
|
25463
24516
|
const { states, incomplete } = await postedStateOf(http, read.items, operation);
|
|
25464
24517
|
const items = read.items.filter((e) => states.get(e.id) === mode);
|
|
25465
24518
|
const approvedCount = items.filter(isApproved).length;
|
|
@@ -25467,32 +24520,32 @@ async function postedStateQuery(_resource, mode, http, opts) {
|
|
|
25467
24520
|
return { items, hasMore: read.hasMore || incomplete, window, nextCursor: read.nextCursor, unconfirmed, approvedCount, unapprovedCount: items.length - approvedCount };
|
|
25468
24521
|
}
|
|
25469
24522
|
async function postedWithin(_resource, from, to, http, opts, opLabel = "time_entries.postedWithin") {
|
|
25470
|
-
const
|
|
25471
|
-
const paging = pagingOpts(opts,
|
|
25472
|
-
if (_optionalChain([opts, 'optionalAccess',
|
|
25473
|
-
const fromB = customRangeDay(from, "from", void 0,
|
|
25474
|
-
const toB = customRangeDay(to, "to", void 0,
|
|
24523
|
+
const OP8 = opLabel;
|
|
24524
|
+
const paging = pagingOpts(opts, OP8);
|
|
24525
|
+
if (_optionalChain([opts, 'optionalAccess', _289 => _289.tz]) !== void 0) validateZone(opts.tz, OP8);
|
|
24526
|
+
const fromB = customRangeDay(from, "from", void 0, OP8);
|
|
24527
|
+
const toB = customRangeDay(to, "to", void 0, OP8);
|
|
25475
24528
|
if (fromB.ms > toB.ms) {
|
|
25476
|
-
throw new AutotaskConfigError(`${
|
|
24529
|
+
throw new AutotaskConfigError(`${OP8}: from (${iso2(fromB.ms)}) must not be after to (${iso2(toB.ms)})`, { operation: OP8 });
|
|
25477
24530
|
}
|
|
25478
24531
|
const windowToMs = toB.ms + DAY_MS2 - 1;
|
|
25479
24532
|
const filter = [
|
|
25480
24533
|
{ field: "dateWorked", op: "gte", value: iso2(fromB.ms) },
|
|
25481
24534
|
{ field: "dateWorked", op: "lte", value: iso2(windowToMs) }
|
|
25482
24535
|
];
|
|
25483
|
-
if (_optionalChain([opts, 'optionalAccess',
|
|
25484
|
-
if (_optionalChain([opts, 'optionalAccess',
|
|
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 });
|
|
25485
24538
|
const window = { from: iso2(fromB.ms), to: iso2(windowToMs) };
|
|
25486
|
-
if (paging.countOnly) return countPostedState(http, filter,
|
|
25487
|
-
const { items, hasMore, nextCursor } = await queryTimeEntries(_resource, filter, clampWindowLimit(_optionalChain([opts, 'optionalAccess',
|
|
25488
|
-
const { states, incomplete } = await postedStateOf(http, items,
|
|
24539
|
+
if (paging.countOnly) return countPostedState(http, filter, OP8, paging.budget, window);
|
|
24540
|
+
const { items, hasMore, nextCursor } = await queryTimeEntries(_resource, filter, clampWindowLimit(_optionalChain([opts, 'optionalAccess', _292 => _292.limit])), OP8, http, paging.cursor);
|
|
24541
|
+
const { states, incomplete } = await postedStateOf(http, items, OP8);
|
|
25489
24542
|
let postedCount = 0;
|
|
25490
24543
|
let unpostedCount = 0;
|
|
25491
24544
|
for (const state of states.values()) {
|
|
25492
24545
|
if (state === "posted") postedCount += 1;
|
|
25493
24546
|
else unpostedCount += 1;
|
|
25494
24547
|
}
|
|
25495
|
-
const keep = _optionalChain([opts, 'optionalAccess',
|
|
24548
|
+
const keep = _optionalChain([opts, 'optionalAccess', _293 => _293.posted]) === void 0 ? void 0 : opts.posted ? "posted" : "unposted";
|
|
25496
24549
|
return {
|
|
25497
24550
|
items: keep === void 0 ? items : items.filter((e) => states.get(e.id) === keep),
|
|
25498
24551
|
postedCount,
|
|
@@ -25942,7 +24995,7 @@ function sameValue(a, b) {
|
|
|
25942
24995
|
async function readTicket(http, id, retries, operation) {
|
|
25943
24996
|
try {
|
|
25944
24997
|
const res = await http.request({ method: "GET", path: `/V1.0/Tickets/${id}`, operation, maxRetries: retries });
|
|
25945
|
-
const item = _optionalChain([res, 'optionalAccess',
|
|
24998
|
+
const item = _optionalChain([res, 'optionalAccess', _294 => _294.item]);
|
|
25946
24999
|
return _nullishCoalesce(item, () => ( null));
|
|
25947
25000
|
} catch (e) {
|
|
25948
25001
|
if (isAutotaskError(e) && (e.httpStatus === 404 || e.code === "NOT_FOUND")) return null;
|
|
@@ -25987,7 +25040,7 @@ async function preview(http, spec, sourceId, toTicketId, opts) {
|
|
|
25987
25040
|
const facts = {
|
|
25988
25041
|
closedStatus: { closedIds: closedStatus.closedIds, source: closedStatus.source, note: closedStatus.note },
|
|
25989
25042
|
sourceTicketId,
|
|
25990
|
-
sourceCompanyID: _nullishCoalesce(_optionalChain([origin, 'optionalAccess',
|
|
25043
|
+
sourceCompanyID: _nullishCoalesce(_optionalChain([origin, 'optionalAccess', _295 => _295.companyID]), () => ( null)),
|
|
25991
25044
|
targetCompanyID: _nullishCoalesce(target.companyID, () => ( null)),
|
|
25992
25045
|
companyChanged: origin === null ? null : origin.companyID !== target.companyID,
|
|
25993
25046
|
contractChanged: origin === null ? null : (_nullishCoalesce(origin.contractID, () => ( null))) !== (_nullishCoalesce(target.contractID, () => ( null)))
|
|
@@ -26105,7 +25158,7 @@ var CONTRACT_SCOPED = ["contractID", "contractServiceID", "contractServiceBundle
|
|
|
26105
25158
|
async function readTimeEntry(http, id, retries) {
|
|
26106
25159
|
try {
|
|
26107
25160
|
const res = await http.request({ method: "GET", path: `/V1.0/TimeEntries/${id}`, operation: OP6, maxRetries: retries });
|
|
26108
|
-
return _nullishCoalesce(_optionalChain([res, 'optionalAccess',
|
|
25161
|
+
return _nullishCoalesce(_optionalChain([res, 'optionalAccess', _296 => _296.item]), () => ( null));
|
|
26109
25162
|
} catch (e) {
|
|
26110
25163
|
if (isAutotaskError(e) && (e.httpStatus === 404 || e.code === "NOT_FOUND")) return null;
|
|
26111
25164
|
throw e;
|
|
@@ -26122,7 +25175,7 @@ async function checkSource({ http, source, sourceId, toTicketId, retries }) {
|
|
|
26122
25175
|
let billing;
|
|
26123
25176
|
try {
|
|
26124
25177
|
billing = await queryRows(http, "BillingItems", [{ field: "timeEntryID", op: "eq", value: sourceId }], OP6, retries);
|
|
26125
|
-
} catch (
|
|
25178
|
+
} catch (e20) {
|
|
26126
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" } });
|
|
26127
25180
|
}
|
|
26128
25181
|
if (billing.some((b) => b.invoiceID || b.postedDate)) {
|
|
@@ -26255,103 +25308,6 @@ async function approve(resource, id, opts = {}, http) {
|
|
|
26255
25308
|
};
|
|
26256
25309
|
}
|
|
26257
25310
|
|
|
26258
|
-
// src/resources/special/time_entries/recentTickets.ts
|
|
26259
|
-
var OP8 = "time_entries.recentTickets";
|
|
26260
|
-
var DAY_MS3 = 864e5;
|
|
26261
|
-
var TICKET_CHUNK = 100;
|
|
26262
|
-
var TICKET_FIELDS2 = ["id", "ticketNumber", "title", "status", "assignedResourceID", "companyID"];
|
|
26263
|
-
function positiveId2(value, name) {
|
|
26264
|
-
const n = typeof value === "string" && /^\d+$/.test(value.trim()) ? Number(value) : value;
|
|
26265
|
-
if (typeof n !== "number" || !Number.isSafeInteger(n) || n < 1) {
|
|
26266
|
-
throw new AutotaskConfigError(`${OP8}: ${name} must be a positive integer id (got ${String(value)})`, { operation: OP8 });
|
|
26267
|
-
}
|
|
26268
|
-
return n;
|
|
26269
|
-
}
|
|
26270
|
-
async function recentTickets(_resource, from, to, http, opts) {
|
|
26271
|
-
if (opts === void 0 || opts === null || typeof opts !== "object") {
|
|
26272
|
-
throw new AutotaskConfigError(`${OP8}: opts { resourceID, companyID?, tz?, limit? } is required`, { operation: OP8 });
|
|
26273
|
-
}
|
|
26274
|
-
const resourceID = positiveId2(opts.resourceID, "resourceID");
|
|
26275
|
-
const companyID = opts.companyID === void 0 ? void 0 : positiveId2(opts.companyID, "companyID");
|
|
26276
|
-
if (opts.tz !== void 0) validateZone(opts.tz, OP8);
|
|
26277
|
-
const fromB = customRangeDay(from, "from", void 0, OP8);
|
|
26278
|
-
const toB = customRangeDay(to, "to", void 0, OP8);
|
|
26279
|
-
if (fromB.ms > toB.ms) {
|
|
26280
|
-
throw new AutotaskConfigError(`${OP8}: from (${isoUtc(fromB.ms)}) must not be after to (${isoUtc(toB.ms)})`, { operation: OP8 });
|
|
26281
|
-
}
|
|
26282
|
-
const windowToMs = toB.ms + DAY_MS3 - 1;
|
|
26283
|
-
const { items: entries, hasMore } = await queryTimeEntries(
|
|
26284
|
-
_resource,
|
|
26285
|
-
[
|
|
26286
|
-
{ field: "dateWorked", op: "gte", value: isoUtc(fromB.ms) },
|
|
26287
|
-
{ field: "dateWorked", op: "lte", value: isoUtc(windowToMs) },
|
|
26288
|
-
{ field: "resourceID", op: "eq", value: resourceID }
|
|
26289
|
-
],
|
|
26290
|
-
clampWindowLimit(opts.limit),
|
|
26291
|
-
OP8,
|
|
26292
|
-
http
|
|
26293
|
-
);
|
|
26294
|
-
const groups = /* @__PURE__ */ new Map();
|
|
26295
|
-
let entriesWithoutTicket = 0;
|
|
26296
|
-
for (const e of entries) {
|
|
26297
|
-
if (typeof e.ticketID !== "number") {
|
|
26298
|
-
entriesWithoutTicket += 1;
|
|
26299
|
-
continue;
|
|
26300
|
-
}
|
|
26301
|
-
const g = _nullishCoalesce(groups.get(e.ticketID), () => ( { entryCount: 0, totalHours: 0, lastWorked: "" }));
|
|
26302
|
-
g.entryCount += 1;
|
|
26303
|
-
g.totalHours += _nullishCoalesce(e.hoursWorked, () => ( 0));
|
|
26304
|
-
if ((_nullishCoalesce(e.dateWorked, () => ( ""))) > g.lastWorked) g.lastWorked = _nullishCoalesce(e.dateWorked, () => ( ""));
|
|
26305
|
-
groups.set(e.ticketID, g);
|
|
26306
|
-
}
|
|
26307
|
-
const ids2 = [...groups.keys()];
|
|
26308
|
-
const tickets = /* @__PURE__ */ new Map();
|
|
26309
|
-
for (let c = 0; c < ids2.length; c += TICKET_CHUNK) {
|
|
26310
|
-
const chunk = ids2.slice(c, c + TICKET_CHUNK);
|
|
26311
|
-
const filter = [{ field: "id", op: "in", value: chunk }];
|
|
26312
|
-
const res = await http.request({
|
|
26313
|
-
method: "POST",
|
|
26314
|
-
idempotent: true,
|
|
26315
|
-
path: "/V1.0/Tickets/query",
|
|
26316
|
-
body: { MaxRecords: TICKET_CHUNK, filter, IncludeFields: TICKET_FIELDS2 },
|
|
26317
|
-
operation: OP8
|
|
26318
|
-
});
|
|
26319
|
-
for (const t of unwrapItems(res, OP8).items) if (typeof t.id === "number") tickets.set(t.id, t);
|
|
26320
|
-
}
|
|
26321
|
-
const items = [];
|
|
26322
|
-
const unresolvedTicketIDs = [];
|
|
26323
|
-
let excludedTickets = 0;
|
|
26324
|
-
for (const [ticketID, g] of groups) {
|
|
26325
|
-
const t = tickets.get(ticketID);
|
|
26326
|
-
if (t === void 0) unresolvedTicketIDs.push(ticketID);
|
|
26327
|
-
if (companyID !== void 0 && _optionalChain([t, 'optionalAccess', _318 => _318.companyID]) !== companyID) {
|
|
26328
|
-
if (t !== void 0) excludedTickets += 1;
|
|
26329
|
-
continue;
|
|
26330
|
-
}
|
|
26331
|
-
items.push({
|
|
26332
|
-
ticketID,
|
|
26333
|
-
..._optionalChain([t, 'optionalAccess', _319 => _319.ticketNumber]) !== void 0 ? { ticketNumber: t.ticketNumber } : {},
|
|
26334
|
-
..._optionalChain([t, 'optionalAccess', _320 => _320.title]) !== void 0 ? { title: t.title } : {},
|
|
26335
|
-
..._optionalChain([t, 'optionalAccess', _321 => _321.status]) !== void 0 ? { status: t.status } : {},
|
|
26336
|
-
..._optionalChain([t, 'optionalAccess', _322 => _322.assignedResourceID]) !== void 0 ? { assignedResourceID: t.assignedResourceID } : {},
|
|
26337
|
-
..._optionalChain([t, 'optionalAccess', _323 => _323.companyID]) !== void 0 ? { companyID: t.companyID } : {},
|
|
26338
|
-
entryCount: g.entryCount,
|
|
26339
|
-
totalHours: Math.round(g.totalHours * 100) / 100,
|
|
26340
|
-
lastWorked: g.lastWorked
|
|
26341
|
-
});
|
|
26342
|
-
}
|
|
26343
|
-
items.sort((a, b) => a.lastWorked < b.lastWorked ? 1 : a.lastWorked > b.lastWorked ? -1 : a.ticketID - b.ticketID);
|
|
26344
|
-
return {
|
|
26345
|
-
items,
|
|
26346
|
-
entriesRead: entries.length,
|
|
26347
|
-
entriesWithoutTicket,
|
|
26348
|
-
unresolvedTicketIDs,
|
|
26349
|
-
...companyID !== void 0 ? { excludedTickets } : {},
|
|
26350
|
-
window: { from: isoUtc(fromB.ms), to: isoUtc(windowToMs) },
|
|
26351
|
-
truncated: hasMore
|
|
26352
|
-
};
|
|
26353
|
-
}
|
|
26354
|
-
|
|
26355
25311
|
// src/resources/time_entries.ts
|
|
26356
25312
|
var TimeEntriesResource = class extends BaseResource {
|
|
26357
25313
|
constructor(http, pageSize) {
|
|
@@ -26378,7 +25334,7 @@ var TimeEntriesResource = class extends BaseResource {
|
|
|
26378
25334
|
}
|
|
26379
25335
|
/** Get one record by id (item envelope; item: null → NotFoundError). `fresh: true` bypasses the read-cache lookup and refreshes the entry (issue #250). */
|
|
26380
25336
|
async get(id, opts = {}) {
|
|
26381
|
-
return this.getGlobal(id, _optionalChain([opts, 'optionalAccess',
|
|
25337
|
+
return this.getGlobal(id, _optionalChain([opts, 'optionalAccess', _297 => _297.fresh]) === true);
|
|
26382
25338
|
}
|
|
26383
25339
|
/** Create a record (OperationResultModel → { id}). `dryRun: true` returns the
|
|
26384
25340
|
* pinned envelope (section 19.3) and never issues the create. A 2xx that is not the
|
|
@@ -26483,11 +25439,6 @@ var TimeEntriesResource = class extends BaseResource {
|
|
|
26483
25439
|
postedWithin(from, to, opts) {
|
|
26484
25440
|
return postedWithin(this, from, to, this.http, opts);
|
|
26485
25441
|
}
|
|
26486
|
-
/** Node-parity special op (hand-written in src/resources/special/time_entries.ts).
|
|
26487
|
-
* @deprecated Since 0.38.0, removed in the next minor. Use operations.aggregate(client, 'time_entries.query', [{ field: 'resourceID', op: 'eq', value: R }, { field: 'dateWorked', op: 'gte', value: FROM }, { field: 'dateWorked', op: 'lte', value: TO }], { groupBy: ['ticketID'], sum: ['hoursWorked'] }) — group keys carry ticketIDName; add { field: 'ticket.companyID', op: 'eq', value: C } to scope by company. */
|
|
26488
|
-
recentTickets(from, to, opts) {
|
|
26489
|
-
return recentTickets(this, from, to, this.http, opts);
|
|
26490
|
-
}
|
|
26491
25442
|
/** Node-parity special op (hand-written in src/resources/special/time_entries.ts). */
|
|
26492
25443
|
today(opts) {
|
|
26493
25444
|
return windowQuery3(this, "today", this.http, opts);
|
|
@@ -26698,19 +25649,4 @@ var TimeEntriesResource = class extends BaseResource {
|
|
|
26698
25649
|
|
|
26699
25650
|
|
|
26700
25651
|
|
|
26701
|
-
|
|
26702
|
-
|
|
26703
|
-
|
|
26704
|
-
|
|
26705
|
-
|
|
26706
|
-
|
|
26707
|
-
|
|
26708
|
-
|
|
26709
|
-
|
|
26710
|
-
|
|
26711
|
-
|
|
26712
|
-
|
|
26713
|
-
|
|
26714
|
-
|
|
26715
|
-
|
|
26716
|
-
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;
|
|
25652
|
+
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;
|