@osdk/client 2.8.0-beta.1 → 2.8.0-beta.10
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/CHANGELOG.md +108 -0
- package/build/browser/Client.js +1 -1
- package/build/browser/Client.js.map +1 -1
- package/build/browser/actions/applyAction.js +2 -1
- package/build/browser/actions/applyAction.js.map +1 -1
- package/build/browser/derivedProperties/createWithPropertiesObjectSet.test.js +101 -0
- package/build/browser/derivedProperties/createWithPropertiesObjectSet.test.js.map +1 -1
- package/build/browser/derivedProperties/derivedPropertyDefinitionFactory.js +1 -1
- package/build/browser/derivedProperties/derivedPropertyDefinitionFactory.js.map +1 -1
- package/build/browser/internal/conversions/modernToLegacyWhereClause.js +1 -1
- package/build/browser/internal/conversions/modernToLegacyWhereClause.js.map +1 -1
- package/build/browser/objectSet/ObjectSetListenerWebsocket.js +2 -2
- package/build/browser/objectSet/ObjectSetListenerWebsocket.js.map +1 -1
- package/build/browser/objectSet/createObjectSet.js +0 -4
- package/build/browser/objectSet/createObjectSet.js.map +1 -1
- package/build/browser/observable/ObservableClient/ObserveLink.js.map +1 -1
- package/build/browser/observable/ObservableClient.js +11 -0
- package/build/browser/observable/ObservableClient.js.map +1 -1
- package/build/browser/observable/internal/AbstractHelper.js +56 -3
- package/build/browser/observable/internal/AbstractHelper.js.map +1 -1
- package/build/browser/observable/internal/AbstractHelper.test.js +114 -0
- package/build/browser/observable/internal/AbstractHelper.test.js.map +1 -0
- package/build/browser/observable/internal/BulkObjectLoader.js +71 -9
- package/build/browser/observable/internal/BulkObjectLoader.js.map +1 -1
- package/build/browser/observable/internal/BulkObjectLoader.test.js +79 -0
- package/build/browser/observable/internal/BulkObjectLoader.test.js.map +1 -1
- package/build/browser/observable/internal/CacheKeys.js +19 -8
- package/build/browser/observable/internal/CacheKeys.js.map +1 -1
- package/build/browser/observable/internal/ListQueryView.js +120 -0
- package/build/browser/observable/internal/ListQueryView.js.map +1 -0
- package/build/browser/observable/internal/ObservableClientImpl.js +136 -24
- package/build/browser/observable/internal/ObservableClientImpl.js.map +1 -1
- package/build/browser/observable/internal/PivotCanonicalizer.js +4 -4
- package/build/browser/observable/internal/PivotCanonicalizer.js.map +1 -1
- package/build/browser/observable/internal/Query.js +7 -5
- package/build/browser/observable/internal/Query.js.map +1 -1
- package/build/browser/observable/internal/RdpCanonicalizer.test.js +23 -7
- package/build/browser/observable/internal/RdpCanonicalizer.test.js.map +1 -1
- package/build/browser/observable/internal/Store.js +19 -2
- package/build/browser/observable/internal/Store.js.map +1 -1
- package/build/browser/observable/internal/Store.test.js +327 -1
- package/build/browser/observable/internal/Store.test.js.map +1 -1
- package/build/browser/observable/internal/actions/ActionApplication.js +8 -6
- package/build/browser/observable/internal/actions/ActionApplication.js.map +1 -1
- package/build/browser/observable/internal/aggregation/AggregationCacheKey.js +5 -3
- package/build/browser/observable/internal/aggregation/AggregationCacheKey.js.map +1 -1
- package/build/browser/observable/internal/aggregation/AggregationQuery.js +28 -2
- package/build/browser/observable/internal/aggregation/AggregationQuery.js.map +1 -1
- package/build/browser/observable/internal/aggregation/AggregationsHelper.js +18 -2
- package/build/browser/observable/internal/aggregation/AggregationsHelper.js.map +1 -1
- package/build/browser/observable/internal/aggregation/AggregationsHelper.test.js +103 -0
- package/build/browser/observable/internal/aggregation/AggregationsHelper.test.js.map +1 -0
- package/build/browser/observable/internal/aggregation/ObjectAggregationQuery.js +22 -3
- package/build/browser/observable/internal/aggregation/ObjectAggregationQuery.js.map +1 -1
- package/build/browser/observable/internal/base-list/BaseListQuery.js +73 -6
- package/build/browser/observable/internal/base-list/BaseListQuery.js.map +1 -1
- package/build/browser/observable/internal/function/FunctionQuery.js +27 -1
- package/build/browser/observable/internal/function/FunctionQuery.js.map +1 -1
- package/build/browser/observable/internal/function/FunctionsHelper.js +2 -1
- package/build/browser/observable/internal/function/FunctionsHelper.js.map +1 -1
- package/build/browser/observable/internal/getObjectTypesThatInvalidate.js +2 -2
- package/build/browser/observable/internal/getObjectTypesThatInvalidate.js.map +1 -1
- package/build/browser/observable/internal/links/LinksHelper.js +3 -2
- package/build/browser/observable/internal/links/LinksHelper.js.map +1 -1
- package/build/browser/observable/internal/links/SpecificLinkCacheKey.js +7 -5
- package/build/browser/observable/internal/links/SpecificLinkCacheKey.js.map +1 -1
- package/build/browser/observable/internal/links/SpecificLinkQuery.js +97 -62
- package/build/browser/observable/internal/links/SpecificLinkQuery.js.map +1 -1
- package/build/browser/observable/internal/list/InterfaceListQuery.js +19 -1
- package/build/browser/observable/internal/list/InterfaceListQuery.js.map +1 -1
- package/build/browser/observable/internal/list/ListQuery.js +30 -10
- package/build/browser/observable/internal/list/ListQuery.js.map +1 -1
- package/build/browser/observable/internal/list/ListQuery.test.js +262 -1
- package/build/browser/observable/internal/list/ListQuery.test.js.map +1 -1
- package/build/browser/observable/internal/list/ListsHelper.js +1 -1
- package/build/browser/observable/internal/list/ListsHelper.js.map +1 -1
- package/build/browser/observable/internal/list/ObjectListQuery.js +22 -21
- package/build/browser/observable/internal/list/ObjectListQuery.js.map +1 -1
- package/build/browser/observable/internal/object/ObjectQuery.js +23 -6
- package/build/browser/observable/internal/object/ObjectQuery.js.map +1 -1
- package/build/browser/observable/internal/object/ObjectsHelper.js +12 -3
- package/build/browser/observable/internal/object/ObjectsHelper.js.map +1 -1
- package/build/browser/observable/internal/object/ObjectsHelper.test.js +200 -0
- package/build/browser/observable/internal/object/ObjectsHelper.test.js.map +1 -0
- package/build/browser/observable/internal/objectset/ObjectSetCacheKey.js.map +1 -1
- package/build/browser/observable/internal/objectset/ObjectSetHelper.js +3 -2
- package/build/browser/observable/internal/objectset/ObjectSetHelper.js.map +1 -1
- package/build/browser/observable/internal/objectset/ObjectSetHelper.test.js +94 -0
- package/build/browser/observable/internal/objectset/ObjectSetHelper.test.js.map +1 -0
- package/build/browser/observable/internal/objectset/ObjectSetQuery.js +4 -1
- package/build/browser/observable/internal/objectset/ObjectSetQuery.js.map +1 -1
- package/build/browser/observable/internal/testUtils/observeLink/expectStandardObserveLink.js +1 -0
- package/build/browser/observable/internal/testUtils/observeLink/expectStandardObserveLink.js.map +1 -1
- package/build/browser/observable/internal/testUtils.js.map +1 -1
- package/build/browser/observable/internal/utils/rdpFieldOperations.js +7 -2
- package/build/browser/observable/internal/utils/rdpFieldOperations.js.map +1 -1
- package/build/browser/observable/internal/utils/rdpFieldOperations.test.js +55 -0
- package/build/browser/observable/internal/utils/rdpFieldOperations.test.js.map +1 -1
- package/build/browser/public/unstable-do-not-use.js +1 -0
- package/build/browser/public/unstable-do-not-use.js.map +1 -1
- package/build/browser/util/UserAgent.js +2 -2
- package/build/browser/util/UserAgent.js.map +1 -1
- package/build/browser/util/interfaceUtils.js +7 -0
- package/build/browser/util/interfaceUtils.js.map +1 -1
- package/build/cjs/{chunk-7543GRIE.cjs → chunk-6VOFZIIJ.cjs} +79 -69
- package/build/cjs/chunk-6VOFZIIJ.cjs.map +1 -0
- package/build/cjs/{chunk-VVKEXIIO.cjs → chunk-YVZM2JPW.cjs} +164 -155
- package/build/cjs/chunk-YVZM2JPW.cjs.map +1 -0
- package/build/cjs/index.cjs +8 -8
- package/build/cjs/public/internal.cjs +8 -8
- package/build/cjs/public/unstable-do-not-use.cjs +928 -361
- package/build/cjs/public/unstable-do-not-use.cjs.map +1 -1
- package/build/cjs/public/unstable-do-not-use.d.cts +64 -26
- package/build/esm/Client.js +1 -1
- package/build/esm/Client.js.map +1 -1
- package/build/esm/actions/applyAction.js +2 -1
- package/build/esm/actions/applyAction.js.map +1 -1
- package/build/esm/derivedProperties/createWithPropertiesObjectSet.test.js +101 -0
- package/build/esm/derivedProperties/createWithPropertiesObjectSet.test.js.map +1 -1
- package/build/esm/derivedProperties/derivedPropertyDefinitionFactory.js +1 -1
- package/build/esm/derivedProperties/derivedPropertyDefinitionFactory.js.map +1 -1
- package/build/esm/internal/conversions/modernToLegacyWhereClause.js +1 -1
- package/build/esm/internal/conversions/modernToLegacyWhereClause.js.map +1 -1
- package/build/esm/objectSet/ObjectSetListenerWebsocket.js +2 -2
- package/build/esm/objectSet/ObjectSetListenerWebsocket.js.map +1 -1
- package/build/esm/objectSet/createObjectSet.js +0 -4
- package/build/esm/objectSet/createObjectSet.js.map +1 -1
- package/build/esm/observable/ObservableClient/ObserveLink.js.map +1 -1
- package/build/esm/observable/ObservableClient.js +11 -0
- package/build/esm/observable/ObservableClient.js.map +1 -1
- package/build/esm/observable/internal/AbstractHelper.js +56 -3
- package/build/esm/observable/internal/AbstractHelper.js.map +1 -1
- package/build/esm/observable/internal/AbstractHelper.test.js +114 -0
- package/build/esm/observable/internal/AbstractHelper.test.js.map +1 -0
- package/build/esm/observable/internal/BulkObjectLoader.js +71 -9
- package/build/esm/observable/internal/BulkObjectLoader.js.map +1 -1
- package/build/esm/observable/internal/BulkObjectLoader.test.js +79 -0
- package/build/esm/observable/internal/BulkObjectLoader.test.js.map +1 -1
- package/build/esm/observable/internal/CacheKeys.js +19 -8
- package/build/esm/observable/internal/CacheKeys.js.map +1 -1
- package/build/esm/observable/internal/ListQueryView.js +120 -0
- package/build/esm/observable/internal/ListQueryView.js.map +1 -0
- package/build/esm/observable/internal/ObservableClientImpl.js +136 -24
- package/build/esm/observable/internal/ObservableClientImpl.js.map +1 -1
- package/build/esm/observable/internal/PivotCanonicalizer.js +4 -4
- package/build/esm/observable/internal/PivotCanonicalizer.js.map +1 -1
- package/build/esm/observable/internal/Query.js +7 -5
- package/build/esm/observable/internal/Query.js.map +1 -1
- package/build/esm/observable/internal/RdpCanonicalizer.test.js +23 -7
- package/build/esm/observable/internal/RdpCanonicalizer.test.js.map +1 -1
- package/build/esm/observable/internal/Store.js +19 -2
- package/build/esm/observable/internal/Store.js.map +1 -1
- package/build/esm/observable/internal/Store.test.js +327 -1
- package/build/esm/observable/internal/Store.test.js.map +1 -1
- package/build/esm/observable/internal/actions/ActionApplication.js +8 -6
- package/build/esm/observable/internal/actions/ActionApplication.js.map +1 -1
- package/build/esm/observable/internal/aggregation/AggregationCacheKey.js +5 -3
- package/build/esm/observable/internal/aggregation/AggregationCacheKey.js.map +1 -1
- package/build/esm/observable/internal/aggregation/AggregationQuery.js +28 -2
- package/build/esm/observable/internal/aggregation/AggregationQuery.js.map +1 -1
- package/build/esm/observable/internal/aggregation/AggregationsHelper.js +18 -2
- package/build/esm/observable/internal/aggregation/AggregationsHelper.js.map +1 -1
- package/build/esm/observable/internal/aggregation/AggregationsHelper.test.js +103 -0
- package/build/esm/observable/internal/aggregation/AggregationsHelper.test.js.map +1 -0
- package/build/esm/observable/internal/aggregation/ObjectAggregationQuery.js +22 -3
- package/build/esm/observable/internal/aggregation/ObjectAggregationQuery.js.map +1 -1
- package/build/esm/observable/internal/base-list/BaseListQuery.js +73 -6
- package/build/esm/observable/internal/base-list/BaseListQuery.js.map +1 -1
- package/build/esm/observable/internal/function/FunctionQuery.js +27 -1
- package/build/esm/observable/internal/function/FunctionQuery.js.map +1 -1
- package/build/esm/observable/internal/function/FunctionsHelper.js +2 -1
- package/build/esm/observable/internal/function/FunctionsHelper.js.map +1 -1
- package/build/esm/observable/internal/getObjectTypesThatInvalidate.js +2 -2
- package/build/esm/observable/internal/getObjectTypesThatInvalidate.js.map +1 -1
- package/build/esm/observable/internal/links/LinksHelper.js +3 -2
- package/build/esm/observable/internal/links/LinksHelper.js.map +1 -1
- package/build/esm/observable/internal/links/SpecificLinkCacheKey.js +7 -5
- package/build/esm/observable/internal/links/SpecificLinkCacheKey.js.map +1 -1
- package/build/esm/observable/internal/links/SpecificLinkQuery.js +97 -62
- package/build/esm/observable/internal/links/SpecificLinkQuery.js.map +1 -1
- package/build/esm/observable/internal/list/InterfaceListQuery.js +19 -1
- package/build/esm/observable/internal/list/InterfaceListQuery.js.map +1 -1
- package/build/esm/observable/internal/list/ListQuery.js +30 -10
- package/build/esm/observable/internal/list/ListQuery.js.map +1 -1
- package/build/esm/observable/internal/list/ListQuery.test.js +262 -1
- package/build/esm/observable/internal/list/ListQuery.test.js.map +1 -1
- package/build/esm/observable/internal/list/ListsHelper.js +1 -1
- package/build/esm/observable/internal/list/ListsHelper.js.map +1 -1
- package/build/esm/observable/internal/list/ObjectListQuery.js +22 -21
- package/build/esm/observable/internal/list/ObjectListQuery.js.map +1 -1
- package/build/esm/observable/internal/object/ObjectQuery.js +23 -6
- package/build/esm/observable/internal/object/ObjectQuery.js.map +1 -1
- package/build/esm/observable/internal/object/ObjectsHelper.js +12 -3
- package/build/esm/observable/internal/object/ObjectsHelper.js.map +1 -1
- package/build/esm/observable/internal/object/ObjectsHelper.test.js +200 -0
- package/build/esm/observable/internal/object/ObjectsHelper.test.js.map +1 -0
- package/build/esm/observable/internal/objectset/ObjectSetCacheKey.js.map +1 -1
- package/build/esm/observable/internal/objectset/ObjectSetHelper.js +3 -2
- package/build/esm/observable/internal/objectset/ObjectSetHelper.js.map +1 -1
- package/build/esm/observable/internal/objectset/ObjectSetHelper.test.js +94 -0
- package/build/esm/observable/internal/objectset/ObjectSetHelper.test.js.map +1 -0
- package/build/esm/observable/internal/objectset/ObjectSetQuery.js +4 -1
- package/build/esm/observable/internal/objectset/ObjectSetQuery.js.map +1 -1
- package/build/esm/observable/internal/testUtils/observeLink/expectStandardObserveLink.js +1 -0
- package/build/esm/observable/internal/testUtils/observeLink/expectStandardObserveLink.js.map +1 -1
- package/build/esm/observable/internal/testUtils.js.map +1 -1
- package/build/esm/observable/internal/utils/rdpFieldOperations.js +7 -2
- package/build/esm/observable/internal/utils/rdpFieldOperations.js.map +1 -1
- package/build/esm/observable/internal/utils/rdpFieldOperations.test.js +55 -0
- package/build/esm/observable/internal/utils/rdpFieldOperations.test.js.map +1 -1
- package/build/esm/public/unstable-do-not-use.js +1 -0
- package/build/esm/public/unstable-do-not-use.js.map +1 -1
- package/build/esm/util/UserAgent.js +2 -2
- package/build/esm/util/UserAgent.js.map +1 -1
- package/build/esm/util/interfaceUtils.js +7 -0
- package/build/esm/util/interfaceUtils.js.map +1 -1
- package/build/types/Client.d.ts +1 -1
- package/build/types/objectSet/createObjectSet.d.ts +5 -1
- package/build/types/objectSet/createObjectSet.d.ts.map +1 -1
- package/build/types/observable/ObservableClient/ObserveLink.d.ts +2 -1
- package/build/types/observable/ObservableClient/ObserveLink.d.ts.map +1 -1
- package/build/types/observable/ObservableClient.d.ts +71 -25
- package/build/types/observable/ObservableClient.d.ts.map +1 -1
- package/build/types/observable/internal/AbstractHelper.d.ts +1 -1
- package/build/types/observable/internal/AbstractHelper.d.ts.map +1 -1
- package/build/types/observable/internal/AbstractHelper.test.d.ts +1 -0
- package/build/types/observable/internal/AbstractHelper.test.d.ts.map +1 -0
- package/build/types/observable/internal/BulkObjectLoader.d.ts +2 -1
- package/build/types/observable/internal/BulkObjectLoader.d.ts.map +1 -1
- package/build/types/observable/internal/CacheKeys.d.ts +5 -0
- package/build/types/observable/internal/CacheKeys.d.ts.map +1 -1
- package/build/types/observable/internal/ListQueryView.d.ts +21 -0
- package/build/types/observable/internal/ListQueryView.d.ts.map +1 -0
- package/build/types/observable/internal/PivotCanonicalizer.d.ts +2 -2
- package/build/types/observable/internal/PivotCanonicalizer.d.ts.map +1 -1
- package/build/types/observable/internal/Query.d.ts.map +1 -1
- package/build/types/observable/internal/Store.d.ts.map +1 -1
- package/build/types/observable/internal/Store.test.d.ts.map +1 -1
- package/build/types/observable/internal/actions/ActionApplication.d.ts.map +1 -1
- package/build/types/observable/internal/aggregation/AggregationCacheKey.d.ts +6 -4
- package/build/types/observable/internal/aggregation/AggregationCacheKey.d.ts.map +1 -1
- package/build/types/observable/internal/aggregation/AggregationQuery.d.ts +8 -3
- package/build/types/observable/internal/aggregation/AggregationQuery.d.ts.map +1 -1
- package/build/types/observable/internal/aggregation/AggregationsHelper.d.ts +19 -14
- package/build/types/observable/internal/aggregation/AggregationsHelper.d.ts.map +1 -1
- package/build/types/observable/internal/aggregation/AggregationsHelper.test.d.ts +1 -0
- package/build/types/observable/internal/aggregation/AggregationsHelper.test.d.ts.map +1 -0
- package/build/types/observable/internal/aggregation/ObjectAggregationQuery.d.ts.map +1 -1
- package/build/types/observable/internal/base-list/BaseListQuery.d.ts +47 -2
- package/build/types/observable/internal/base-list/BaseListQuery.d.ts.map +1 -1
- package/build/types/observable/internal/function/FunctionQuery.d.ts +1 -1
- package/build/types/observable/internal/function/FunctionQuery.d.ts.map +1 -1
- package/build/types/observable/internal/function/FunctionsHelper.d.ts +1 -0
- package/build/types/observable/internal/function/FunctionsHelper.d.ts.map +1 -1
- package/build/types/observable/internal/links/LinksHelper.d.ts +5 -5
- package/build/types/observable/internal/links/LinksHelper.d.ts.map +1 -1
- package/build/types/observable/internal/links/SpecificLinkCacheKey.d.ts +8 -6
- package/build/types/observable/internal/links/SpecificLinkCacheKey.d.ts.map +1 -1
- package/build/types/observable/internal/links/SpecificLinkQuery.d.ts +3 -4
- package/build/types/observable/internal/links/SpecificLinkQuery.d.ts.map +1 -1
- package/build/types/observable/internal/list/InterfaceListQuery.d.ts.map +1 -1
- package/build/types/observable/internal/list/ListQuery.d.ts +0 -3
- package/build/types/observable/internal/list/ListQuery.d.ts.map +1 -1
- package/build/types/observable/internal/list/ListsHelper.d.ts +4 -4
- package/build/types/observable/internal/list/ListsHelper.d.ts.map +1 -1
- package/build/types/observable/internal/list/ObjectListQuery.d.ts.map +1 -1
- package/build/types/observable/internal/object/ObjectQuery.d.ts +2 -1
- package/build/types/observable/internal/object/ObjectQuery.d.ts.map +1 -1
- package/build/types/observable/internal/object/ObjectsHelper.d.ts +8 -4
- package/build/types/observable/internal/object/ObjectsHelper.d.ts.map +1 -1
- package/build/types/observable/internal/object/ObjectsHelper.test.d.ts +1 -0
- package/build/types/observable/internal/object/ObjectsHelper.test.d.ts.map +1 -0
- package/build/types/observable/internal/objectset/ObjectSetCacheKey.d.ts +2 -1
- package/build/types/observable/internal/objectset/ObjectSetCacheKey.d.ts.map +1 -1
- package/build/types/observable/internal/objectset/ObjectSetHelper.d.ts +3 -1
- package/build/types/observable/internal/objectset/ObjectSetHelper.d.ts.map +1 -1
- package/build/types/observable/internal/objectset/ObjectSetHelper.test.d.ts +1 -0
- package/build/types/observable/internal/objectset/ObjectSetHelper.test.d.ts.map +1 -0
- package/build/types/observable/internal/objectset/ObjectSetQuery.d.ts +2 -0
- package/build/types/observable/internal/objectset/ObjectSetQuery.d.ts.map +1 -1
- package/build/types/observable/internal/testUtils.d.ts +2 -2
- package/build/types/observable/internal/testUtils.d.ts.map +1 -1
- package/build/types/public/unstable-do-not-use.d.ts +1 -0
- package/build/types/public/unstable-do-not-use.d.ts.map +1 -1
- package/build/types/util/interfaceUtils.d.ts +2 -1
- package/build/types/util/interfaceUtils.d.ts.map +1 -1
- package/package.json +12 -12
- package/build/cjs/chunk-7543GRIE.cjs.map +0 -1
- package/build/cjs/chunk-VVKEXIIO.cjs.map +0 -1
- package/build/cjs/delay-W2TSML2P.cjs +0 -75
- package/build/cjs/delay-W2TSML2P.cjs.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AbstractHelper.js","names":["QuerySubscription","AbstractHelper","constructor","store","cacheKeys","observe","options","subFn","query","getQuery","_subscribe","retain","cacheKey","mode","revalidate","catch","e","error","logger","sub","subscribe","querySub","registerSubscriptionDedupeInterval","subscriptionId","dedupeInterval","add","unregisterSubscriptionDedupeInterval","release"],"sources":["AbstractHelper.ts"],"sourcesContent":["/*\n * Copyright 2025 Palantir Technologies, Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type {\n CommonObserveOptions,\n Observer,\n} from \"../ObservableClient/common.js\";\nimport type { CacheKeys } from \"./CacheKeys.js\";\nimport type { KnownCacheKey } from \"./KnownCacheKey.js\";\nimport type { Query } from \"./Query.js\";\nimport { QuerySubscription } from \"./QuerySubscription.js\";\nimport type { Store } from \"./Store.js\";\n\nexport abstract class AbstractHelper<\n TQuery extends Query<KnownCacheKey, any, CommonObserveOptions>,\n TObserveOptions extends CommonObserveOptions,\n> {\n protected readonly store: Store;\n protected readonly cacheKeys: CacheKeys<KnownCacheKey>;\n\n constructor(store: Store, cacheKeys: CacheKeys<KnownCacheKey>) {\n this.store = store;\n this.cacheKeys = cacheKeys;\n }\n\n observe(\n options: TObserveOptions,\n subFn: Observer<\n TQuery extends Query<any, infer PAYLOAD, any> ? PAYLOAD : never\n >,\n ): QuerySubscription<TQuery> {\n const query = this.getQuery(options);\n return this._subscribe(query, options, subFn);\n }\n\n abstract getQuery(options: TObserveOptions): TQuery;\n\n protected _subscribe(\n query: TQuery,\n options: TObserveOptions,\n subFn: Observer<\n TQuery extends Query<any, infer PAYLOAD, any> ? PAYLOAD : never\n >,\n ): QuerySubscription<TQuery> {\n // the ListQuery represents the shared state of the list\n this.store.cacheKeys.retain(query.cacheKey);\n\n if (options.mode !== \"offline\") {\n query.revalidate(options.mode === \"force\").catch((e: unknown) => {\n subFn.error(e);\n\n // we don't want observeObject() to return a promise,\n // so we settle for logging an error here instead of\n // dropping it on the floor.\n if (this.store.logger) {\n this.store.logger.error(\"Unhandled error in observeObject\", e);\n } else {\n throw e;\n }\n });\n }\n\n const sub = query.subscribe(subFn);\n const querySub = new QuerySubscription(query, sub);\n\n query.registerSubscriptionDedupeInterval(\n querySub.subscriptionId,\n (options as CommonObserveOptions).dedupeInterval,\n );\n\n sub.add(() => {\n query.unregisterSubscriptionDedupeInterval(querySub.subscriptionId);\n this.store.cacheKeys.release(query.cacheKey);\n });\n\n return querySub;\n }\n}\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AASA,SAASA,iBAAiB,QAAQ,wBAAwB;AAG1D,OAAO,MAAeC,cAAc,CAGlC;EAIAC,WAAWA,CAACC,KAAY,EAAEC,SAAmC,EAAE;IAC7D,IAAI,CAACD,KAAK,GAAGA,KAAK;IAClB,IAAI,CAACC,SAAS,GAAGA,SAAS;EAC5B;EAEAC,OAAOA,CACLC,OAAwB,EACxBC,KAEC,EAC0B;IAC3B,MAAMC,KAAK,GAAG,IAAI,CAACC,QAAQ,CAACH,OAAO,CAAC;IACpC,OAAO,IAAI,CAACI,UAAU,CAACF,KAAK,EAAEF,OAAO,EAAEC,KAAK,CAAC;EAC/C;EAIUG,UAAUA,CAClBF,KAAa,EACbF,OAAwB,EACxBC,KAEC,EAC0B;IAC3B;IACA,IAAI,CAACJ,KAAK,CAACC,SAAS,CAACO,MAAM,CAACH,KAAK,CAACI,QAAQ,CAAC;IAE3C,IAAIN,OAAO,CAACO,IAAI,KAAK,SAAS,EAAE;MAC9BL,KAAK,CAACM,UAAU,CAACR,OAAO,CAACO,IAAI,KAAK,OAAO,CAAC,CAACE,KAAK,CAAEC,CAAU,IAAK;QAC/DT,KAAK,CAACU,KAAK,CAACD,CAAC,CAAC;;QAEd;QACA;QACA;QACA,IAAI,IAAI,CAACb,KAAK,CAACe,MAAM,EAAE;UACrB,IAAI,CAACf,KAAK,CAACe,MAAM,CAACD,KAAK,CAAC,kCAAkC,EAAED,CAAC,CAAC;QAChE,CAAC,MAAM;UACL,MAAMA,CAAC;QACT;MACF,CAAC,CAAC;IACJ;IAEA,MAAMG,GAAG,GAAGX,KAAK,CAACY,SAAS,CAACb,KAAK,CAAC;IAClC,MAAMc,QAAQ,GAAG,IAAIrB,iBAAiB,CAACQ,KAAK,EAAEW,GAAG,CAAC;IAElDX,KAAK,CAACc,kCAAkC,CACtCD,QAAQ,CAACE,cAAc,EACtBjB,OAAO,CAA0BkB,cACpC,CAAC;IAEDL,GAAG,CAACM,GAAG,CAAC,MAAM;MACZjB,KAAK,CAACkB,oCAAoC,CAACL,QAAQ,CAACE,cAAc,CAAC;MACnE,IAAI,CAACpB,KAAK,CAACC,SAAS,CAACuB,OAAO,CAACnB,KAAK,CAACI,QAAQ,CAAC;IAC9C,CAAC,CAAC;IAEF,OAAOS,QAAQ;EACjB;AACF","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"AbstractHelper.js","names":["ListQueryView","QuerySubscription","supportsViews","query","registerFetchPageSize","getLoadedCount","hasMorePages","notifySubscribers","fetchMore","AbstractHelper","constructor","store","cacheKeys","observe","options","subFn","getQuery","_subscribe","pendingCleanupCount","pendingCleanup","get","cacheKey","delete","set","retain","mode","revalidate","catch","e","error","logger","listOptions","useView","pageSize","undefined","autoFetchMore","sub","subscribe","querySub","registerSubscriptionDedupeInterval","subscriptionId","dedupeInterval","add","unregisterSubscriptionDedupeInterval","queueMicrotask","currentPending","release"],"sources":["AbstractHelper.ts"],"sourcesContent":["/*\n * Copyright 2025 Palantir Technologies, Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type {\n CommonObserveOptions,\n Observer,\n} from \"../ObservableClient/common.js\";\nimport type { BaseListPayloadShape } from \"./base-list/BaseListQuery.js\";\nimport type { CacheKeys } from \"./CacheKeys.js\";\nimport type { KnownCacheKey } from \"./KnownCacheKey.js\";\nimport { ListQueryView, type ListQueryViewTarget } from \"./ListQueryView.js\";\nimport type { Query } from \"./Query.js\";\nimport { QuerySubscription } from \"./QuerySubscription.js\";\nimport type { Store } from \"./Store.js\";\n\n/**\n * Check if a query supports view-based pagination (has the required methods).\n * Generic over PAYLOAD to preserve type information when the guard passes.\n */\nfunction supportsViews<PAYLOAD extends BaseListPayloadShape>(\n query: unknown,\n): query is ListQueryViewTarget<PAYLOAD> {\n return (\n query != null\n && typeof (query as ListQueryViewTarget<PAYLOAD>).registerFetchPageSize\n === \"function\"\n && typeof (query as ListQueryViewTarget<PAYLOAD>).getLoadedCount\n === \"function\"\n && typeof (query as ListQueryViewTarget<PAYLOAD>).hasMorePages\n === \"function\"\n && typeof (query as ListQueryViewTarget<PAYLOAD>).notifySubscribers\n === \"function\"\n && typeof (query as ListQueryViewTarget<PAYLOAD>).fetchMore === \"function\"\n );\n}\n\n/**\n * Options that may include list-specific pagination settings.\n */\ninterface ListObserveOptions {\n pageSize?: number;\n autoFetchMore?: boolean | number;\n}\n\nexport abstract class AbstractHelper<\n TQuery extends Query<KnownCacheKey, any, CommonObserveOptions>,\n TObserveOptions extends CommonObserveOptions,\n> {\n protected readonly store: Store;\n protected readonly cacheKeys: CacheKeys<KnownCacheKey>;\n\n constructor(store: Store, cacheKeys: CacheKeys<KnownCacheKey>) {\n this.store = store;\n this.cacheKeys = cacheKeys;\n }\n\n observe(\n options: TObserveOptions,\n subFn: Observer<\n TQuery extends Query<any, infer PAYLOAD, any> ? PAYLOAD : never\n >,\n ): QuerySubscription<TQuery> {\n const query = this.getQuery(options);\n return this._subscribe(query, options, subFn);\n }\n\n abstract getQuery(options: TObserveOptions): TQuery;\n\n protected _subscribe<\n PAYLOAD extends (TQuery extends Query<any, infer P, any> ? P : never),\n >(\n query: TQuery,\n options: TObserveOptions,\n subFn: Observer<PAYLOAD>,\n ): QuerySubscription<TQuery> {\n // the ListQuery represents the shared state of the list\n // If there is a deferred release pending for this key (from a prior\n // unmount), cancel exactly one pending release and avoid an extra retain.\n // This keeps refcounts balanced during unmount→remount within the same tick\n // (e.g. React StrictMode effect cleanup + re-run).\n const pendingCleanupCount = this.store.pendingCleanup.get(query.cacheKey)\n ?? 0;\n if (pendingCleanupCount > 0) {\n if (pendingCleanupCount === 1) {\n this.store.pendingCleanup.delete(query.cacheKey);\n } else {\n this.store.pendingCleanup.set(\n query.cacheKey,\n pendingCleanupCount - 1,\n );\n }\n } else {\n this.store.cacheKeys.retain(query.cacheKey);\n }\n\n if (options.mode !== \"offline\") {\n query.revalidate(options.mode === \"force\").catch((e: unknown) => {\n subFn.error(e);\n\n // we don't want observeObject() to return a promise,\n // so we settle for logging an error here instead of\n // dropping it on the floor.\n if (this.store.logger) {\n this.store.logger.error(\"Unhandled error in observeObject\", e);\n } else {\n throw e;\n }\n });\n }\n\n // For queries that support views (list-like queries), wrap with ListQueryView\n // to handle per-subscriber view data such as pageSize\n const listOptions = options as ListObserveOptions;\n const useView = supportsViews<PAYLOAD & BaseListPayloadShape>(query)\n && (listOptions.pageSize !== undefined\n || listOptions.autoFetchMore !== undefined);\n\n const sub = useView\n ? new ListQueryView<PAYLOAD & BaseListPayloadShape>(\n query,\n listOptions.pageSize ?? 100,\n listOptions.autoFetchMore,\n ).subscribe(subFn as Observer<PAYLOAD & BaseListPayloadShape>)\n : query.subscribe(subFn);\n\n const querySub = new QuerySubscription(query, sub);\n\n query.registerSubscriptionDedupeInterval(\n querySub.subscriptionId,\n options.dedupeInterval,\n );\n\n sub.add(() => {\n query.unregisterSubscriptionDedupeInterval(querySub.subscriptionId);\n\n // Defer the release to the next microtask so React unmount-remount\n // cycles can re-subscribe before the cache key is released.\n // This prevents propagateWrite from skipping keys that are\n // momentarily between subscriptions.\n //\n // Note: microtask ordering is only guaranteed within a single\n // queueMicrotask call, not across separate invocations. If\n // additional microtasks are introduced that interact with\n // pendingCleanup or cacheKeys, ensure they don't rely on\n // running before or after this one.\n this.store.pendingCleanup.set(\n query.cacheKey,\n (this.store.pendingCleanup.get(query.cacheKey) ?? 0) + 1,\n );\n queueMicrotask(() => {\n const currentPending = this.store.pendingCleanup.get(query.cacheKey)\n ?? 0;\n if (currentPending > 0) {\n if (currentPending === 1) {\n this.store.pendingCleanup.delete(query.cacheKey);\n } else {\n this.store.pendingCleanup.set(query.cacheKey, currentPending - 1);\n }\n this.store.cacheKeys.release(query.cacheKey);\n }\n });\n });\n\n return querySub;\n }\n}\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AASA,SAASA,aAAa,QAAkC,oBAAoB;AAE5E,SAASC,iBAAiB,QAAQ,wBAAwB;AAG1D;AACA;AACA;AACA;AACA,SAASC,aAAaA,CACpBC,KAAc,EACyB;EACvC,OACEA,KAAK,IAAI,IAAI,IACV,OAAQA,KAAK,CAAkCC,qBAAqB,KACjE,UAAU,IACb,OAAQD,KAAK,CAAkCE,cAAc,KAC1D,UAAU,IACb,OAAQF,KAAK,CAAkCG,YAAY,KACxD,UAAU,IACb,OAAQH,KAAK,CAAkCI,iBAAiB,KAC7D,UAAU,IACb,OAAQJ,KAAK,CAAkCK,SAAS,KAAK,UAAU;AAE9E;;AAEA;AACA;AACA;;AAMA,OAAO,MAAeC,cAAc,CAGlC;EAIAC,WAAWA,CAACC,KAAY,EAAEC,SAAmC,EAAE;IAC7D,IAAI,CAACD,KAAK,GAAGA,KAAK;IAClB,IAAI,CAACC,SAAS,GAAGA,SAAS;EAC5B;EAEAC,OAAOA,CACLC,OAAwB,EACxBC,KAEC,EAC0B;IAC3B,MAAMZ,KAAK,GAAG,IAAI,CAACa,QAAQ,CAACF,OAAO,CAAC;IACpC,OAAO,IAAI,CAACG,UAAU,CAACd,KAAK,EAAEW,OAAO,EAAEC,KAAK,CAAC;EAC/C;EAIUE,UAAUA,CAGlBd,KAAa,EACbW,OAAwB,EACxBC,KAAwB,EACG;IAC3B;IACA;IACA;IACA;IACA;IACA,MAAMG,mBAAmB,GAAG,IAAI,CAACP,KAAK,CAACQ,cAAc,CAACC,GAAG,CAACjB,KAAK,CAACkB,QAAQ,CAAC,IACpE,CAAC;IACN,IAAIH,mBAAmB,GAAG,CAAC,EAAE;MAC3B,IAAIA,mBAAmB,KAAK,CAAC,EAAE;QAC7B,IAAI,CAACP,KAAK,CAACQ,cAAc,CAACG,MAAM,CAACnB,KAAK,CAACkB,QAAQ,CAAC;MAClD,CAAC,MAAM;QACL,IAAI,CAACV,KAAK,CAACQ,cAAc,CAACI,GAAG,CAC3BpB,KAAK,CAACkB,QAAQ,EACdH,mBAAmB,GAAG,CACxB,CAAC;MACH;IACF,CAAC,MAAM;MACL,IAAI,CAACP,KAAK,CAACC,SAAS,CAACY,MAAM,CAACrB,KAAK,CAACkB,QAAQ,CAAC;IAC7C;IAEA,IAAIP,OAAO,CAACW,IAAI,KAAK,SAAS,EAAE;MAC9BtB,KAAK,CAACuB,UAAU,CAACZ,OAAO,CAACW,IAAI,KAAK,OAAO,CAAC,CAACE,KAAK,CAAEC,CAAU,IAAK;QAC/Db,KAAK,CAACc,KAAK,CAACD,CAAC,CAAC;;QAEd;QACA;QACA;QACA,IAAI,IAAI,CAACjB,KAAK,CAACmB,MAAM,EAAE;UACrB,IAAI,CAACnB,KAAK,CAACmB,MAAM,CAACD,KAAK,CAAC,kCAAkC,EAAED,CAAC,CAAC;QAChE,CAAC,MAAM;UACL,MAAMA,CAAC;QACT;MACF,CAAC,CAAC;IACJ;;IAEA;IACA;IACA,MAAMG,WAAW,GAAGjB,OAA6B;IACjD,MAAMkB,OAAO,GAAG9B,aAAa,CAAiCC,KAAK,CAAC,KAC9D4B,WAAW,CAACE,QAAQ,KAAKC,SAAS,IACjCH,WAAW,CAACI,aAAa,KAAKD,SAAS,CAAC;IAE/C,MAAME,GAAG,GAAGJ,OAAO,GACf,IAAIhC,aAAa,CACjBG,KAAK,EACL4B,WAAW,CAACE,QAAQ,IAAI,GAAG,EAC3BF,WAAW,CAACI,aACd,CAAC,CAACE,SAAS,CAACtB,KAAiD,CAAC,GAC5DZ,KAAK,CAACkC,SAAS,CAACtB,KAAK,CAAC;IAE1B,MAAMuB,QAAQ,GAAG,IAAIrC,iBAAiB,CAACE,KAAK,EAAEiC,GAAG,CAAC;IAElDjC,KAAK,CAACoC,kCAAkC,CACtCD,QAAQ,CAACE,cAAc,EACvB1B,OAAO,CAAC2B,cACV,CAAC;IAEDL,GAAG,CAACM,GAAG,CAAC,MAAM;MACZvC,KAAK,CAACwC,oCAAoC,CAACL,QAAQ,CAACE,cAAc,CAAC;;MAEnE;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA,IAAI,CAAC7B,KAAK,CAACQ,cAAc,CAACI,GAAG,CAC3BpB,KAAK,CAACkB,QAAQ,EACd,CAAC,IAAI,CAACV,KAAK,CAACQ,cAAc,CAACC,GAAG,CAACjB,KAAK,CAACkB,QAAQ,CAAC,IAAI,CAAC,IAAI,CACzD,CAAC;MACDuB,cAAc,CAAC,MAAM;QACnB,MAAMC,cAAc,GAAG,IAAI,CAAClC,KAAK,CAACQ,cAAc,CAACC,GAAG,CAACjB,KAAK,CAACkB,QAAQ,CAAC,IAC/D,CAAC;QACN,IAAIwB,cAAc,GAAG,CAAC,EAAE;UACtB,IAAIA,cAAc,KAAK,CAAC,EAAE;YACxB,IAAI,CAAClC,KAAK,CAACQ,cAAc,CAACG,MAAM,CAACnB,KAAK,CAACkB,QAAQ,CAAC;UAClD,CAAC,MAAM;YACL,IAAI,CAACV,KAAK,CAACQ,cAAc,CAACI,GAAG,CAACpB,KAAK,CAACkB,QAAQ,EAAEwB,cAAc,GAAG,CAAC,CAAC;UACnE;UACA,IAAI,CAAClC,KAAK,CAACC,SAAS,CAACkC,OAAO,CAAC3C,KAAK,CAACkB,QAAQ,CAAC;QAC9C;MACF,CAAC,CAAC;IACJ,CAAC,CAAC;IAEF,OAAOiB,QAAQ;EACjB;AACF","ignoreList":[]}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2026 Palantir Technologies, Inc. All rights reserved.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { Subscription } from "rxjs";
|
|
18
|
+
import { describe, expect, it, vi } from "vitest";
|
|
19
|
+
import { AbstractHelper } from "./AbstractHelper.js";
|
|
20
|
+
function flushMicrotasks() {
|
|
21
|
+
return new Promise(resolve => queueMicrotask(resolve));
|
|
22
|
+
}
|
|
23
|
+
describe("AbstractHelper pending cleanup", () => {
|
|
24
|
+
it("coalesces unsubscribe→resubscribe within the same tick", async () => {
|
|
25
|
+
const retain = vi.fn();
|
|
26
|
+
const release = vi.fn();
|
|
27
|
+
const store = {
|
|
28
|
+
cacheKeys: {
|
|
29
|
+
retain,
|
|
30
|
+
release
|
|
31
|
+
},
|
|
32
|
+
pendingCleanup: new Map(),
|
|
33
|
+
logger: undefined
|
|
34
|
+
};
|
|
35
|
+
const query = {
|
|
36
|
+
cacheKey: {
|
|
37
|
+
type: "object",
|
|
38
|
+
otherKeys: ["Foo", 1]
|
|
39
|
+
},
|
|
40
|
+
subscribe: () => new Subscription(),
|
|
41
|
+
registerSubscriptionDedupeInterval: () => {},
|
|
42
|
+
unregisterSubscriptionDedupeInterval: () => {}
|
|
43
|
+
};
|
|
44
|
+
const helper = new class extends AbstractHelper {
|
|
45
|
+
getQuery() {
|
|
46
|
+
return query;
|
|
47
|
+
}
|
|
48
|
+
}(store, store.cacheKeys);
|
|
49
|
+
const observer = {
|
|
50
|
+
next: () => {},
|
|
51
|
+
error: () => {},
|
|
52
|
+
complete: () => {}
|
|
53
|
+
};
|
|
54
|
+
const sub1 = helper.observe({
|
|
55
|
+
mode: "offline"
|
|
56
|
+
}, observer);
|
|
57
|
+
expect(retain).toHaveBeenCalledTimes(1);
|
|
58
|
+
sub1.unsubscribe();
|
|
59
|
+
const sub2 = helper.observe({
|
|
60
|
+
mode: "offline"
|
|
61
|
+
}, observer);
|
|
62
|
+
expect(retain).toHaveBeenCalledTimes(1);
|
|
63
|
+
await flushMicrotasks();
|
|
64
|
+
expect(release).toHaveBeenCalledTimes(0);
|
|
65
|
+
sub2.unsubscribe();
|
|
66
|
+
await flushMicrotasks();
|
|
67
|
+
expect(release).toHaveBeenCalledTimes(1);
|
|
68
|
+
expect(store.pendingCleanup.size).toBe(0);
|
|
69
|
+
});
|
|
70
|
+
it("releases once per unsubscribe when multiple occur in the same tick", async () => {
|
|
71
|
+
const retain = vi.fn();
|
|
72
|
+
const release = vi.fn();
|
|
73
|
+
const store = {
|
|
74
|
+
cacheKeys: {
|
|
75
|
+
retain,
|
|
76
|
+
release
|
|
77
|
+
},
|
|
78
|
+
pendingCleanup: new Map(),
|
|
79
|
+
logger: undefined
|
|
80
|
+
};
|
|
81
|
+
const query = {
|
|
82
|
+
cacheKey: {
|
|
83
|
+
type: "object",
|
|
84
|
+
otherKeys: ["Foo", 1]
|
|
85
|
+
},
|
|
86
|
+
subscribe: () => new Subscription(),
|
|
87
|
+
registerSubscriptionDedupeInterval: () => {},
|
|
88
|
+
unregisterSubscriptionDedupeInterval: () => {}
|
|
89
|
+
};
|
|
90
|
+
const helper = new class extends AbstractHelper {
|
|
91
|
+
getQuery() {
|
|
92
|
+
return query;
|
|
93
|
+
}
|
|
94
|
+
}(store, store.cacheKeys);
|
|
95
|
+
const observer = {
|
|
96
|
+
next: () => {},
|
|
97
|
+
error: () => {},
|
|
98
|
+
complete: () => {}
|
|
99
|
+
};
|
|
100
|
+
const sub1 = helper.observe({
|
|
101
|
+
mode: "offline"
|
|
102
|
+
}, observer);
|
|
103
|
+
const sub2 = helper.observe({
|
|
104
|
+
mode: "offline"
|
|
105
|
+
}, observer);
|
|
106
|
+
expect(retain).toHaveBeenCalledTimes(2);
|
|
107
|
+
sub1.unsubscribe();
|
|
108
|
+
sub2.unsubscribe();
|
|
109
|
+
await flushMicrotasks();
|
|
110
|
+
expect(release).toHaveBeenCalledTimes(2);
|
|
111
|
+
expect(store.pendingCleanup.size).toBe(0);
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
//# sourceMappingURL=AbstractHelper.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AbstractHelper.test.js","names":["Subscription","describe","expect","it","vi","AbstractHelper","flushMicrotasks","Promise","resolve","queueMicrotask","retain","fn","release","store","cacheKeys","pendingCleanup","Map","logger","undefined","query","cacheKey","type","otherKeys","subscribe","registerSubscriptionDedupeInterval","unregisterSubscriptionDedupeInterval","helper","getQuery","observer","next","error","complete","sub1","observe","mode","toHaveBeenCalledTimes","unsubscribe","sub2","size","toBe"],"sources":["AbstractHelper.test.ts"],"sourcesContent":["/*\n * Copyright 2026 Palantir Technologies, Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Subscription } from \"rxjs\";\nimport { describe, expect, it, vi } from \"vitest\";\nimport { AbstractHelper } from \"./AbstractHelper.js\";\n\nfunction flushMicrotasks(): Promise<void> {\n return new Promise(resolve => queueMicrotask(resolve));\n}\n\ndescribe(\"AbstractHelper pending cleanup\", () => {\n it(\"coalesces unsubscribe→resubscribe within the same tick\", async () => {\n const cacheKey = { type: \"object\", otherKeys: [\"Foo\", 1] } as any;\n\n const retain = vi.fn();\n const release = vi.fn();\n const store = {\n cacheKeys: { retain, release },\n pendingCleanup: new Map<any, number>(),\n logger: undefined,\n } as any;\n\n const query = {\n cacheKey,\n subscribe: () => new Subscription(),\n registerSubscriptionDedupeInterval: () => {},\n unregisterSubscriptionDedupeInterval: () => {},\n } as any;\n\n const helper = new (class extends AbstractHelper<any, any> {\n getQuery(): any {\n return query;\n }\n })(store, store.cacheKeys);\n\n const observer = { next: () => {}, error: () => {}, complete: () => {} };\n\n const sub1 = helper.observe({ mode: \"offline\" }, observer);\n expect(retain).toHaveBeenCalledTimes(1);\n\n sub1.unsubscribe();\n const sub2 = helper.observe({ mode: \"offline\" }, observer);\n expect(retain).toHaveBeenCalledTimes(1);\n\n await flushMicrotasks();\n expect(release).toHaveBeenCalledTimes(0);\n\n sub2.unsubscribe();\n await flushMicrotasks();\n expect(release).toHaveBeenCalledTimes(1);\n expect(store.pendingCleanup.size).toBe(0);\n });\n\n it(\"releases once per unsubscribe when multiple occur in the same tick\", async () => {\n const cacheKey = { type: \"object\", otherKeys: [\"Foo\", 1] } as any;\n\n const retain = vi.fn();\n const release = vi.fn();\n const store = {\n cacheKeys: { retain, release },\n pendingCleanup: new Map<any, number>(),\n logger: undefined,\n } as any;\n\n const query = {\n cacheKey,\n subscribe: () => new Subscription(),\n registerSubscriptionDedupeInterval: () => {},\n unregisterSubscriptionDedupeInterval: () => {},\n } as any;\n\n const helper = new (class extends AbstractHelper<any, any> {\n getQuery(): any {\n return query;\n }\n })(store, store.cacheKeys);\n\n const observer = { next: () => {}, error: () => {}, complete: () => {} };\n\n const sub1 = helper.observe({ mode: \"offline\" }, observer);\n const sub2 = helper.observe({ mode: \"offline\" }, observer);\n expect(retain).toHaveBeenCalledTimes(2);\n\n sub1.unsubscribe();\n sub2.unsubscribe();\n await flushMicrotasks();\n\n expect(release).toHaveBeenCalledTimes(2);\n expect(store.pendingCleanup.size).toBe(0);\n });\n});\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -37,36 +37,50 @@ export class BulkObjectLoader {
|
|
|
37
37
|
this.#maxWait = maxWait;
|
|
38
38
|
this.#maxEntries = maxEntries;
|
|
39
39
|
}
|
|
40
|
-
async fetch(apiName, primaryKey) {
|
|
40
|
+
async fetch(apiName, primaryKey, defType = "object") {
|
|
41
41
|
const deferred = pDefer();
|
|
42
42
|
const entry = this.#m.get(apiName);
|
|
43
43
|
entry.data.push({
|
|
44
44
|
primaryKey: primaryKey,
|
|
45
45
|
deferred
|
|
46
46
|
});
|
|
47
|
+
if (entry.defType === undefined) {
|
|
48
|
+
entry.defType = defType;
|
|
49
|
+
} else if (entry.defType !== defType) {
|
|
50
|
+
deferred.reject(new PalantirApiError(`Conflicting defType for ${apiName}: existing=${entry.defType}, new=${defType}`));
|
|
51
|
+
return deferred.promise;
|
|
52
|
+
}
|
|
47
53
|
if (!entry.timer) {
|
|
48
54
|
entry.timer = setTimeout(() => {
|
|
49
|
-
this.#loadObjects(apiName, entry.data);
|
|
55
|
+
this.#loadObjects(apiName, entry.data, entry.defType ?? "object");
|
|
50
56
|
}, this.#maxWait);
|
|
51
57
|
}
|
|
52
58
|
if (entry.data.length >= this.#maxEntries) {
|
|
53
59
|
clearTimeout(entry.timer);
|
|
54
|
-
this.#loadObjects(apiName, entry.data);
|
|
60
|
+
this.#loadObjects(apiName, entry.data, entry.defType ?? "object");
|
|
55
61
|
}
|
|
56
62
|
return await deferred.promise;
|
|
57
63
|
}
|
|
58
|
-
#loadObjects(apiName, arr) {
|
|
64
|
+
#loadObjects(apiName, arr, defType) {
|
|
59
65
|
this.#m.delete(apiName);
|
|
60
|
-
this.#
|
|
66
|
+
const loadFn = defType === "interface" ? this.#loadInterfaceObjects(apiName, arr) : this.#loadObjectTypeObjects(apiName, arr);
|
|
67
|
+
loadFn.catch(e => {
|
|
61
68
|
this.#logger?.error("Unhandled exception", e);
|
|
69
|
+
for (const {
|
|
70
|
+
primaryKey,
|
|
71
|
+
deferred
|
|
72
|
+
} of arr) {
|
|
73
|
+
const errorMessage = e instanceof Error ? e.message : String(e);
|
|
74
|
+
deferred.reject(new PalantirApiError(`Failed to load ${apiName} with pk ${primaryKey}: ${errorMessage}`));
|
|
75
|
+
}
|
|
62
76
|
});
|
|
63
77
|
}
|
|
64
|
-
async #
|
|
65
|
-
const
|
|
78
|
+
async #loadObjectTypeObjects(apiName, arr) {
|
|
79
|
+
const objectDef = {
|
|
66
80
|
type: "object",
|
|
67
81
|
apiName
|
|
68
82
|
};
|
|
69
|
-
const objMetadata = await this.#client.fetchMetadata(
|
|
83
|
+
const objMetadata = await this.#client.fetchMetadata(objectDef);
|
|
70
84
|
const pks = arr.map(x => x.primaryKey);
|
|
71
85
|
|
|
72
86
|
// Use $eq for single object fetches (this is for public app compatibility)
|
|
@@ -82,7 +96,7 @@ export class BulkObjectLoader {
|
|
|
82
96
|
};
|
|
83
97
|
const {
|
|
84
98
|
data
|
|
85
|
-
} = await this.#client(
|
|
99
|
+
} = await this.#client(objectDef).where(whereClause).fetchPage({
|
|
86
100
|
$pageSize: pks.length,
|
|
87
101
|
$includeRid: true
|
|
88
102
|
});
|
|
@@ -98,5 +112,53 @@ export class BulkObjectLoader {
|
|
|
98
112
|
}
|
|
99
113
|
}
|
|
100
114
|
}
|
|
115
|
+
async #loadInterfaceObjects(apiName, arr) {
|
|
116
|
+
const pks = arr.map(x => x.primaryKey);
|
|
117
|
+
const interfaceMetadata = await this.#client.fetchMetadata({
|
|
118
|
+
type: "interface",
|
|
119
|
+
apiName
|
|
120
|
+
});
|
|
121
|
+
const implementingTypes = interfaceMetadata.implementedBy ?? [];
|
|
122
|
+
const foundObjects = new Map();
|
|
123
|
+
for (const objectTypeName of implementingTypes) {
|
|
124
|
+
const objectDef = {
|
|
125
|
+
type: "object",
|
|
126
|
+
apiName: objectTypeName
|
|
127
|
+
};
|
|
128
|
+
const objMetadata = await this.#client.fetchMetadata(objectDef);
|
|
129
|
+
const remainingPks = pks.filter(pk => !foundObjects.has(pk));
|
|
130
|
+
if (remainingPks.length === 0) {
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
const whereClause = remainingPks.length === 1 ? {
|
|
134
|
+
[objMetadata.primaryKeyApiName]: {
|
|
135
|
+
$eq: remainingPks[0]
|
|
136
|
+
}
|
|
137
|
+
} : {
|
|
138
|
+
[objMetadata.primaryKeyApiName]: {
|
|
139
|
+
$in: remainingPks
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
const {
|
|
143
|
+
data
|
|
144
|
+
} = await this.#client(objectDef).where(whereClause).fetchPage({
|
|
145
|
+
$pageSize: remainingPks.length
|
|
146
|
+
});
|
|
147
|
+
for (const obj of data) {
|
|
148
|
+
foundObjects.set(obj.$primaryKey, obj);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
for (const {
|
|
152
|
+
primaryKey,
|
|
153
|
+
deferred
|
|
154
|
+
} of arr) {
|
|
155
|
+
const object = foundObjects.get(primaryKey);
|
|
156
|
+
if (object) {
|
|
157
|
+
deferred.resolve(object);
|
|
158
|
+
} else {
|
|
159
|
+
deferred.reject(new PalantirApiError(`Interface ${apiName} object not found: ${primaryKey}`));
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
101
163
|
}
|
|
102
164
|
//# sourceMappingURL=BulkObjectLoader.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BulkObjectLoader.js","names":["PalantirApiError","DefaultMap","DefaultWeakMap","pDefer","additionalContext","weakCache","c","BulkObjectLoader","getBulkObjectLoader","client","get","m","data","timer","undefined","logger","maxWait","maxEntries","constructor","fetch","apiName","primaryKey","deferred","entry","push","setTimeout","loadObjects","length","clearTimeout","promise","#loadObjects","arr","delete","reallyLoadObjects","catch","e","error","#reallyLoadObjects","miniDef","type","objMetadata","fetchMetadata","pks","map","x","whereClause","primaryKeyApiName","$eq","$in","where","fetchPage","$pageSize","$includeRid","object","find","$primaryKey","resolve","reject"],"sources":["BulkObjectLoader.ts"],"sourcesContent":["/*\n * Copyright 2025 Palantir Technologies, Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { Logger, ObjectTypeDefinition } from \"@osdk/api\";\nimport { PalantirApiError } from \"@osdk/shared.net.errors\";\nimport { DefaultMap, DefaultWeakMap } from \"mnemonist\";\nimport type { DeferredPromise } from \"p-defer\";\nimport pDefer from \"p-defer\";\nimport { additionalContext, type Client } from \"../../Client.js\";\nimport type {\n ObjectHolder,\n} from \"../../object/convertWireToOsdkObjects/ObjectHolder.js\";\n\ninterface InternalValue {\n primaryKey: string;\n deferred: DeferredPromise<ObjectHolder>;\n}\n\ninterface Accumulator {\n data: InternalValue[];\n timer?: ReturnType<typeof setTimeout>;\n}\n\nconst weakCache = new DefaultWeakMap<Client, BulkObjectLoader>(c =>\n new BulkObjectLoader(c)\n);\n\nexport function getBulkObjectLoader(client: Client): BulkObjectLoader {\n return weakCache.get(client);\n}\n\nexport class BulkObjectLoader {\n #client: Client;\n\n #m = new DefaultMap<string, Accumulator>(() => ({\n data: [],\n timer: undefined,\n }));\n #logger: Logger | undefined;\n #maxWait: number;\n #maxEntries: number;\n\n constructor(client: Client, maxWait = 25, maxEntries = 100) {\n this.#client = client;\n this.#logger = client[additionalContext].logger;\n this.#maxWait = maxWait;\n this.#maxEntries = maxEntries;\n }\n\n public async fetch(\n apiName: string,\n primaryKey: string | number | boolean,\n ): Promise<ObjectHolder> {\n const deferred = pDefer<ObjectHolder>();\n\n const entry = this.#m.get(apiName);\n entry.data.push({\n primaryKey: primaryKey as string,\n deferred,\n });\n\n if (!entry.timer) {\n entry.timer = setTimeout(() => {\n this.#loadObjects(apiName, entry.data);\n }, this.#maxWait);\n }\n\n if (entry.data.length >= this.#maxEntries) {\n clearTimeout(entry.timer);\n this.#loadObjects(apiName, entry.data);\n }\n\n return await deferred.promise;\n }\n\n #loadObjects(apiName: string, arr: InternalValue[]) {\n this.#m.delete(apiName);\n\n this.#reallyLoadObjects(apiName, arr).catch((e: unknown) => {\n this.#logger?.error(\"Unhandled exception\", e);\n });\n }\n\n async #reallyLoadObjects(apiName: string, arr: InternalValue[]) {\n const miniDef = { type: \"object\", apiName } as ObjectTypeDefinition;\n const objMetadata = await this.#client.fetchMetadata(miniDef);\n\n const pks = arr.map(x => x.primaryKey);\n\n // Use $eq for single object fetches (this is for public app compatibility)\n // Use $in for batch fetches\n const whereClause = pks.length === 1\n ? { [objMetadata.primaryKeyApiName]: { $eq: pks[0] } }\n : { [objMetadata.primaryKeyApiName]: { $in: pks } };\n\n const { data } = await this.#client(miniDef)\n .where(whereClause).fetchPage({\n $pageSize: pks.length,\n $includeRid: true,\n });\n\n for (const { primaryKey, deferred } of arr) {\n const object = data.find(x => x.$primaryKey === primaryKey) as\n | ObjectHolder\n | undefined;\n if (object) {\n deferred.resolve(object);\n } else {\n deferred.reject(\n new PalantirApiError(`Object not found: ${primaryKey}`),\n );\n }\n }\n }\n}\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"BulkObjectLoader.js","names":["PalantirApiError","DefaultMap","DefaultWeakMap","pDefer","additionalContext","weakCache","c","BulkObjectLoader","getBulkObjectLoader","client","get","m","data","timer","undefined","logger","maxWait","maxEntries","constructor","fetch","apiName","primaryKey","defType","deferred","entry","push","reject","promise","setTimeout","loadObjects","length","clearTimeout","#loadObjects","arr","delete","loadFn","loadInterfaceObjects","loadObjectTypeObjects","catch","e","error","errorMessage","Error","message","String","#loadObjectTypeObjects","objectDef","type","objMetadata","fetchMetadata","pks","map","x","whereClause","primaryKeyApiName","$eq","$in","where","fetchPage","$pageSize","$includeRid","object","find","$primaryKey","resolve","#loadInterfaceObjects","interfaceMetadata","implementingTypes","implementedBy","foundObjects","Map","objectTypeName","remainingPks","filter","pk","has","obj","set"],"sources":["BulkObjectLoader.ts"],"sourcesContent":["/*\n * Copyright 2025 Palantir Technologies, Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type {\n InterfaceDefinition,\n Logger,\n ObjectTypeDefinition,\n} from \"@osdk/api\";\nimport { PalantirApiError } from \"@osdk/shared.net.errors\";\nimport { DefaultMap, DefaultWeakMap } from \"mnemonist\";\nimport type { DeferredPromise } from \"p-defer\";\nimport pDefer from \"p-defer\";\nimport { additionalContext, type Client } from \"../../Client.js\";\nimport type {\n ObjectHolder,\n} from \"../../object/convertWireToOsdkObjects/ObjectHolder.js\";\nimport type { DefType } from \"../../util/interfaceUtils.js\";\n\ninterface InternalValue {\n primaryKey: string;\n deferred: DeferredPromise<ObjectHolder>;\n}\n\ninterface Accumulator {\n data: InternalValue[];\n timer?: ReturnType<typeof setTimeout>;\n defType?: DefType;\n}\n\nconst weakCache = new DefaultWeakMap<Client, BulkObjectLoader>(c =>\n new BulkObjectLoader(c)\n);\n\nexport function getBulkObjectLoader(client: Client): BulkObjectLoader {\n return weakCache.get(client);\n}\n\nexport class BulkObjectLoader {\n #client: Client;\n\n #m = new DefaultMap<string, Accumulator>(() => ({\n data: [],\n timer: undefined,\n }));\n #logger: Logger | undefined;\n #maxWait: number;\n #maxEntries: number;\n\n constructor(client: Client, maxWait = 25, maxEntries = 100) {\n this.#client = client;\n this.#logger = client[additionalContext].logger;\n this.#maxWait = maxWait;\n this.#maxEntries = maxEntries;\n }\n\n public async fetch(\n apiName: string,\n primaryKey: string | number | boolean,\n defType: DefType = \"object\",\n ): Promise<ObjectHolder> {\n const deferred = pDefer<ObjectHolder>();\n\n const entry = this.#m.get(apiName);\n entry.data.push({\n primaryKey: primaryKey as string,\n deferred,\n });\n\n if (entry.defType === undefined) {\n entry.defType = defType;\n } else if (entry.defType !== defType) {\n deferred.reject(\n new PalantirApiError(\n `Conflicting defType for ${apiName}: existing=${entry.defType}, new=${defType}`,\n ),\n );\n return deferred.promise;\n }\n\n if (!entry.timer) {\n entry.timer = setTimeout(() => {\n this.#loadObjects(apiName, entry.data, entry.defType ?? \"object\");\n }, this.#maxWait);\n }\n\n if (entry.data.length >= this.#maxEntries) {\n clearTimeout(entry.timer);\n this.#loadObjects(apiName, entry.data, entry.defType ?? \"object\");\n }\n\n return await deferred.promise;\n }\n\n #loadObjects(\n apiName: string,\n arr: InternalValue[],\n defType: DefType,\n ) {\n this.#m.delete(apiName);\n\n const loadFn = defType === \"interface\"\n ? this.#loadInterfaceObjects(apiName, arr)\n : this.#loadObjectTypeObjects(apiName, arr);\n\n loadFn.catch((e: unknown) => {\n this.#logger?.error(\"Unhandled exception\", e);\n for (const { primaryKey, deferred } of arr) {\n const errorMessage = e instanceof Error ? e.message : String(e);\n deferred.reject(\n new PalantirApiError(\n `Failed to load ${apiName} with pk ${primaryKey}: ${errorMessage}`,\n ),\n );\n }\n });\n }\n\n async #loadObjectTypeObjects(apiName: string, arr: InternalValue[]) {\n const objectDef = { type: \"object\", apiName } as ObjectTypeDefinition;\n const objMetadata = await this.#client.fetchMetadata(objectDef);\n\n const pks = arr.map(x => x.primaryKey);\n\n // Use $eq for single object fetches (this is for public app compatibility)\n // Use $in for batch fetches\n const whereClause = pks.length === 1\n ? { [objMetadata.primaryKeyApiName]: { $eq: pks[0] } }\n : { [objMetadata.primaryKeyApiName]: { $in: pks } };\n\n const { data } = await this.#client(objectDef)\n .where(whereClause).fetchPage({\n $pageSize: pks.length,\n $includeRid: true,\n });\n\n for (const { primaryKey, deferred } of arr) {\n const object = data.find(x => x.$primaryKey === primaryKey) as\n | ObjectHolder\n | undefined;\n if (object) {\n deferred.resolve(object);\n } else {\n deferred.reject(\n new PalantirApiError(`Object not found: ${primaryKey}`),\n );\n }\n }\n }\n\n async #loadInterfaceObjects(apiName: string, arr: InternalValue[]) {\n const pks = arr.map(x => x.primaryKey);\n\n const interfaceDef = {\n type: \"interface\",\n apiName,\n } as InterfaceDefinition;\n\n const interfaceMetadata = await this.#client.fetchMetadata(interfaceDef);\n const implementingTypes = interfaceMetadata.implementedBy ?? [];\n\n const foundObjects = new Map<string | number, ObjectHolder>();\n\n for (const objectTypeName of implementingTypes) {\n const objectDef = {\n type: \"object\",\n apiName: objectTypeName,\n } as ObjectTypeDefinition;\n const objMetadata = await this.#client.fetchMetadata(objectDef);\n\n const remainingPks = pks.filter(pk => !foundObjects.has(pk));\n if (remainingPks.length === 0) {\n break;\n }\n\n const whereClause = remainingPks.length === 1\n ? { [objMetadata.primaryKeyApiName]: { $eq: remainingPks[0] } }\n : { [objMetadata.primaryKeyApiName]: { $in: remainingPks } };\n\n const { data } = await this.#client(objectDef)\n .where(whereClause).fetchPage({\n $pageSize: remainingPks.length,\n });\n\n for (const obj of data) {\n foundObjects.set(obj.$primaryKey, obj as ObjectHolder);\n }\n }\n\n for (const { primaryKey, deferred } of arr) {\n const object = foundObjects.get(primaryKey);\n if (object) {\n deferred.resolve(object);\n } else {\n deferred.reject(\n new PalantirApiError(\n `Interface ${apiName} object not found: ${primaryKey}`,\n ),\n );\n }\n }\n }\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -156,5 +156,84 @@ describe(BulkObjectLoader, () => {
|
|
|
156
156
|
});
|
|
157
157
|
vi.useRealTimers();
|
|
158
158
|
});
|
|
159
|
+
describe("interface loading", () => {
|
|
160
|
+
const mockObjectSet = data => {
|
|
161
|
+
const os = {
|
|
162
|
+
where: () => os,
|
|
163
|
+
fetchPage: vi.fn().mockResolvedValue({
|
|
164
|
+
data
|
|
165
|
+
})
|
|
166
|
+
};
|
|
167
|
+
return os;
|
|
168
|
+
};
|
|
169
|
+
it("loads interface objects by querying implementing types", async () => {
|
|
170
|
+
const loader = new BulkObjectLoader(client, 25, 100);
|
|
171
|
+
vi.useFakeTimers();
|
|
172
|
+
|
|
173
|
+
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
174
|
+
vi.mocked(client.fetchMetadata).mockResolvedValueOnce({
|
|
175
|
+
type: "interface",
|
|
176
|
+
implementedBy: ["Employee"],
|
|
177
|
+
links: {},
|
|
178
|
+
apiName: "FooInterface",
|
|
179
|
+
displayName: "FooInterface",
|
|
180
|
+
description: undefined,
|
|
181
|
+
properties: {},
|
|
182
|
+
rid: "ri.test"
|
|
183
|
+
}).mockResolvedValueOnce({
|
|
184
|
+
primaryKeyApiName: "employeeId"
|
|
185
|
+
});
|
|
186
|
+
client.mockReturnValueOnce(mockObjectSet([{
|
|
187
|
+
$apiName: "Employee",
|
|
188
|
+
$objectType: "Employee",
|
|
189
|
+
$primaryKey: 1
|
|
190
|
+
}]));
|
|
191
|
+
const loadPromise = loader.fetch("FooInterface", 1, "interface");
|
|
192
|
+
vi.advanceTimersByTime(26);
|
|
193
|
+
await expect(loadPromise).resolves.toMatchObject({
|
|
194
|
+
$primaryKey: 1
|
|
195
|
+
});
|
|
196
|
+
vi.useRealTimers();
|
|
197
|
+
});
|
|
198
|
+
it("rejects when interface object is not found in any implementing type", async () => {
|
|
199
|
+
const loader = new BulkObjectLoader(client, 25, 100);
|
|
200
|
+
vi.useFakeTimers();
|
|
201
|
+
|
|
202
|
+
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
203
|
+
vi.mocked(client.fetchMetadata).mockResolvedValueOnce({
|
|
204
|
+
type: "interface",
|
|
205
|
+
implementedBy: ["Employee"],
|
|
206
|
+
links: {},
|
|
207
|
+
apiName: "FooInterface",
|
|
208
|
+
displayName: "FooInterface",
|
|
209
|
+
description: undefined,
|
|
210
|
+
properties: {},
|
|
211
|
+
rid: "ri.test"
|
|
212
|
+
}).mockResolvedValueOnce({
|
|
213
|
+
primaryKeyApiName: "employeeId"
|
|
214
|
+
});
|
|
215
|
+
client.mockReturnValueOnce(mockObjectSet([]));
|
|
216
|
+
const loadPromise = loader.fetch("FooInterface", 1, "interface");
|
|
217
|
+
vi.advanceTimersByTime(26);
|
|
218
|
+
await expect(loadPromise).rejects.toThrow("Interface FooInterface object not found: 1");
|
|
219
|
+
vi.useRealTimers();
|
|
220
|
+
});
|
|
221
|
+
it("loads object type when defType='object' (default)", async () => {
|
|
222
|
+
const loader = new BulkObjectLoader(client, 25, 100);
|
|
223
|
+
vi.useFakeTimers();
|
|
224
|
+
const firstRequest = mockClient.mockFetchPageOnce();
|
|
225
|
+
const loadPromise = loader.fetch("Employee", 1);
|
|
226
|
+
vi.advanceTimersByTime(26);
|
|
227
|
+
firstRequest.resolve({
|
|
228
|
+
data: [employees[1]],
|
|
229
|
+
nextPageToken: undefined,
|
|
230
|
+
totalCount: "1"
|
|
231
|
+
});
|
|
232
|
+
await expect(loadPromise).resolves.toMatchObject({
|
|
233
|
+
$primaryKey: 1
|
|
234
|
+
});
|
|
235
|
+
vi.useRealTimers();
|
|
236
|
+
});
|
|
237
|
+
});
|
|
159
238
|
});
|
|
160
239
|
//# sourceMappingURL=BulkObjectLoader.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BulkObjectLoader.test.js","names":["beforeEach","describe","expect","it","vi","BulkObjectLoader","createClientMockHelper","client","mockClient","mocked","fetchMetadata","mockReturnValue","Promise","resolve","primaryKeyApiName","employees","$apiName","$objectType","$primaryKey","loader","firstRequest","mockFetchPageOnce","secondRequest","load0","fetch","load1","load2","data","nextPageToken","undefined","totalCount","resolves","toMatchObject","mock","fn","then","not","toHaveBeenCalled","useFakeTimers","advanceTimersByTime","runOnlyPendingTimers","mockThen","useRealTimers","whereClauses","mockObjectSet","where","clause","push","fetchPage","mockResolvedValue","mockReturnValueOnce","toEqual","id","$eq","length","mockObjectSet2","all","$in"],"sources":["BulkObjectLoader.test.ts"],"sourcesContent":["/*\n * Copyright 2025 Palantir Technologies, Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type {\n ObjectMetadata,\n ObjectSet,\n ObjectTypeDefinition,\n WhereClause,\n} from \"@osdk/api\";\nimport type { Mock } from \"vitest\";\nimport { beforeEach, describe, expect, it, vi } from \"vitest\";\nimport type { Client } from \"../../Client.js\";\nimport { BulkObjectLoader } from \"./BulkObjectLoader.js\";\nimport { createClientMockHelper, type MockClientHelper } from \"./testUtils.js\";\n\ndescribe(BulkObjectLoader, () => {\n let client: Mock<Client> & Client;\n let mockClient: MockClientHelper;\n\n beforeEach(async () => {\n mockClient = createClientMockHelper();\n client = mockClient.client;\n\n // eslint-disable-next-line @typescript-eslint/unbound-method\n vi.mocked(client.fetchMetadata).mockReturnValue(\n Promise.resolve(\n {\n primaryKeyApiName: \"id\",\n } satisfies Pick<ObjectMetadata, \"primaryKeyApiName\"> as ObjectMetadata,\n ),\n );\n });\n\n const employees = [\n {\n $apiName: \"Employee\",\n $objectType: \"Employee\",\n $primaryKey: 0,\n },\n {\n $apiName: \"Employee\",\n $objectType: \"Employee\",\n $primaryKey: 1,\n },\n {\n $apiName: \"Employee\",\n $objectType: \"Employee\",\n $primaryKey: 2,\n },\n ];\n\n it(\"splits up work by count\", async () => {\n const loader = new BulkObjectLoader(client, 25, /*ms*/ 2 /*entries*/);\n\n const firstRequest = mockClient.mockFetchPageOnce();\n const secondRequest = mockClient.mockFetchPageOnce();\n\n const load0 = loader.fetch(\"Employee\", 0);\n const load1 = loader.fetch(\"Employee\", 1);\n const load2 = loader.fetch(\"Employee\", 2);\n\n firstRequest.resolve({\n data: [employees[0], employees[1]],\n nextPageToken: undefined,\n totalCount: \"2\",\n });\n\n await expect(load0).resolves.toMatchObject({\n $primaryKey: 0,\n });\n\n await expect(load1).resolves.toMatchObject({\n $primaryKey: 1,\n });\n\n const mock = vi.fn();\n void load2.then(mock);\n expect(mock).not.toHaveBeenCalled();\n\n secondRequest.resolve({\n data: [employees[2]],\n nextPageToken: undefined,\n totalCount: \"1\",\n });\n\n await expect(load2).resolves.toMatchObject({\n $primaryKey: 2,\n });\n\n expect(mock).toHaveBeenCalled();\n });\n\n it(\"splits up work by time\", async () => {\n const loader = new BulkObjectLoader(client, /*ms*/ 25, /*entries*/ 100);\n\n const firstRequest = mockClient.mockFetchPageOnce();\n const secondRequest = mockClient.mockFetchPageOnce();\n\n vi.useFakeTimers();\n\n const load0 = loader.fetch(\"Employee\", 0);\n\n vi.advanceTimersByTime(26);\n\n const load1 = loader.fetch(\"Employee\", 1);\n const load2 = loader.fetch(\"Employee\", 2);\n\n firstRequest.resolve({\n data: [employees[0]],\n nextPageToken: undefined,\n totalCount: \"1\",\n });\n\n vi.runOnlyPendingTimers();\n\n await expect(load0).resolves.toMatchObject({\n $primaryKey: 0,\n });\n\n const mockThen = vi.fn();\n void load2.then(mockThen);\n\n expect(mockThen).not.toHaveBeenCalled();\n\n secondRequest.resolve({\n data: [\n employees[1],\n employees[2],\n ],\n nextPageToken: undefined,\n totalCount: \"2\",\n });\n\n await expect(load1).resolves.toMatchObject({\n $primaryKey: 1,\n });\n\n await expect(load2).resolves.toMatchObject({\n $primaryKey: 2,\n });\n\n expect(mockThen).toHaveBeenCalled();\n\n vi.useRealTimers();\n });\n\n it(\"uses $eq for single object and $in for multiple objects\", async () => {\n const loader = new BulkObjectLoader(client, 25, 100);\n\n vi.useFakeTimers();\n\n const whereClauses: WhereClause<ObjectTypeDefinition>[] = [];\n\n const mockObjectSet: ObjectSet<ObjectTypeDefinition> = {\n where: (clause) => {\n whereClauses.push(clause);\n return mockObjectSet;\n },\n fetchPage: vi.fn().mockResolvedValue({\n data: [employees[0]],\n nextPageToken: undefined,\n totalCount: \"1\",\n }),\n } as Pick<\n ObjectSet<ObjectTypeDefinition>,\n \"fetchPage\" | \"where\"\n > as ObjectSet<ObjectTypeDefinition>;\n\n client.mockReturnValueOnce(mockObjectSet);\n const load0 = loader.fetch(\"Employee\", 0);\n\n vi.advanceTimersByTime(26);\n\n await load0;\n\n expect(whereClauses[0]).toEqual({\n id: { $eq: 0 },\n });\n\n whereClauses.length = 0;\n\n const mockObjectSet2: ObjectSet<ObjectTypeDefinition> = {\n where: (clause) => {\n whereClauses.push(clause);\n return mockObjectSet2;\n },\n fetchPage: vi.fn().mockResolvedValue({\n data: [employees[1], employees[2]],\n nextPageToken: undefined,\n totalCount: \"2\",\n }),\n } as Pick<\n ObjectSet<ObjectTypeDefinition>,\n \"fetchPage\" | \"where\"\n > as ObjectSet<ObjectTypeDefinition>;\n\n client.mockReturnValueOnce(mockObjectSet2);\n\n const load1 = loader.fetch(\"Employee\", 1);\n const load2 = loader.fetch(\"Employee\", 2);\n\n vi.advanceTimersByTime(26);\n\n await Promise.all([load1, load2]);\n\n expect(whereClauses[0]).toEqual({\n id: { $in: [1, 2] },\n });\n\n vi.useRealTimers();\n });\n});\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"BulkObjectLoader.test.js","names":["beforeEach","describe","expect","it","vi","BulkObjectLoader","createClientMockHelper","client","mockClient","mocked","fetchMetadata","mockReturnValue","Promise","resolve","primaryKeyApiName","employees","$apiName","$objectType","$primaryKey","loader","firstRequest","mockFetchPageOnce","secondRequest","load0","fetch","load1","load2","data","nextPageToken","undefined","totalCount","resolves","toMatchObject","mock","fn","then","not","toHaveBeenCalled","useFakeTimers","advanceTimersByTime","runOnlyPendingTimers","mockThen","useRealTimers","whereClauses","mockObjectSet","where","clause","push","fetchPage","mockResolvedValue","mockReturnValueOnce","toEqual","id","$eq","length","mockObjectSet2","all","$in","os","mockResolvedValueOnce","type","implementedBy","links","apiName","displayName","description","properties","rid","loadPromise","rejects","toThrow"],"sources":["BulkObjectLoader.test.ts"],"sourcesContent":["/*\n * Copyright 2025 Palantir Technologies, Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type {\n InterfaceMetadata,\n ObjectMetadata,\n ObjectSet,\n ObjectTypeDefinition,\n WhereClause,\n} from \"@osdk/api\";\nimport type { Mock } from \"vitest\";\nimport { beforeEach, describe, expect, it, vi } from \"vitest\";\nimport type { Client } from \"../../Client.js\";\nimport { BulkObjectLoader } from \"./BulkObjectLoader.js\";\nimport { createClientMockHelper, type MockClientHelper } from \"./testUtils.js\";\n\ndescribe(BulkObjectLoader, () => {\n let client: Mock<Client> & Client;\n let mockClient: MockClientHelper;\n\n beforeEach(async () => {\n mockClient = createClientMockHelper();\n client = mockClient.client;\n\n // eslint-disable-next-line @typescript-eslint/unbound-method\n vi.mocked(client.fetchMetadata).mockReturnValue(\n Promise.resolve(\n {\n primaryKeyApiName: \"id\",\n } satisfies Pick<ObjectMetadata, \"primaryKeyApiName\"> as ObjectMetadata,\n ),\n );\n });\n\n const employees = [\n {\n $apiName: \"Employee\",\n $objectType: \"Employee\",\n $primaryKey: 0,\n },\n {\n $apiName: \"Employee\",\n $objectType: \"Employee\",\n $primaryKey: 1,\n },\n {\n $apiName: \"Employee\",\n $objectType: \"Employee\",\n $primaryKey: 2,\n },\n ];\n\n it(\"splits up work by count\", async () => {\n const loader = new BulkObjectLoader(client, 25, /*ms*/ 2 /*entries*/);\n\n const firstRequest = mockClient.mockFetchPageOnce();\n const secondRequest = mockClient.mockFetchPageOnce();\n\n const load0 = loader.fetch(\"Employee\", 0);\n const load1 = loader.fetch(\"Employee\", 1);\n const load2 = loader.fetch(\"Employee\", 2);\n\n firstRequest.resolve({\n data: [employees[0], employees[1]],\n nextPageToken: undefined,\n totalCount: \"2\",\n });\n\n await expect(load0).resolves.toMatchObject({\n $primaryKey: 0,\n });\n\n await expect(load1).resolves.toMatchObject({\n $primaryKey: 1,\n });\n\n const mock = vi.fn();\n void load2.then(mock);\n expect(mock).not.toHaveBeenCalled();\n\n secondRequest.resolve({\n data: [employees[2]],\n nextPageToken: undefined,\n totalCount: \"1\",\n });\n\n await expect(load2).resolves.toMatchObject({\n $primaryKey: 2,\n });\n\n expect(mock).toHaveBeenCalled();\n });\n\n it(\"splits up work by time\", async () => {\n const loader = new BulkObjectLoader(client, /*ms*/ 25, /*entries*/ 100);\n\n const firstRequest = mockClient.mockFetchPageOnce();\n const secondRequest = mockClient.mockFetchPageOnce();\n\n vi.useFakeTimers();\n\n const load0 = loader.fetch(\"Employee\", 0);\n\n vi.advanceTimersByTime(26);\n\n const load1 = loader.fetch(\"Employee\", 1);\n const load2 = loader.fetch(\"Employee\", 2);\n\n firstRequest.resolve({\n data: [employees[0]],\n nextPageToken: undefined,\n totalCount: \"1\",\n });\n\n vi.runOnlyPendingTimers();\n\n await expect(load0).resolves.toMatchObject({\n $primaryKey: 0,\n });\n\n const mockThen = vi.fn();\n void load2.then(mockThen);\n\n expect(mockThen).not.toHaveBeenCalled();\n\n secondRequest.resolve({\n data: [\n employees[1],\n employees[2],\n ],\n nextPageToken: undefined,\n totalCount: \"2\",\n });\n\n await expect(load1).resolves.toMatchObject({\n $primaryKey: 1,\n });\n\n await expect(load2).resolves.toMatchObject({\n $primaryKey: 2,\n });\n\n expect(mockThen).toHaveBeenCalled();\n\n vi.useRealTimers();\n });\n\n it(\"uses $eq for single object and $in for multiple objects\", async () => {\n const loader = new BulkObjectLoader(client, 25, 100);\n\n vi.useFakeTimers();\n\n const whereClauses: WhereClause<ObjectTypeDefinition>[] = [];\n\n const mockObjectSet: ObjectSet<ObjectTypeDefinition> = {\n where: (clause) => {\n whereClauses.push(clause);\n return mockObjectSet;\n },\n fetchPage: vi.fn().mockResolvedValue({\n data: [employees[0]],\n nextPageToken: undefined,\n totalCount: \"1\",\n }),\n } as Pick<\n ObjectSet<ObjectTypeDefinition>,\n \"fetchPage\" | \"where\"\n > as ObjectSet<ObjectTypeDefinition>;\n\n client.mockReturnValueOnce(mockObjectSet);\n const load0 = loader.fetch(\"Employee\", 0);\n\n vi.advanceTimersByTime(26);\n\n await load0;\n\n expect(whereClauses[0]).toEqual({\n id: { $eq: 0 },\n });\n\n whereClauses.length = 0;\n\n const mockObjectSet2: ObjectSet<ObjectTypeDefinition> = {\n where: (clause) => {\n whereClauses.push(clause);\n return mockObjectSet2;\n },\n fetchPage: vi.fn().mockResolvedValue({\n data: [employees[1], employees[2]],\n nextPageToken: undefined,\n totalCount: \"2\",\n }),\n } as Pick<\n ObjectSet<ObjectTypeDefinition>,\n \"fetchPage\" | \"where\"\n > as ObjectSet<ObjectTypeDefinition>;\n\n client.mockReturnValueOnce(mockObjectSet2);\n\n const load1 = loader.fetch(\"Employee\", 1);\n const load2 = loader.fetch(\"Employee\", 2);\n\n vi.advanceTimersByTime(26);\n\n await Promise.all([load1, load2]);\n\n expect(whereClauses[0]).toEqual({\n id: { $in: [1, 2] },\n });\n\n vi.useRealTimers();\n });\n\n describe(\"interface loading\", () => {\n const mockObjectSet = (data: unknown[]) => {\n const os: ObjectSet<ObjectTypeDefinition> = {\n where: () => os,\n fetchPage: vi.fn().mockResolvedValue({ data }),\n } as Pick<\n ObjectSet<ObjectTypeDefinition>,\n \"fetchPage\" | \"where\"\n > as ObjectSet<ObjectTypeDefinition>;\n return os;\n };\n\n it(\"loads interface objects by querying implementing types\", async () => {\n const loader = new BulkObjectLoader(client, 25, 100);\n vi.useFakeTimers();\n\n // eslint-disable-next-line @typescript-eslint/unbound-method\n vi.mocked(client.fetchMetadata)\n .mockResolvedValueOnce(\n {\n type: \"interface\",\n implementedBy: [\"Employee\"],\n links: {},\n apiName: \"FooInterface\",\n displayName: \"FooInterface\",\n description: undefined,\n properties: {},\n rid: \"ri.test\",\n } satisfies InterfaceMetadata,\n )\n .mockResolvedValueOnce(\n { primaryKeyApiName: \"employeeId\" } as ObjectMetadata,\n );\n\n const fullObj = {\n $apiName: \"Employee\",\n $objectType: \"Employee\",\n $primaryKey: 1,\n };\n\n client.mockReturnValueOnce(mockObjectSet([fullObj]));\n\n const loadPromise = loader.fetch(\"FooInterface\", 1, \"interface\");\n vi.advanceTimersByTime(26);\n\n await expect(loadPromise).resolves.toMatchObject({ $primaryKey: 1 });\n vi.useRealTimers();\n });\n\n it(\"rejects when interface object is not found in any implementing type\", async () => {\n const loader = new BulkObjectLoader(client, 25, 100);\n vi.useFakeTimers();\n\n // eslint-disable-next-line @typescript-eslint/unbound-method\n vi.mocked(client.fetchMetadata)\n .mockResolvedValueOnce(\n {\n type: \"interface\",\n implementedBy: [\"Employee\"],\n links: {},\n apiName: \"FooInterface\",\n displayName: \"FooInterface\",\n description: undefined,\n properties: {},\n rid: \"ri.test\",\n } satisfies InterfaceMetadata,\n )\n .mockResolvedValueOnce(\n { primaryKeyApiName: \"employeeId\" } as ObjectMetadata,\n );\n\n client.mockReturnValueOnce(mockObjectSet([]));\n\n const loadPromise = loader.fetch(\"FooInterface\", 1, \"interface\");\n vi.advanceTimersByTime(26);\n\n await expect(loadPromise).rejects.toThrow(\n \"Interface FooInterface object not found: 1\",\n );\n vi.useRealTimers();\n });\n\n it(\"loads object type when defType='object' (default)\", async () => {\n const loader = new BulkObjectLoader(client, 25, 100);\n vi.useFakeTimers();\n\n const firstRequest = mockClient.mockFetchPageOnce();\n\n const loadPromise = loader.fetch(\"Employee\", 1);\n vi.advanceTimersByTime(26);\n\n firstRequest.resolve({\n data: [employees[1]],\n nextPageToken: undefined,\n totalCount: \"1\",\n });\n\n await expect(loadPromise).resolves.toMatchObject({ $primaryKey: 1 });\n vi.useRealTimers();\n });\n });\n});\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAUA,SAASA,UAAU,EAAEC,QAAQ,EAAEC,MAAM,EAAEC,EAAE,EAAEC,EAAE,QAAQ,QAAQ;AAE7D,SAASC,gBAAgB,QAAQ,uBAAuB;AACxD,SAASC,sBAAsB,QAA+B,gBAAgB;AAE9EL,QAAQ,CAACI,gBAAgB,EAAE,MAAM;EAC/B,IAAIE,MAA6B;EACjC,IAAIC,UAA4B;EAEhCR,UAAU,CAAC,YAAY;IACrBQ,UAAU,GAAGF,sBAAsB,CAAC,CAAC;IACrCC,MAAM,GAAGC,UAAU,CAACD,MAAM;;IAE1B;IACAH,EAAE,CAACK,MAAM,CAACF,MAAM,CAACG,aAAa,CAAC,CAACC,eAAe,CAC7CC,OAAO,CAACC,OAAO,CACb;MACEC,iBAAiB,EAAE;IACrB,CACF,CACF,CAAC;EACH,CAAC,CAAC;EAEF,MAAMC,SAAS,GAAG,CAChB;IACEC,QAAQ,EAAE,UAAU;IACpBC,WAAW,EAAE,UAAU;IACvBC,WAAW,EAAE;EACf,CAAC,EACD;IACEF,QAAQ,EAAE,UAAU;IACpBC,WAAW,EAAE,UAAU;IACvBC,WAAW,EAAE;EACf,CAAC,EACD;IACEF,QAAQ,EAAE,UAAU;IACpBC,WAAW,EAAE,UAAU;IACvBC,WAAW,EAAE;EACf,CAAC,CACF;EAEDf,EAAE,CAAC,yBAAyB,EAAE,YAAY;IACxC,MAAMgB,MAAM,GAAG,IAAId,gBAAgB,CAACE,MAAM,EAAE,EAAE,EAAE,MAAO,CAAC,CAAC,WAAW,CAAC;IAErE,MAAMa,YAAY,GAAGZ,UAAU,CAACa,iBAAiB,CAAC,CAAC;IACnD,MAAMC,aAAa,GAAGd,UAAU,CAACa,iBAAiB,CAAC,CAAC;IAEpD,MAAME,KAAK,GAAGJ,MAAM,CAACK,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC;IACzC,MAAMC,KAAK,GAAGN,MAAM,CAACK,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC;IACzC,MAAME,KAAK,GAAGP,MAAM,CAACK,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC;IAEzCJ,YAAY,CAACP,OAAO,CAAC;MACnBc,IAAI,EAAE,CAACZ,SAAS,CAAC,CAAC,CAAC,EAAEA,SAAS,CAAC,CAAC,CAAC,CAAC;MAClCa,aAAa,EAAEC,SAAS;MACxBC,UAAU,EAAE;IACd,CAAC,CAAC;IAEF,MAAM5B,MAAM,CAACqB,KAAK,CAAC,CAACQ,QAAQ,CAACC,aAAa,CAAC;MACzCd,WAAW,EAAE;IACf,CAAC,CAAC;IAEF,MAAMhB,MAAM,CAACuB,KAAK,CAAC,CAACM,QAAQ,CAACC,aAAa,CAAC;MACzCd,WAAW,EAAE;IACf,CAAC,CAAC;IAEF,MAAMe,IAAI,GAAG7B,EAAE,CAAC8B,EAAE,CAAC,CAAC;IACpB,KAAKR,KAAK,CAACS,IAAI,CAACF,IAAI,CAAC;IACrB/B,MAAM,CAAC+B,IAAI,CAAC,CAACG,GAAG,CAACC,gBAAgB,CAAC,CAAC;IAEnCf,aAAa,CAACT,OAAO,CAAC;MACpBc,IAAI,EAAE,CAACZ,SAAS,CAAC,CAAC,CAAC,CAAC;MACpBa,aAAa,EAAEC,SAAS;MACxBC,UAAU,EAAE;IACd,CAAC,CAAC;IAEF,MAAM5B,MAAM,CAACwB,KAAK,CAAC,CAACK,QAAQ,CAACC,aAAa,CAAC;MACzCd,WAAW,EAAE;IACf,CAAC,CAAC;IAEFhB,MAAM,CAAC+B,IAAI,CAAC,CAACI,gBAAgB,CAAC,CAAC;EACjC,CAAC,CAAC;EAEFlC,EAAE,CAAC,wBAAwB,EAAE,YAAY;IACvC,MAAMgB,MAAM,GAAG,IAAId,gBAAgB,CAACE,MAAM,EAAE,MAAO,EAAE,EAAE,WAAY,GAAG,CAAC;IAEvE,MAAMa,YAAY,GAAGZ,UAAU,CAACa,iBAAiB,CAAC,CAAC;IACnD,MAAMC,aAAa,GAAGd,UAAU,CAACa,iBAAiB,CAAC,CAAC;IAEpDjB,EAAE,CAACkC,aAAa,CAAC,CAAC;IAElB,MAAMf,KAAK,GAAGJ,MAAM,CAACK,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC;IAEzCpB,EAAE,CAACmC,mBAAmB,CAAC,EAAE,CAAC;IAE1B,MAAMd,KAAK,GAAGN,MAAM,CAACK,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC;IACzC,MAAME,KAAK,GAAGP,MAAM,CAACK,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC;IAEzCJ,YAAY,CAACP,OAAO,CAAC;MACnBc,IAAI,EAAE,CAACZ,SAAS,CAAC,CAAC,CAAC,CAAC;MACpBa,aAAa,EAAEC,SAAS;MACxBC,UAAU,EAAE;IACd,CAAC,CAAC;IAEF1B,EAAE,CAACoC,oBAAoB,CAAC,CAAC;IAEzB,MAAMtC,MAAM,CAACqB,KAAK,CAAC,CAACQ,QAAQ,CAACC,aAAa,CAAC;MACzCd,WAAW,EAAE;IACf,CAAC,CAAC;IAEF,MAAMuB,QAAQ,GAAGrC,EAAE,CAAC8B,EAAE,CAAC,CAAC;IACxB,KAAKR,KAAK,CAACS,IAAI,CAACM,QAAQ,CAAC;IAEzBvC,MAAM,CAACuC,QAAQ,CAAC,CAACL,GAAG,CAACC,gBAAgB,CAAC,CAAC;IAEvCf,aAAa,CAACT,OAAO,CAAC;MACpBc,IAAI,EAAE,CACJZ,SAAS,CAAC,CAAC,CAAC,EACZA,SAAS,CAAC,CAAC,CAAC,CACb;MACDa,aAAa,EAAEC,SAAS;MACxBC,UAAU,EAAE;IACd,CAAC,CAAC;IAEF,MAAM5B,MAAM,CAACuB,KAAK,CAAC,CAACM,QAAQ,CAACC,aAAa,CAAC;MACzCd,WAAW,EAAE;IACf,CAAC,CAAC;IAEF,MAAMhB,MAAM,CAACwB,KAAK,CAAC,CAACK,QAAQ,CAACC,aAAa,CAAC;MACzCd,WAAW,EAAE;IACf,CAAC,CAAC;IAEFhB,MAAM,CAACuC,QAAQ,CAAC,CAACJ,gBAAgB,CAAC,CAAC;IAEnCjC,EAAE,CAACsC,aAAa,CAAC,CAAC;EACpB,CAAC,CAAC;EAEFvC,EAAE,CAAC,yDAAyD,EAAE,YAAY;IACxE,MAAMgB,MAAM,GAAG,IAAId,gBAAgB,CAACE,MAAM,EAAE,EAAE,EAAE,GAAG,CAAC;IAEpDH,EAAE,CAACkC,aAAa,CAAC,CAAC;IAElB,MAAMK,YAAiD,GAAG,EAAE;IAE5D,MAAMC,aAA8C,GAAG;MACrDC,KAAK,EAAGC,MAAM,IAAK;QACjBH,YAAY,CAACI,IAAI,CAACD,MAAM,CAAC;QACzB,OAAOF,aAAa;MACtB,CAAC;MACDI,SAAS,EAAE5C,EAAE,CAAC8B,EAAE,CAAC,CAAC,CAACe,iBAAiB,CAAC;QACnCtB,IAAI,EAAE,CAACZ,SAAS,CAAC,CAAC,CAAC,CAAC;QACpBa,aAAa,EAAEC,SAAS;QACxBC,UAAU,EAAE;MACd,CAAC;IACH,CAGoC;IAEpCvB,MAAM,CAAC2C,mBAAmB,CAACN,aAAa,CAAC;IACzC,MAAMrB,KAAK,GAAGJ,MAAM,CAACK,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC;IAEzCpB,EAAE,CAACmC,mBAAmB,CAAC,EAAE,CAAC;IAE1B,MAAMhB,KAAK;IAEXrB,MAAM,CAACyC,YAAY,CAAC,CAAC,CAAC,CAAC,CAACQ,OAAO,CAAC;MAC9BC,EAAE,EAAE;QAAEC,GAAG,EAAE;MAAE;IACf,CAAC,CAAC;IAEFV,YAAY,CAACW,MAAM,GAAG,CAAC;IAEvB,MAAMC,cAA+C,GAAG;MACtDV,KAAK,EAAGC,MAAM,IAAK;QACjBH,YAAY,CAACI,IAAI,CAACD,MAAM,CAAC;QACzB,OAAOS,cAAc;MACvB,CAAC;MACDP,SAAS,EAAE5C,EAAE,CAAC8B,EAAE,CAAC,CAAC,CAACe,iBAAiB,CAAC;QACnCtB,IAAI,EAAE,CAACZ,SAAS,CAAC,CAAC,CAAC,EAAEA,SAAS,CAAC,CAAC,CAAC,CAAC;QAClCa,aAAa,EAAEC,SAAS;QACxBC,UAAU,EAAE;MACd,CAAC;IACH,CAGoC;IAEpCvB,MAAM,CAAC2C,mBAAmB,CAACK,cAAc,CAAC;IAE1C,MAAM9B,KAAK,GAAGN,MAAM,CAACK,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC;IACzC,MAAME,KAAK,GAAGP,MAAM,CAACK,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC;IAEzCpB,EAAE,CAACmC,mBAAmB,CAAC,EAAE,CAAC;IAE1B,MAAM3B,OAAO,CAAC4C,GAAG,CAAC,CAAC/B,KAAK,EAAEC,KAAK,CAAC,CAAC;IAEjCxB,MAAM,CAACyC,YAAY,CAAC,CAAC,CAAC,CAAC,CAACQ,OAAO,CAAC;MAC9BC,EAAE,EAAE;QAAEK,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC;MAAE;IACpB,CAAC,CAAC;IAEFrD,EAAE,CAACsC,aAAa,CAAC,CAAC;EACpB,CAAC,CAAC;EAEFzC,QAAQ,CAAC,mBAAmB,EAAE,MAAM;IAClC,MAAM2C,aAAa,GAAIjB,IAAe,IAAK;MACzC,MAAM+B,EAAmC,GAAG;QAC1Cb,KAAK,EAAEA,CAAA,KAAMa,EAAE;QACfV,SAAS,EAAE5C,EAAE,CAAC8B,EAAE,CAAC,CAAC,CAACe,iBAAiB,CAAC;UAAEtB;QAAK,CAAC;MAC/C,CAGoC;MACpC,OAAO+B,EAAE;IACX,CAAC;IAEDvD,EAAE,CAAC,wDAAwD,EAAE,YAAY;MACvE,MAAMgB,MAAM,GAAG,IAAId,gBAAgB,CAACE,MAAM,EAAE,EAAE,EAAE,GAAG,CAAC;MACpDH,EAAE,CAACkC,aAAa,CAAC,CAAC;;MAElB;MACAlC,EAAE,CAACK,MAAM,CAACF,MAAM,CAACG,aAAa,CAAC,CAC5BiD,qBAAqB,CACpB;QACEC,IAAI,EAAE,WAAW;QACjBC,aAAa,EAAE,CAAC,UAAU,CAAC;QAC3BC,KAAK,EAAE,CAAC,CAAC;QACTC,OAAO,EAAE,cAAc;QACvBC,WAAW,EAAE,cAAc;QAC3BC,WAAW,EAAEpC,SAAS;QACtBqC,UAAU,EAAE,CAAC,CAAC;QACdC,GAAG,EAAE;MACP,CACF,CAAC,CACAR,qBAAqB,CACpB;QAAE7C,iBAAiB,EAAE;MAAa,CACpC,CAAC;MAQHP,MAAM,CAAC2C,mBAAmB,CAACN,aAAa,CAAC,CANzB;QACd5B,QAAQ,EAAE,UAAU;QACpBC,WAAW,EAAE,UAAU;QACvBC,WAAW,EAAE;MACf,CAAC,CAEiD,CAAC,CAAC;MAEpD,MAAMkD,WAAW,GAAGjD,MAAM,CAACK,KAAK,CAAC,cAAc,EAAE,CAAC,EAAE,WAAW,CAAC;MAChEpB,EAAE,CAACmC,mBAAmB,CAAC,EAAE,CAAC;MAE1B,MAAMrC,MAAM,CAACkE,WAAW,CAAC,CAACrC,QAAQ,CAACC,aAAa,CAAC;QAAEd,WAAW,EAAE;MAAE,CAAC,CAAC;MACpEd,EAAE,CAACsC,aAAa,CAAC,CAAC;IACpB,CAAC,CAAC;IAEFvC,EAAE,CAAC,qEAAqE,EAAE,YAAY;MACpF,MAAMgB,MAAM,GAAG,IAAId,gBAAgB,CAACE,MAAM,EAAE,EAAE,EAAE,GAAG,CAAC;MACpDH,EAAE,CAACkC,aAAa,CAAC,CAAC;;MAElB;MACAlC,EAAE,CAACK,MAAM,CAACF,MAAM,CAACG,aAAa,CAAC,CAC5BiD,qBAAqB,CACpB;QACEC,IAAI,EAAE,WAAW;QACjBC,aAAa,EAAE,CAAC,UAAU,CAAC;QAC3BC,KAAK,EAAE,CAAC,CAAC;QACTC,OAAO,EAAE,cAAc;QACvBC,WAAW,EAAE,cAAc;QAC3BC,WAAW,EAAEpC,SAAS;QACtBqC,UAAU,EAAE,CAAC,CAAC;QACdC,GAAG,EAAE;MACP,CACF,CAAC,CACAR,qBAAqB,CACpB;QAAE7C,iBAAiB,EAAE;MAAa,CACpC,CAAC;MAEHP,MAAM,CAAC2C,mBAAmB,CAACN,aAAa,CAAC,EAAE,CAAC,CAAC;MAE7C,MAAMwB,WAAW,GAAGjD,MAAM,CAACK,KAAK,CAAC,cAAc,EAAE,CAAC,EAAE,WAAW,CAAC;MAChEpB,EAAE,CAACmC,mBAAmB,CAAC,EAAE,CAAC;MAE1B,MAAMrC,MAAM,CAACkE,WAAW,CAAC,CAACC,OAAO,CAACC,OAAO,CACvC,4CACF,CAAC;MACDlE,EAAE,CAACsC,aAAa,CAAC,CAAC;IACpB,CAAC,CAAC;IAEFvC,EAAE,CAAC,mDAAmD,EAAE,YAAY;MAClE,MAAMgB,MAAM,GAAG,IAAId,gBAAgB,CAACE,MAAM,EAAE,EAAE,EAAE,GAAG,CAAC;MACpDH,EAAE,CAACkC,aAAa,CAAC,CAAC;MAElB,MAAMlB,YAAY,GAAGZ,UAAU,CAACa,iBAAiB,CAAC,CAAC;MAEnD,MAAM+C,WAAW,GAAGjD,MAAM,CAACK,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC;MAC/CpB,EAAE,CAACmC,mBAAmB,CAAC,EAAE,CAAC;MAE1BnB,YAAY,CAACP,OAAO,CAAC;QACnBc,IAAI,EAAE,CAACZ,SAAS,CAAC,CAAC,CAAC,CAAC;QACpBa,aAAa,EAAEC,SAAS;QACxBC,UAAU,EAAE;MACd,CAAC,CAAC;MAEF,MAAM5B,MAAM,CAACkE,WAAW,CAAC,CAACrC,QAAQ,CAACC,aAAa,CAAC;QAAEd,WAAW,EAAE;MAAE,CAAC,CAAC;MACpEd,EAAE,CAACsC,aAAa,CAAC,CAAC;IACpB,CAAC,CAAC;EACJ,CAAC,CAAC;AACJ,CAAC,CAAC","ignoreList":[]}
|
|
@@ -65,17 +65,11 @@ export class CacheKeys {
|
|
|
65
65
|
});
|
|
66
66
|
}
|
|
67
67
|
get(type, ...args) {
|
|
68
|
-
|
|
69
|
-
// This makes get("object", "Foo", 1, undefined) === get("object", "Foo", 1)
|
|
70
|
-
const normalizedArgs = [...args];
|
|
71
|
-
while (normalizedArgs.length > 0 && normalizedArgs[normalizedArgs.length - 1] === undefined) {
|
|
72
|
-
normalizedArgs.pop();
|
|
73
|
-
}
|
|
74
|
-
const cacheKeyArgs = [type, ...normalizedArgs];
|
|
68
|
+
const cacheKeyArgs = this.#normalizeArgs(type, args);
|
|
75
69
|
if (process.env.NODE_ENV !== "production" && DEBUG_CACHE_KEYS) {
|
|
76
70
|
// eslint-disable-next-line no-console
|
|
77
71
|
console.debug(`CacheKeys.get([${type},
|
|
78
|
-
${
|
|
72
|
+
${cacheKeyArgs.slice(1).map(x => JSON.stringify(x)).join(", ")}]) - already exists? `, this.#cacheKeys.peekArray(cacheKeyArgs) != null);
|
|
79
73
|
}
|
|
80
74
|
const cacheKey = this.#cacheKeys.lookupArray(cacheKeyArgs);
|
|
81
75
|
|
|
@@ -84,6 +78,23 @@ export class CacheKeys {
|
|
|
84
78
|
this.#refCounts.register(cacheKey);
|
|
85
79
|
return cacheKey;
|
|
86
80
|
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Look up an existing cache key without creating or registering it.
|
|
84
|
+
* Returns undefined if the key does not exist in the trie.
|
|
85
|
+
*/
|
|
86
|
+
peek(type, ...args) {
|
|
87
|
+
return this.#cacheKeys.peekArray(this.#normalizeArgs(type, args));
|
|
88
|
+
}
|
|
89
|
+
#normalizeArgs(type, args) {
|
|
90
|
+
// Normalize trailing undefined values to ensure consistent cache key lookup
|
|
91
|
+
// This makes ("object", "Foo", 1, undefined) equivalent to ("object", "Foo", 1)
|
|
92
|
+
const normalizedArgs = [...args];
|
|
93
|
+
while (normalizedArgs.length > 0 && normalizedArgs[normalizedArgs.length - 1] === undefined) {
|
|
94
|
+
normalizedArgs.pop();
|
|
95
|
+
}
|
|
96
|
+
return [type, ...normalizedArgs];
|
|
97
|
+
}
|
|
87
98
|
retain(cacheKey) {
|
|
88
99
|
this.#refCounts.retain(cacheKey);
|
|
89
100
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CacheKeys.js","names":["Trie","DEBUG_CACHE_KEYS","DEBUG_REFCOUNTS","RefCounts","CacheKeys","cacheKeys","keys","cacheKey","type","otherKeys","slice","onCreate","console","log","JSON","stringify","finalizationRegistry","register","refCounts","k","cleanupCacheKey","onDestroy","constructor","setInterval","gc","FinalizationRegistry","cleanupCallback","e","error","get","args","
|
|
1
|
+
{"version":3,"file":"CacheKeys.js","names":["Trie","DEBUG_CACHE_KEYS","DEBUG_REFCOUNTS","RefCounts","CacheKeys","cacheKeys","keys","cacheKey","type","otherKeys","slice","onCreate","console","log","JSON","stringify","finalizationRegistry","register","refCounts","k","cleanupCacheKey","onDestroy","constructor","setInterval","gc","FinalizationRegistry","cleanupCallback","e","error","get","args","cacheKeyArgs","normalizeArgs","process","env","NODE_ENV","debug","map","x","join","peekArray","lookupArray","peek","#normalizeArgs","normalizedArgs","length","undefined","pop","retain","release","remove","#remove","key"],"sources":["CacheKeys.ts"],"sourcesContent":["/*\n * Copyright 2025 Palantir Technologies, Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Trie } from \"@wry/trie\";\nimport { DEBUG_CACHE_KEYS, DEBUG_REFCOUNTS } from \"../DebugFlags.js\";\nimport type { CacheKey } from \"./CacheKey.js\";\nimport { RefCounts } from \"./RefCounts.js\";\n\n/**\n * Cache key management with canonicalization.\n * - Uses Trie structure for efficient storage and lookup\n */\nexport class CacheKeys<TCacheKey extends CacheKey> {\n #cacheKeys = new Trie<TCacheKey>(false, (keys) => {\n const cacheKey = {\n type: keys[0],\n otherKeys: keys.slice(1),\n } as unknown as TCacheKey;\n this.#onCreate?.(cacheKey);\n\n if (DEBUG_REFCOUNTS) {\n // eslint-disable-next-line no-console\n console.log(\n `CacheKeys.onCreate(${cacheKey.type}, ${\n JSON.stringify(cacheKey.otherKeys)\n })`,\n );\n\n this.#finalizationRegistry.register(cacheKey, () => {\n // eslint-disable-next-line no-console\n console.log(\n `CacheKey Finalization(${cacheKey.type}, ${\n JSON.stringify(cacheKey.otherKeys)\n })`,\n );\n });\n }\n return cacheKey;\n });\n\n #refCounts = new RefCounts<TCacheKey>(\n DEBUG_REFCOUNTS ? 15_000 : 60_000,\n (k) => this.#cleanupCacheKey(k),\n );\n\n // we are currently only using this for debug logging and should just remove it in the future if that\n // continues to be true\n #finalizationRegistry: FinalizationRegistry<() => void>;\n\n #onCreate?: (cacheKey: TCacheKey) => void;\n #onDestroy?: (cacheKey: TCacheKey) => void;\n\n constructor(\n { onCreate, onDestroy }: {\n onCreate?: (cacheKey: TCacheKey) => void;\n onDestroy?: (cacheKey: TCacheKey) => void;\n },\n ) {\n this.#onCreate = onCreate;\n this.#onDestroy = onDestroy;\n\n setInterval(() => {\n this.#refCounts.gc();\n }, 1000);\n\n this.#finalizationRegistry = new FinalizationRegistry<() => void>(\n (cleanupCallback) => {\n try {\n cleanupCallback();\n } catch (e) {\n // eslint-disable-next-line no-console\n console.error(\n \"Caught an error while running a finalization callback\",\n e,\n );\n }\n },\n );\n }\n\n get<K extends TCacheKey>(\n type: K[\"type\"],\n ...args: K[\"__cacheKey\"][\"args\"]\n ): K {\n const cacheKeyArgs = this.#normalizeArgs(type, args);\n if (process.env.NODE_ENV !== \"production\" && DEBUG_CACHE_KEYS) {\n // eslint-disable-next-line no-console\n console.debug(\n `CacheKeys.get([${type},\n ${\n cacheKeyArgs.slice(1).map(x => JSON.stringify(x)).join(\", \")\n }]) - already exists? `,\n this.#cacheKeys.peekArray(cacheKeyArgs) != null,\n );\n }\n\n const cacheKey = this.#cacheKeys.lookupArray(\n cacheKeyArgs,\n ) as K;\n\n // This is an idempotent call that ensures the cache key is registered for\n // cleanup. If already registered, this does nothing.\n this.#refCounts.register(cacheKey);\n\n return cacheKey;\n }\n\n /**\n * Look up an existing cache key without creating or registering it.\n * Returns undefined if the key does not exist in the trie.\n */\n peek<K extends TCacheKey>(\n type: K[\"type\"],\n ...args: K[\"__cacheKey\"][\"args\"]\n ): K | undefined {\n return this.#cacheKeys.peekArray(\n this.#normalizeArgs(type, args),\n ) as K | undefined;\n }\n\n #normalizeArgs(\n type: TCacheKey[\"type\"],\n args: unknown[],\n ): unknown[] {\n // Normalize trailing undefined values to ensure consistent cache key lookup\n // This makes (\"object\", \"Foo\", 1, undefined) equivalent to (\"object\", \"Foo\", 1)\n const normalizedArgs = [...args];\n while (\n normalizedArgs.length > 0\n && normalizedArgs[normalizedArgs.length - 1] === undefined\n ) {\n normalizedArgs.pop();\n }\n return [type, ...normalizedArgs];\n }\n\n retain(cacheKey: TCacheKey): void {\n this.#refCounts.retain(cacheKey);\n }\n\n release(cacheKey: TCacheKey): void {\n this.#refCounts.release(cacheKey);\n }\n\n #remove<K extends TCacheKey>(cacheKey: K): void {\n this.#cacheKeys.remove(cacheKey.type, ...cacheKey.otherKeys);\n }\n\n /**\n * Called after a key is no longer retained and the timeout has elapsed\n * @param key\n */\n #cleanupCacheKey = (key: TCacheKey) => {\n this.#onDestroy?.(key);\n this.#remove(key);\n };\n}\n"],"mappings":"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","ignoreList":[]}
|