joist-core 2.3.0-next.97 → 2.3.0-next.98
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/build/EntityManager.cjs +45 -46
- package/build/EntityManager.cjs.map +1 -1
- package/build/EntityManager.d.cts +7 -7
- package/build/EntityManager.d.mts +7 -7
- package/build/EntityManager.js +27 -28
- package/build/EntityManager.js.map +1 -1
- package/build/EntityMetadata.cjs +0 -2
- package/build/EntityMetadata.cjs.map +1 -1
- package/build/EntityMetadata.d.cts +3 -3
- package/build/EntityMetadata.d.mts +3 -3
- package/build/EntityMetadata.js +0 -2
- package/build/EntityMetadata.js.map +1 -1
- package/build/IndexManager.cjs +1 -1
- package/build/IndexManager.js +1 -1
- package/build/PluginManager.cjs +0 -1
- package/build/PluginManager.cjs.map +1 -1
- package/build/PluginManager.d.cts +1 -1
- package/build/PluginManager.d.mts +1 -1
- package/build/PluginManager.js +0 -1
- package/build/PluginManager.js.map +1 -1
- package/build/batchloaders/enumCollectionBatchLoader.cjs +1 -1
- package/build/batchloaders/enumCollectionBatchLoader.d.cts +1 -1
- package/build/batchloaders/enumCollectionBatchLoader.d.mts +1 -1
- package/build/batchloaders/enumCollectionBatchLoader.js +1 -1
- package/build/batchloaders/loadBatchLoader.cjs +3 -4
- package/build/batchloaders/loadBatchLoader.cjs.map +1 -1
- package/build/batchloaders/loadBatchLoader.d.cts +1 -1
- package/build/batchloaders/loadBatchLoader.d.mts +1 -1
- package/build/batchloaders/loadBatchLoader.js +2 -3
- package/build/batchloaders/loadBatchLoader.js.map +1 -1
- package/build/batchloaders/manyToManyBatchLoader.cjs +1 -1
- package/build/batchloaders/manyToManyBatchLoader.d.cts +1 -1
- package/build/batchloaders/manyToManyBatchLoader.d.mts +1 -1
- package/build/batchloaders/manyToManyBatchLoader.js +1 -1
- package/build/batchloaders/oneToManyBatchLoader.cjs +1 -1
- package/build/batchloaders/oneToManyBatchLoader.js +1 -1
- package/build/batchloaders/oneToOneBatchLoader.cjs +1 -1
- package/build/batchloaders/oneToOneBatchLoader.js +1 -1
- package/build/batchloaders/populateBatchLoader.cjs +11 -12
- package/build/batchloaders/populateBatchLoader.cjs.map +1 -1
- package/build/batchloaders/populateBatchLoader.d.cts +1 -1
- package/build/batchloaders/populateBatchLoader.d.mts +1 -1
- package/build/batchloaders/populateBatchLoader.js +7 -8
- package/build/batchloaders/populateBatchLoader.js.map +1 -1
- package/build/batchloaders/recursiveChildrenBatchLoader.cjs +2 -2
- package/build/batchloaders/recursiveChildrenBatchLoader.js +2 -2
- package/build/batchloaders/recursiveM2mBatchLoader.cjs +2 -2
- package/build/batchloaders/recursiveM2mBatchLoader.js +2 -2
- package/build/batchloaders/recursiveParentsBatchLoader.cjs +2 -2
- package/build/batchloaders/recursiveParentsBatchLoader.js +2 -2
- package/build/changes.cjs +2 -3
- package/build/changes.cjs.map +1 -1
- package/build/changes.js +2 -3
- package/build/changes.js.map +1 -1
- package/build/config.cjs +3 -3
- package/build/config.cjs.map +1 -1
- package/build/config.d.cts +2 -2
- package/build/config.d.mts +2 -2
- package/build/config.js +1 -1
- package/build/config.js.map +1 -1
- package/build/configure.cjs +10 -10
- package/build/configure.cjs.map +1 -1
- package/build/configure.js +5 -5
- package/build/configure.js.map +1 -1
- package/build/dataloaders/fastWhereFilterHash.cjs +8 -8
- package/build/dataloaders/fastWhereFilterHash.cjs.map +1 -1
- package/build/dataloaders/fastWhereFilterHash.d.cts.map +1 -1
- package/build/dataloaders/fastWhereFilterHash.d.mts.map +1 -1
- package/build/dataloaders/fastWhereFilterHash.js +3 -3
- package/build/dataloaders/fastWhereFilterHash.js.map +1 -1
- package/build/dataloaders/findByUniqueDataLoader.cjs.map +1 -1
- package/build/dataloaders/findByUniqueDataLoader.js.map +1 -1
- package/build/dataloaders/findCountDataLoader.cjs +2 -2
- package/build/dataloaders/findCountDataLoader.cjs.map +1 -1
- package/build/dataloaders/findCountDataLoader.js +1 -1
- package/build/dataloaders/findCountDataLoader.js.map +1 -1
- package/build/dataloaders/findDataLoader.cjs +6 -6
- package/build/dataloaders/findDataLoader.cjs.map +1 -1
- package/build/dataloaders/findDataLoader.d.cts +1 -1
- package/build/dataloaders/findDataLoader.d.mts +1 -1
- package/build/dataloaders/findDataLoader.js +3 -3
- package/build/dataloaders/findDataLoader.js.map +1 -1
- package/build/dataloaders/findIdsDataLoader.cjs +3 -3
- package/build/dataloaders/findIdsDataLoader.cjs.map +1 -1
- package/build/dataloaders/findIdsDataLoader.js +2 -2
- package/build/dataloaders/findIdsDataLoader.js.map +1 -1
- package/build/dataloaders/findOrCreateDataLoader.cjs +1 -1
- package/build/dataloaders/findOrCreateDataLoader.js +1 -1
- package/build/dataloaders/findPaginatedDataLoader.cjs +6 -6
- package/build/dataloaders/findPaginatedDataLoader.cjs.map +1 -1
- package/build/dataloaders/findPaginatedDataLoader.d.cts +1 -1
- package/build/dataloaders/findPaginatedDataLoader.d.mts +1 -1
- package/build/dataloaders/findPaginatedDataLoader.js +3 -3
- package/build/dataloaders/findPaginatedDataLoader.js.map +1 -1
- package/build/dataloaders/lensDataLoader.cjs +3 -3
- package/build/dataloaders/lensDataLoader.cjs.map +1 -1
- package/build/dataloaders/lensDataLoader.js +2 -2
- package/build/dataloaders/lensDataLoader.js.map +1 -1
- package/build/dataloaders/manyToManyFindDataLoader.cjs +1 -1
- package/build/dataloaders/manyToManyFindDataLoader.js +1 -1
- package/build/dataloaders/oneToManyFindDataLoader.cjs +1 -1
- package/build/dataloaders/oneToManyFindDataLoader.js +1 -1
- package/build/defaults.cjs +5 -5
- package/build/defaults.cjs.map +1 -1
- package/build/defaults.d.cts +1 -1
- package/build/defaults.d.mts +1 -1
- package/build/defaults.js +4 -4
- package/build/defaults.js.map +1 -1
- package/build/drivers/Driver.cjs +0 -1
- package/build/drivers/Driver.d.cts +1 -1
- package/build/drivers/Driver.d.mts +1 -1
- package/build/drivers/Driver.js +0 -1
- package/build/drivers/EntityWriter.cjs +6 -7
- package/build/drivers/EntityWriter.cjs.map +1 -1
- package/build/drivers/EntityWriter.d.cts +1 -1
- package/build/drivers/EntityWriter.d.mts +1 -1
- package/build/drivers/EntityWriter.js +2 -3
- package/build/drivers/EntityWriter.js.map +1 -1
- package/build/drivers/IdAssigner.cjs +0 -1
- package/build/drivers/IdAssigner.cjs.map +1 -1
- package/build/drivers/IdAssigner.d.cts +1 -1
- package/build/drivers/IdAssigner.d.mts +1 -1
- package/build/drivers/IdAssigner.js +0 -1
- package/build/drivers/IdAssigner.js.map +1 -1
- package/build/drivers/driverApi.cjs +19 -0
- package/build/drivers/driverApi.cjs.map +1 -0
- package/build/drivers/driverApi.d.cts +22 -0
- package/build/drivers/driverApi.d.cts.map +1 -0
- package/build/drivers/driverApi.d.mts +22 -0
- package/build/drivers/driverApi.d.mts.map +1 -0
- package/build/drivers/driverApi.js +18 -0
- package/build/drivers/driverApi.js.map +1 -0
- package/build/fields.cjs +3 -3
- package/build/fields.cjs.map +1 -1
- package/build/fields.js +2 -2
- package/build/fields.js.map +1 -1
- package/build/{FlushLock.cjs → flush/FlushLock.cjs} +1 -1
- package/build/flush/FlushLock.cjs.map +1 -0
- package/build/{FlushLock.d.cts → flush/FlushLock.d.cts} +1 -1
- package/build/flush/FlushLock.d.cts.map +1 -0
- package/build/{FlushLock.d.mts → flush/FlushLock.d.mts} +1 -1
- package/build/flush/FlushLock.d.mts.map +1 -0
- package/build/{FlushLock.js → flush/FlushLock.js} +1 -1
- package/build/flush/FlushLock.js.map +1 -0
- package/build/{JoinRows.cjs → flush/JoinRows.cjs} +5 -8
- package/build/flush/JoinRows.cjs.map +1 -0
- package/build/{JoinRows.d.cts → flush/JoinRows.d.cts} +5 -5
- package/build/flush/JoinRows.d.cts.map +1 -0
- package/build/{JoinRows.d.mts → flush/JoinRows.d.mts} +5 -5
- package/build/flush/JoinRows.d.mts.map +1 -0
- package/build/{JoinRows.js → flush/JoinRows.js} +5 -8
- package/build/flush/JoinRows.js.map +1 -0
- package/build/{PendingChanges.cjs → flush/PendingChanges.cjs} +0 -1
- package/build/{PendingChanges.d.cts → flush/PendingChanges.d.cts} +2 -2
- package/build/flush/PendingChanges.d.cts.map +1 -0
- package/build/{PendingChanges.d.mts → flush/PendingChanges.d.mts} +2 -2
- package/build/flush/PendingChanges.d.mts.map +1 -0
- package/build/flush/PendingChanges.js +1 -0
- package/build/{Todo.cjs → flush/Todo.cjs} +4 -6
- package/build/flush/Todo.cjs.map +1 -0
- package/build/{Todo.d.cts → flush/Todo.d.cts} +3 -3
- package/build/flush/Todo.d.cts.map +1 -0
- package/build/{Todo.d.mts → flush/Todo.d.mts} +3 -3
- package/build/flush/Todo.d.mts.map +1 -0
- package/build/{Todo.js → flush/Todo.js} +4 -6
- package/build/flush/Todo.js.map +1 -0
- package/build/index.cjs +99 -99
- package/build/index.cjs.map +1 -1
- package/build/index.d.cts +15 -15
- package/build/index.d.mts +15 -15
- package/build/index.js +41 -41
- package/build/index.js.map +1 -1
- package/build/json.cjs +4 -4
- package/build/json.cjs.map +1 -1
- package/build/json.js +3 -3
- package/build/json.js.map +1 -1
- package/build/keys.cjs +1 -1
- package/build/keys.js +1 -1
- package/build/{HintTree.cjs → loading/HintTree.cjs} +2 -5
- package/build/loading/HintTree.cjs.map +1 -0
- package/build/{HintTree.d.cts → loading/HintTree.d.cts} +3 -3
- package/build/loading/HintTree.d.cts.map +1 -0
- package/build/{HintTree.d.mts → loading/HintTree.d.mts} +3 -3
- package/build/loading/HintTree.d.mts.map +1 -0
- package/build/{HintTree.js → loading/HintTree.js} +2 -5
- package/build/loading/HintTree.js.map +1 -0
- package/build/{IsLoadedCache.cjs → loading/IsLoadedCache.cjs} +2 -3
- package/build/loading/IsLoadedCache.cjs.map +1 -0
- package/build/{IsLoadedCache.d.cts → loading/IsLoadedCache.d.cts} +2 -2
- package/build/loading/IsLoadedCache.d.cts.map +1 -0
- package/build/{IsLoadedCache.d.mts → loading/IsLoadedCache.d.mts} +2 -2
- package/build/loading/IsLoadedCache.d.mts.map +1 -0
- package/build/{IsLoadedCache.js → loading/IsLoadedCache.js} +2 -3
- package/build/loading/IsLoadedCache.js.map +1 -0
- package/build/{loadHints.cjs → loading/loadHints.cjs} +6 -11
- package/build/loading/loadHints.cjs.map +1 -0
- package/build/{loadHints.d.cts → loading/loadHints.d.cts} +14 -14
- package/build/loading/loadHints.d.cts.map +1 -0
- package/build/{loadHints.d.mts → loading/loadHints.d.mts} +14 -14
- package/build/loading/loadHints.d.mts.map +1 -0
- package/build/{loadHints.js → loading/loadHints.js} +3 -8
- package/build/loading/loadHints.js.map +1 -0
- package/build/{loadLens.cjs → loading/loadLens.cjs} +8 -9
- package/build/loading/loadLens.cjs.map +1 -0
- package/build/{loadLens.d.cts → loading/loadLens.d.cts} +3 -3
- package/build/loading/loadLens.d.cts.map +1 -0
- package/build/{loadLens.d.mts → loading/loadLens.d.mts} +3 -3
- package/build/loading/loadLens.d.mts.map +1 -0
- package/build/{loadLens.js → loading/loadLens.js} +8 -9
- package/build/loading/loadLens.js.map +1 -0
- package/build/{withLoaded.cjs → loading/withLoaded.cjs} +12 -13
- package/build/loading/withLoaded.cjs.map +1 -0
- package/build/{withLoaded.d.cts → loading/withLoaded.d.cts} +9 -9
- package/build/loading/withLoaded.d.cts.map +1 -0
- package/build/{withLoaded.d.mts → loading/withLoaded.d.mts} +9 -9
- package/build/loading/withLoaded.d.mts.map +1 -0
- package/build/{withLoaded.js → loading/withLoaded.js} +10 -11
- package/build/loading/withLoaded.js.map +1 -0
- package/build/logging/ReactionLogger.cjs +0 -2
- package/build/logging/ReactionLogger.cjs.map +1 -1
- package/build/logging/ReactionLogger.d.cts +2 -2
- package/build/logging/ReactionLogger.d.mts +2 -2
- package/build/logging/ReactionLogger.js +0 -2
- package/build/logging/ReactionLogger.js.map +1 -1
- package/build/newTestInstance.cjs +3 -3
- package/build/newTestInstance.cjs.map +1 -1
- package/build/newTestInstance.d.cts +1 -1
- package/build/newTestInstance.d.mts +1 -1
- package/build/newTestInstance.js +2 -2
- package/build/newTestInstance.js.map +1 -1
- package/build/normalizeHints.cjs +0 -2
- package/build/normalizeHints.cjs.map +1 -1
- package/build/normalizeHints.d.cts +2 -2
- package/build/normalizeHints.d.mts +2 -2
- package/build/normalizeHints.js +0 -2
- package/build/normalizeHints.js.map +1 -1
- package/build/nowUTC.cjs +2 -2
- package/build/nowUTC.cjs.map +1 -1
- package/build/nowUTC.d.cts +1 -1
- package/build/nowUTC.d.mts +1 -1
- package/build/nowUTC.js +1 -1
- package/build/nowUTC.js.map +1 -1
- package/build/plugins/PreloadPlugin.cjs +0 -2
- package/build/plugins/PreloadPlugin.d.cts +2 -2
- package/build/plugins/PreloadPlugin.d.mts +2 -2
- package/build/plugins/PreloadPlugin.js +0 -2
- package/build/preloading/JsonAggregatePreloader.cjs +1 -4
- package/build/preloading/JsonAggregatePreloader.cjs.map +1 -1
- package/build/preloading/JsonAggregatePreloader.d.cts +2 -2
- package/build/preloading/JsonAggregatePreloader.d.mts +2 -2
- package/build/preloading/JsonAggregatePreloader.js +1 -4
- package/build/preloading/JsonAggregatePreloader.js.map +1 -1
- package/build/preloading/partitionHint.cjs +2 -3
- package/build/preloading/partitionHint.cjs.map +1 -1
- package/build/preloading/partitionHint.d.cts +1 -1
- package/build/preloading/partitionHint.d.mts +1 -1
- package/build/preloading/partitionHint.js +2 -3
- package/build/preloading/partitionHint.js.map +1 -1
- package/build/queries/ConditionBuilder.cjs.map +1 -1
- package/build/queries/ConditionBuilder.d.cts +1 -1
- package/build/queries/ConditionBuilder.d.mts +1 -1
- package/build/queries/ConditionBuilder.js.map +1 -1
- package/build/queries/find/Aliases.cjs +1 -1
- package/build/queries/find/Aliases.cjs.map +1 -1
- package/build/queries/find/Aliases.js +1 -1
- package/build/queries/find/Aliases.js.map +1 -1
- package/build/queries/find/QueryParser.cjs +1 -1
- package/build/queries/find/QueryParser.js +1 -1
- package/build/queries/find/scopes.cjs.map +1 -1
- package/build/queries/find/scopes.d.cts +1 -1
- package/build/queries/find/scopes.d.mts +1 -1
- package/build/queries/find/scopes.js.map +1 -1
- package/build/queries/sql/Expr.cjs +2 -2
- package/build/queries/sql/Expr.cjs.map +1 -1
- package/build/queries/sql/Expr.d.cts +1 -1
- package/build/queries/sql/Expr.d.mts +1 -1
- package/build/queries/sql/Expr.js +1 -1
- package/build/queries/sql/Expr.js.map +1 -1
- package/build/queries/sql/Tables.cjs +1 -1
- package/build/queries/sql/Tables.cjs.map +1 -1
- package/build/queries/sql/Tables.js +1 -1
- package/build/queries/sql/Tables.js.map +1 -1
- package/build/queries/sql/execute.cjs +1 -1
- package/build/queries/sql/execute.cjs.map +1 -1
- package/build/queries/sql/execute.js +1 -1
- package/build/queries/sql/execute.js.map +1 -1
- package/build/queries/sql/expressions/codecs.cjs +2 -2
- package/build/queries/sql/expressions/codecs.cjs.map +1 -1
- package/build/queries/sql/expressions/codecs.js +1 -1
- package/build/queries/sql/expressions/codecs.js.map +1 -1
- package/build/queries/sql/expressions/expression.cjs.map +1 -1
- package/build/queries/sql/expressions/expression.js.map +1 -1
- package/build/queries/sql/query.cjs.map +1 -1
- package/build/queries/sql/query.d.cts +1 -1
- package/build/queries/sql/query.d.mts +1 -1
- package/build/queries/sql/query.js.map +1 -1
- package/build/{unnest.cjs → queries/unnest.cjs} +1 -2
- package/build/queries/unnest.cjs.map +1 -0
- package/build/{unnest.d.cts → queries/unnest.d.cts} +3 -3
- package/build/queries/unnest.d.cts.map +1 -0
- package/build/{unnest.d.mts → queries/unnest.d.mts} +3 -3
- package/build/queries/unnest.d.mts.map +1 -0
- package/build/{unnest.js → queries/unnest.js} +1 -2
- package/build/queries/unnest.js.map +1 -0
- package/build/queries/valueFilters.cjs.map +1 -1
- package/build/queries/valueFilters.d.cts +1 -1
- package/build/queries/valueFilters.d.mts +1 -1
- package/build/queries/valueFilters.js.map +1 -1
- package/build/{ReactionsManager.cjs → reactivity/ReactionsManager.cjs} +8 -10
- package/build/reactivity/ReactionsManager.cjs.map +1 -0
- package/build/{ReactionsManager.d.cts → reactivity/ReactionsManager.d.cts} +5 -6
- package/build/reactivity/ReactionsManager.d.cts.map +1 -0
- package/build/{ReactionsManager.d.mts → reactivity/ReactionsManager.d.mts} +5 -6
- package/build/reactivity/ReactionsManager.d.mts.map +1 -0
- package/build/{ReactionsManager.js → reactivity/ReactionsManager.js} +6 -8
- package/build/reactivity/ReactionsManager.js.map +1 -0
- package/build/{reactiveHints.cjs → reactivity/reactiveHints.cjs} +12 -16
- package/build/reactivity/reactiveHints.cjs.map +1 -0
- package/build/{reactiveHints.d.cts → reactivity/reactiveHints.d.cts} +17 -17
- package/build/reactivity/reactiveHints.d.cts.map +1 -0
- package/build/{reactiveHints.d.mts → reactivity/reactiveHints.d.mts} +17 -17
- package/build/reactivity/reactiveHints.d.mts.map +1 -0
- package/build/{reactiveHints.js → reactivity/reactiveHints.js} +12 -16
- package/build/reactivity/reactiveHints.js.map +1 -0
- package/build/relations/AsyncReactiveField.cjs +6 -6
- package/build/relations/AsyncReactiveField.cjs.map +1 -1
- package/build/relations/AsyncReactiveField.d.cts +1 -1
- package/build/relations/AsyncReactiveField.d.mts +1 -1
- package/build/relations/AsyncReactiveField.js +4 -4
- package/build/relations/AsyncReactiveField.js.map +1 -1
- package/build/relations/Collection.cjs +1 -1
- package/build/relations/Collection.js +1 -1
- package/build/relations/CustomCollection.d.cts +1 -1
- package/build/relations/CustomCollection.d.mts +1 -1
- package/build/relations/CustomReference.cjs +1 -1
- package/build/relations/CustomReference.d.cts +1 -1
- package/build/relations/CustomReference.d.mts +1 -1
- package/build/relations/CustomReference.js +1 -1
- package/build/relations/EnumCollection.d.cts +1 -1
- package/build/relations/EnumCollection.d.mts +1 -1
- package/build/relations/LazyField.cjs +4 -4
- package/build/relations/LazyField.js +4 -4
- package/build/relations/ManyToManyCollection.cjs +3 -3
- package/build/relations/ManyToManyCollection.js +3 -3
- package/build/relations/ManyToManyLargeCollection.cjs +3 -3
- package/build/relations/ManyToManyLargeCollection.js +3 -3
- package/build/relations/ManyToOneReference.cjs +4 -4
- package/build/relations/ManyToOneReference.js +4 -4
- package/build/relations/OneToManyCollection.cjs +1 -2
- package/build/relations/OneToManyCollection.cjs.map +1 -1
- package/build/relations/OneToManyCollection.d.cts +1 -1
- package/build/relations/OneToManyCollection.d.mts +1 -1
- package/build/relations/OneToManyCollection.js +1 -2
- package/build/relations/OneToManyCollection.js.map +1 -1
- package/build/relations/OneToManyLargeCollection.cjs +1 -1
- package/build/relations/OneToManyLargeCollection.js +1 -1
- package/build/relations/OneToOneReference.cjs +5 -5
- package/build/relations/OneToOneReference.js +5 -5
- package/build/relations/PolymorphicReference.cjs +3 -3
- package/build/relations/PolymorphicReference.js +3 -3
- package/build/relations/ReactiveField.cjs +5 -6
- package/build/relations/ReactiveField.cjs.map +1 -1
- package/build/relations/ReactiveField.d.cts +2 -2
- package/build/relations/ReactiveField.d.mts +2 -2
- package/build/relations/ReactiveField.js +3 -4
- package/build/relations/ReactiveField.js.map +1 -1
- package/build/relations/ReactiveGetter.cjs +0 -1
- package/build/relations/ReactiveGetter.cjs.map +1 -1
- package/build/relations/ReactiveGetter.d.cts +1 -1
- package/build/relations/ReactiveGetter.d.mts +1 -1
- package/build/relations/ReactiveGetter.js +0 -1
- package/build/relations/ReactiveGetter.js.map +1 -1
- package/build/relations/ReactiveManyToMany.cjs +5 -6
- package/build/relations/ReactiveManyToMany.cjs.map +1 -1
- package/build/relations/ReactiveManyToMany.d.cts +2 -2
- package/build/relations/ReactiveManyToMany.d.cts.map +1 -1
- package/build/relations/ReactiveManyToMany.d.mts +2 -2
- package/build/relations/ReactiveManyToMany.d.mts.map +1 -1
- package/build/relations/ReactiveManyToMany.js +3 -4
- package/build/relations/ReactiveManyToMany.js.map +1 -1
- package/build/relations/ReactiveManyToManyOtherSide.cjs +1 -3
- package/build/relations/ReactiveManyToManyOtherSide.cjs.map +1 -1
- package/build/relations/ReactiveManyToManyOtherSide.d.cts +2 -2
- package/build/relations/ReactiveManyToManyOtherSide.d.mts +2 -2
- package/build/relations/ReactiveManyToManyOtherSide.js +1 -3
- package/build/relations/ReactiveManyToManyOtherSide.js.map +1 -1
- package/build/relations/ReactiveReference.cjs +8 -9
- package/build/relations/ReactiveReference.cjs.map +1 -1
- package/build/relations/ReactiveReference.d.cts +2 -2
- package/build/relations/ReactiveReference.d.cts.map +1 -1
- package/build/relations/ReactiveReference.d.mts +2 -2
- package/build/relations/ReactiveReference.d.mts.map +1 -1
- package/build/relations/ReactiveReference.js +6 -7
- package/build/relations/ReactiveReference.js.map +1 -1
- package/build/relations/RecursiveCollection.cjs +3 -4
- package/build/relations/RecursiveCollection.cjs.map +1 -1
- package/build/relations/RecursiveCollection.d.cts +1 -1
- package/build/relations/RecursiveCollection.d.mts +1 -1
- package/build/relations/RecursiveCollection.js +3 -4
- package/build/relations/RecursiveCollection.js.map +1 -1
- package/build/relations/Reference.cjs +1 -1
- package/build/relations/Reference.js +1 -1
- package/build/relations/hasAsyncMethod.cjs +2 -2
- package/build/relations/hasAsyncMethod.cjs.map +1 -1
- package/build/relations/hasAsyncMethod.d.cts +1 -1
- package/build/relations/hasAsyncMethod.d.mts +1 -1
- package/build/relations/hasAsyncMethod.js +1 -1
- package/build/relations/hasAsyncMethod.js.map +1 -1
- package/build/relations/hasManyDerived.cjs +3 -3
- package/build/relations/hasManyDerived.cjs.map +1 -1
- package/build/relations/hasManyDerived.d.cts +1 -1
- package/build/relations/hasManyDerived.d.mts +1 -1
- package/build/relations/hasManyDerived.js +2 -2
- package/build/relations/hasManyDerived.js.map +1 -1
- package/build/relations/hasManyThrough.cjs +6 -6
- package/build/relations/hasManyThrough.cjs.map +1 -1
- package/build/relations/hasManyThrough.d.cts +1 -1
- package/build/relations/hasManyThrough.d.mts +1 -1
- package/build/relations/hasManyThrough.js +2 -2
- package/build/relations/hasOneDerived.cjs +3 -3
- package/build/relations/hasOneDerived.cjs.map +1 -1
- package/build/relations/hasOneDerived.d.cts +1 -1
- package/build/relations/hasOneDerived.d.mts +1 -1
- package/build/relations/hasOneDerived.js +2 -2
- package/build/relations/hasOneDerived.js.map +1 -1
- package/build/relations/hasOneThrough.cjs +7 -7
- package/build/relations/hasOneThrough.cjs.map +1 -1
- package/build/relations/hasOneThrough.d.cts +1 -1
- package/build/relations/hasOneThrough.d.mts +1 -1
- package/build/relations/hasOneThrough.js +2 -2
- package/build/relations/hasProperty.cjs +4 -4
- package/build/relations/hasProperty.cjs.map +1 -1
- package/build/relations/hasProperty.d.cts +2 -2
- package/build/relations/hasProperty.d.mts +2 -2
- package/build/relations/hasProperty.js +2 -2
- package/build/relations/hasProperty.js.map +1 -1
- package/build/relations/index.cjs +10 -10
- package/build/relations/index.js +10 -10
- package/build/rules.cjs +3 -4
- package/build/rules.cjs.map +1 -1
- package/build/rules.d.cts +1 -1
- package/build/rules.d.mts +1 -1
- package/build/rules.js +3 -4
- package/build/rules.js.map +1 -1
- package/build/{TypeInfo.cjs → serde/TypeInfo.cjs} +1 -1
- package/build/serde/TypeInfo.cjs.map +1 -0
- package/build/{TypeInfo.d.cts → serde/TypeInfo.d.cts} +2 -2
- package/build/serde/TypeInfo.d.cts.map +1 -0
- package/build/{TypeInfo.d.mts → serde/TypeInfo.d.mts} +2 -2
- package/build/serde/TypeInfo.d.mts.map +1 -0
- package/build/{TypeInfo.js → serde/TypeInfo.js} +1 -1
- package/build/serde/TypeInfo.js.map +1 -0
- package/build/{columns.cjs → serde/columns.cjs} +1 -1
- package/build/serde/columns.cjs.map +1 -0
- package/build/{columns.d.cts → serde/columns.d.cts} +2 -2
- package/build/serde/columns.d.cts.map +1 -0
- package/build/{columns.d.mts → serde/columns.d.mts} +2 -2
- package/build/serde/columns.d.mts.map +1 -0
- package/build/{columns.js → serde/columns.js} +1 -1
- package/build/serde/columns.js.map +1 -0
- package/build/{fieldSerde.cjs → serde/fieldSerde.cjs} +6 -6
- package/build/serde/fieldSerde.cjs.map +1 -0
- package/build/{fieldSerde.d.cts → serde/fieldSerde.d.cts} +5 -5
- package/build/serde/fieldSerde.d.cts.map +1 -0
- package/build/{fieldSerde.d.mts → serde/fieldSerde.d.mts} +5 -5
- package/build/serde/fieldSerde.d.mts.map +1 -0
- package/build/{fieldSerde.js → serde/fieldSerde.js} +6 -6
- package/build/serde/fieldSerde.js.map +1 -0
- package/build/{serde.cjs → serde/serde.cjs} +25 -26
- package/build/serde/serde.cjs.map +1 -0
- package/build/{serde.d.cts → serde/serde.d.cts} +1 -1
- package/build/serde/serde.d.cts.map +1 -0
- package/build/{serde.d.mts → serde/serde.d.mts} +1 -1
- package/build/serde/serde.d.mts.map +1 -0
- package/build/{serde.js → serde/serde.js} +4 -5
- package/build/serde/serde.js.map +1 -0
- package/build/{temporal.cjs → serde/temporal.cjs} +1 -1
- package/build/serde/temporal.cjs.map +1 -0
- package/build/{temporal.d.cts → serde/temporal.d.cts} +1 -1
- package/build/serde/temporal.d.cts.map +1 -0
- package/build/{temporal.d.mts → serde/temporal.d.mts} +1 -1
- package/build/serde/temporal.d.mts.map +1 -0
- package/build/{temporal.js → serde/temporal.js} +1 -1
- package/build/serde/temporal.js.map +1 -0
- package/build/{temporalMappers.cjs → serde/temporalMappers.cjs} +4 -5
- package/build/serde/temporalMappers.cjs.map +1 -0
- package/build/{temporalMappers.d.cts → serde/temporalMappers.d.cts} +1 -1
- package/build/serde/temporalMappers.d.cts.map +1 -0
- package/build/{temporalMappers.d.mts → serde/temporalMappers.d.mts} +1 -1
- package/build/serde/temporalMappers.d.mts.map +1 -0
- package/build/{temporalMappers.js → serde/temporalMappers.js} +2 -3
- package/build/serde/temporalMappers.js.map +1 -0
- package/build/upsert.cjs +1 -1
- package/build/upsert.js +1 -1
- package/build/utils.cjs +0 -1
- package/build/utils.cjs.map +1 -1
- package/build/utils.d.cts +1 -1
- package/build/utils.d.mts +1 -1
- package/build/utils.js +0 -1
- package/build/utils.js.map +1 -1
- package/package.json +2 -2
- package/build/FlushLock.cjs.map +0 -1
- package/build/FlushLock.d.cts.map +0 -1
- package/build/FlushLock.d.mts.map +0 -1
- package/build/FlushLock.js.map +0 -1
- package/build/HintTree.cjs.map +0 -1
- package/build/HintTree.d.cts.map +0 -1
- package/build/HintTree.d.mts.map +0 -1
- package/build/HintTree.js.map +0 -1
- package/build/IsLoadedCache.cjs.map +0 -1
- package/build/IsLoadedCache.d.cts.map +0 -1
- package/build/IsLoadedCache.d.mts.map +0 -1
- package/build/IsLoadedCache.js.map +0 -1
- package/build/JoinRows.cjs.map +0 -1
- package/build/JoinRows.d.cts.map +0 -1
- package/build/JoinRows.d.mts.map +0 -1
- package/build/JoinRows.js.map +0 -1
- package/build/PendingChanges.d.cts.map +0 -1
- package/build/PendingChanges.d.mts.map +0 -1
- package/build/PendingChanges.js +0 -2
- package/build/ReactionsManager.cjs.map +0 -1
- package/build/ReactionsManager.d.cts.map +0 -1
- package/build/ReactionsManager.d.mts.map +0 -1
- package/build/ReactionsManager.js.map +0 -1
- package/build/Todo.cjs.map +0 -1
- package/build/Todo.d.cts.map +0 -1
- package/build/Todo.d.mts.map +0 -1
- package/build/Todo.js.map +0 -1
- package/build/TypeInfo.cjs.map +0 -1
- package/build/TypeInfo.d.cts.map +0 -1
- package/build/TypeInfo.d.mts.map +0 -1
- package/build/TypeInfo.js.map +0 -1
- package/build/columns.cjs.map +0 -1
- package/build/columns.d.cts.map +0 -1
- package/build/columns.d.mts.map +0 -1
- package/build/columns.js.map +0 -1
- package/build/driverApi.cjs +0 -18
- package/build/driverApi.cjs.map +0 -1
- package/build/driverApi.d.cts +0 -22
- package/build/driverApi.d.cts.map +0 -1
- package/build/driverApi.d.mts +0 -22
- package/build/driverApi.d.mts.map +0 -1
- package/build/driverApi.js +0 -18
- package/build/driverApi.js.map +0 -1
- package/build/fieldSerde.cjs.map +0 -1
- package/build/fieldSerde.d.cts.map +0 -1
- package/build/fieldSerde.d.mts.map +0 -1
- package/build/fieldSerde.js.map +0 -1
- package/build/loadHints.cjs.map +0 -1
- package/build/loadHints.d.cts.map +0 -1
- package/build/loadHints.d.mts.map +0 -1
- package/build/loadHints.js.map +0 -1
- package/build/loadLens.cjs.map +0 -1
- package/build/loadLens.d.cts.map +0 -1
- package/build/loadLens.d.mts.map +0 -1
- package/build/loadLens.js.map +0 -1
- package/build/reactiveHints.cjs.map +0 -1
- package/build/reactiveHints.d.cts.map +0 -1
- package/build/reactiveHints.d.mts.map +0 -1
- package/build/reactiveHints.js.map +0 -1
- package/build/serde.cjs.map +0 -1
- package/build/serde.d.cts.map +0 -1
- package/build/serde.d.mts.map +0 -1
- package/build/serde.js.map +0 -1
- package/build/temporal.cjs.map +0 -1
- package/build/temporal.d.cts.map +0 -1
- package/build/temporal.d.mts.map +0 -1
- package/build/temporal.js.map +0 -1
- package/build/temporalMappers.cjs.map +0 -1
- package/build/temporalMappers.d.cts.map +0 -1
- package/build/temporalMappers.d.mts.map +0 -1
- package/build/temporalMappers.js.map +0 -1
- package/build/unnest.cjs.map +0 -1
- package/build/unnest.d.cts.map +0 -1
- package/build/unnest.d.mts.map +0 -1
- package/build/unnest.js.map +0 -1
- package/build/withLoaded.cjs.map +0 -1
- package/build/withLoaded.d.cts.map +0 -1
- package/build/withLoaded.d.mts.map +0 -1
- package/build/withLoaded.js.map +0 -1
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
require("./BatchLoader.cjs");
|
|
3
|
+
const require_normalizeHints = require("../normalizeHints.cjs");
|
|
4
|
+
const require_reactivity_reactiveHints = require("../reactivity/reactiveHints.cjs");
|
|
5
|
+
const require_keys = require("../keys.cjs");
|
|
3
6
|
const require_queries_sql_keywords = require("../queries/sql/keywords.cjs");
|
|
4
|
-
const
|
|
7
|
+
const require_batchloaders_manyToManyBatchLoader = require("./manyToManyBatchLoader.cjs");
|
|
5
8
|
const require_batchloaders_oneToManyBatchLoader = require("./oneToManyBatchLoader.cjs");
|
|
6
9
|
const require_queries_find_QueryParser = require("../queries/find/QueryParser.cjs");
|
|
7
|
-
const
|
|
8
|
-
const require_normalizeHints = require("../normalizeHints.cjs");
|
|
9
|
-
require("../loadHints.cjs");
|
|
10
|
-
const require_utils = require("../utils.cjs");
|
|
11
|
-
const require_batchloaders_manyToManyBatchLoader = require("./manyToManyBatchLoader.cjs");
|
|
10
|
+
const require_batchloaders_oneToOneBatchLoader = require("./oneToOneBatchLoader.cjs");
|
|
12
11
|
const require_relations_ReactiveField = require("../relations/ReactiveField.cjs");
|
|
13
|
-
const
|
|
12
|
+
const require_utils = require("../utils.cjs");
|
|
14
13
|
require("../EntityMetadata.cjs");
|
|
15
14
|
const require_queries_sql_AliasAssigner = require("../queries/sql/AliasAssigner.cjs");
|
|
16
|
-
const
|
|
15
|
+
const require_loading_HintTree = require("../loading/HintTree.cjs");
|
|
17
16
|
require("../index.cjs");
|
|
18
17
|
const require_batchloaders_loadBatchLoader = require("./loadBatchLoader.cjs");
|
|
19
18
|
const require_EntityManager = require("../EntityManager.cjs");
|
|
@@ -72,11 +71,11 @@ function populateBatchLoader(em, meta, hint, mode, opts = {}) {
|
|
|
72
71
|
const fallbackPromises = [];
|
|
73
72
|
for (const [key, tree] of Object.entries(layerNode.hints)) {
|
|
74
73
|
const field = layerMeta?.allFields[key];
|
|
75
|
-
const isPoly =
|
|
74
|
+
const isPoly = require_reactivity_reactiveHints.isPolyHint(key);
|
|
76
75
|
let loader;
|
|
77
76
|
let batchPromise;
|
|
78
77
|
for (const entity of tree.entities) {
|
|
79
|
-
const relation = isPoly ?
|
|
78
|
+
const relation = isPoly ? require_reactivity_reactiveHints.getRelationFromMaybePolyKey(entity, key) : entity[key];
|
|
80
79
|
if (!relation || typeof relation.load !== "function") {
|
|
81
80
|
if (isPoly) continue;
|
|
82
81
|
throw new Error(`Invalid load hint '${key}' on ${entity}`);
|
|
@@ -123,7 +122,7 @@ function populateBatchLoader(em, meta, hint, mode, opts = {}) {
|
|
|
123
122
|
if (Object.keys(tree.hints).length === 0) continue;
|
|
124
123
|
const childrenByParent = /* @__PURE__ */ new Map();
|
|
125
124
|
for (const entity of tree.entities) {
|
|
126
|
-
const relation =
|
|
125
|
+
const relation = require_reactivity_reactiveHints.getRelationFromMaybePolyKey(entity, key);
|
|
127
126
|
childrenByParent.set(entity, relation ? require_utils.toArray(getEvenDeleted(relation)) : []);
|
|
128
127
|
}
|
|
129
128
|
if (childrenByParent.size === 0) continue;
|
|
@@ -142,7 +141,7 @@ function populateBatchLoader(em, meta, hint, mode, opts = {}) {
|
|
|
142
141
|
}
|
|
143
142
|
if (nestedLoadPromises) await Promise.all(nestedLoadPromises);
|
|
144
143
|
}
|
|
145
|
-
await populateLayer(meta,
|
|
144
|
+
await populateLayer(meta, require_loading_HintTree.buildHintTree(populates));
|
|
146
145
|
});
|
|
147
146
|
}
|
|
148
147
|
/** Probes `relation` to see if it's a m2o/o2m/m2m relation that supports `getWithDeleted`, otherwise calls `get`. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"populateBatchLoader.cjs","names":["hintKey","getEmInternalApi","AliasAssigner","keyToNumber","kqDot","indexBy","isPolyHint","getRelationFromMaybePolyKey","ReactiveFieldImpl","oneToManyBatchLoader","manyToManyBatchLoader","oneToOneBatchLoader","loadBatchLoader","toArray","buildHintTree"],"sources":["../../src/batchloaders/populateBatchLoader.ts"],"sourcesContent":["import { type Entity } from \"../Entity.ts\";\nimport { type EntityMetadata } from \"../EntityMetadata.ts\";\nimport { type HintNode, buildHintTree } from \"../HintTree.ts\";\nimport {\n AliasAssigner,\n type EntityManager,\n type ParsedFindQuery,\n addTablePerClassJoinsAndClassTag,\n getEmInternalApi,\n indexBy,\n keyToNumber,\n kqDot,\n} from \"../index.ts\";\nimport { type LoadHint } from \"../loadHints.ts\";\nimport { hintKey } from \"../normalizeHints.ts\";\nimport { getRelationFromMaybePolyKey, isPolyHint } from \"../reactiveHints.ts\";\nimport { ReactiveFieldImpl } from \"../relations/ReactiveField.ts\";\nimport { toArray } from \"../utils.ts\";\nimport { type BatchLoader } from \"./BatchLoader.ts\";\nimport { loadBatchLoader } from \"./loadBatchLoader.ts\";\nimport { manyToManyBatchLoader } from \"./manyToManyBatchLoader.ts\";\nimport { oneToManyBatchLoader } from \"./oneToManyBatchLoader.ts\";\nimport { oneToOneBatchLoader } from \"./oneToOneBatchLoader.ts\";\n\nexport const populateOperation = \"populate\";\n\nexport function populateBatchLoader(\n em: EntityManager,\n meta: EntityMetadata,\n hint: LoadHint<any>,\n mode: \"preload\" | \"intermixed\",\n opts: { forceReload?: boolean } = {},\n): BatchLoader<{ entity: Entity; hint: LoadHint<any> }> {\n // For batching populates, we want different levels of course-ness:\n // - preloading populates that are only SQL should be batched together as much as possible, but\n // - intermixed populates (some with custom relations) should be batched as separately as possible\n // (while still batching identical populates together) to reduce the chance of promise deadlocks.\n //\n // I.e. if we know this is a sql-only hint, we can batch all loads for a given entity\n // together do leverage join-based preloading.\n //\n // However, intermixed batches can be prone to promise deadlocking (one relation .load getting\n // stuck in a batch that is asking for its own dependencies to load), so if this is an intermixed\n // hint, then use a batch key that includes the hint itself, which will make it unlikely\n // for non-sql relations to deadlock on each other/themselves.\n const batchKey =\n mode === \"preload\" ? `${meta.tagName}:${opts.forceReload}` : `${meta.tagName}:${hintKey(hint)}:${opts.forceReload}`;\n return em.getBatchLoader(populateOperation, batchKey, async (populates) => {\n async function populateLayer(layerMeta: EntityMetadata | undefined, layerNode: HintNode<Entity>): Promise<void> {\n // Skip join-based preloading if nothing in this layer needs loading. If any entity in the list\n // needs loading, just load everything\n const { preloader } = getEmInternalApi(em);\n // We may not have a layerMeta if we're going through non-field properties\n if (preloader && layerMeta) {\n const preloadThisLayer = Object.entries(layerNode.hints).some(([key, hint]) => {\n for (const entity of hint.entities) {\n if ((entity as any)[key]?.isLoaded === false && !(entity as any)[key]?.isPreloaded) return true;\n }\n return false;\n });\n if (preloadThisLayer) {\n // Do an up-front SQL call of `select id, ...preloads... from table`,\n const assigner = new AliasAssigner();\n const meta = layerMeta;\n const alias = assigner.getAlias(meta.tableName);\n const entities = [...layerNode.entities].filter((e) => !e.isNewEntity);\n const ids = entities.map((e) => keyToNumber(meta, e.id));\n // Create a ParsedFindQuery for `addPreloading` to inject joins into\n const query: ParsedFindQuery = {\n // We already have the entities loaded, so can do just `SELECT a.id` + the preload columns\n selects: [kqDot(alias, \"id\")],\n tables: [{ alias, join: \"primary\", table: meta.tableName }],\n condition: {\n kind: \"exp\",\n op: \"and\",\n conditions: [\n { kind: \"column\", alias, column: \"id\", dbType: meta.idDbType, cond: { kind: \"in\", value: ids } },\n ],\n },\n orderBys: [],\n };\n // If we're selecting from small_publishers, join in our base class in case\n // the preloader wants to join on a column like `publishers.group_id`\n addTablePerClassJoinsAndClassTag(query, meta, alias, false);\n const hydrator = preloader.addPreloading(meta, layerNode, query);\n if (hydrator) {\n const rows = await em[\"executeFind\"](meta, populateOperation, query, {});\n const entitiesById = indexBy(entities, (e) => keyToNumber(meta, e.id));\n const entitiesInOrder = rows.map((row) => entitiesById.get(row[\"id\"]));\n hydrator(rows, entitiesInOrder);\n }\n }\n }\n\n // First pass: batch SQL relations directly, fall back to relation.load() for non-SQL\n const batchPromises = new Set<Promise<void>>();\n const relationsToPreload: { preload(): void }[] = [];\n const fallbackPromises: (Promise<any> | undefined)[] = [];\n\n for (const [key, tree] of Object.entries(layerNode.hints)) {\n const field = layerMeta?.allFields[key];\n // Hoist the poly-key parsing out of the per-entity loop; most keys are not poly\n const isPoly = isPolyHint(key);\n let loader: BatchLoader<any> | undefined;\n let batchPromise: Promise<void> | undefined;\n for (const entity of tree.entities) {\n const relation = isPoly ? getRelationFromMaybePolyKey(entity, key) : (entity as any)[key];\n // This happens to let through non-relation hints like 'name' on user, which wasn't intentional,\n // but currently doesn't blow up (somehow), and is depended on by internal tests.\n if (!relation || typeof relation.load !== \"function\") {\n // We don't want to throw on poly hints, because they're not actually loaded on the entity\n if (isPoly) continue;\n throw new Error(`Invalid load hint '${key}' on ${entity}`);\n }\n\n // If we're populating a hasReactiveField, don't bother loading it\n // if it's already been calculated (i.e. we have no reason to believe its value\n // is stale, so we should avoid pulling all of its data into memory).\n //\n // (Note that we can't do this same optimization for ReactiveReferenceImpl, because\n // as a FK, it will always need at least some `.load()` to fetch its entity from the database.\n // So we go ahead and call `.load()`, assuming it will just load its cached value, but it\n // will also check internally if it's marked for recalc, and load its load hint if necessary.\n if (relation instanceof ReactiveFieldImpl && relation.isSet) continue;\n if (relation.isLoaded && !opts.forceReload) continue;\n if (relation.isPreloaded) {\n relation.preload();\n continue;\n }\n\n // For non-derived SQL relations on existing entities, use batch loaders directly\n // (1 shared promise per batch instead of per-entity async overhead).\n // Skip new entities (no id) and derived relations (reactive m2m/m2o have extra logic in load).\n if (!entity.isNewEntity && field) {\n if (field.kind === \"o2m\") {\n batchPromise = (loader ??= oneToManyBatchLoader(em, relation)).load(entity.idTagged!);\n relationsToPreload.push(relation);\n continue;\n } else if (field.kind === \"m2m\" && !field.derived) {\n batchPromise = (loader ??= manyToManyBatchLoader(em, relation)).load(\n `${relation.columnName}=${entity.id}`,\n );\n relationsToPreload.push(relation);\n continue;\n } else if (field.kind === \"o2o\") {\n batchPromise = (loader ??= oneToOneBatchLoader(em, relation)).load(entity.idTagged!);\n relationsToPreload.push(relation);\n continue;\n } else if (field.kind === \"m2o\" && !field.derived) {\n const taggedId = relation.idTaggedMaybe;\n if (taggedId) {\n batchPromise = (loader ??= loadBatchLoader(em, field.otherMetadata())).load({\n taggedId,\n hint: undefined,\n });\n relationsToPreload.push(relation);\n continue;\n }\n }\n }\n fallbackPromises.push(relation.load(opts) as Promise<any>);\n }\n if (batchPromise) batchPromises.add(batchPromise);\n }\n\n if (batchPromises.size > 0 || fallbackPromises.length > 0) {\n await Promise.all([Promise.all(batchPromises), Promise.all(fallbackPromises)]);\n }\n for (const relation of relationsToPreload) {\n relation.preload();\n }\n\n // 2nd breadth-width pass to do nested load hints, this will fan out at the sibling level.\n // i.e. populateLayer(...reviews...) & populateLayer(...comments...)\n let nestedLoadPromises: (Promise<void> | undefined)[] | undefined;\n for (const [key, tree] of Object.entries(layerNode.hints)) {\n if (Object.keys(tree.hints).length === 0) continue;\n\n // Get the children we found, i.e. [a1, a2, a3] -> all of their books\n const childrenByParent = new Map<Entity, Entity[]>();\n for (const entity of tree.entities) {\n const relation = getRelationFromMaybePolyKey(entity, key);\n childrenByParent.set(entity, relation ? toArray(getEvenDeleted(relation)) : []);\n }\n if (childrenByParent.size === 0) continue;\n\n // Rewrite our node.entities to be the next layer of children, i.e. children will be all books, for all of\n // `[a1, a2, a3]`, but only the books of `a2` need to recurse into `book: reviews` and only the books of\n // `a3` need to recurse into `book: comments`, so swap `node.entities` (which is currently authors)\n // with the books. This is what prevents our dataloader-merged TreeHint from over-fetching and loading\n // the superset load hint for all entities.\n function rewrite(node: HintNode<Entity>) {\n const next = new Set<Entity>();\n for (const entity of node.entities) {\n const children = childrenByParent.get(entity);\n if (children) for (const child of children) next.add(child);\n }\n node.entities = next;\n Object.values(node.hints).forEach((node) => rewrite(node));\n }\n\n rewrite(tree);\n\n const nextMeta = (layerMeta?.allFields[key] as any)?.otherMetadata?.();\n (nestedLoadPromises ??= []).push(populateLayer(nextMeta, tree));\n }\n if (nestedLoadPromises) await Promise.all(nestedLoadPromises);\n }\n\n await populateLayer(meta, buildHintTree(populates));\n });\n}\n\n/** Probes `relation` to see if it's a m2o/o2m/m2m relation that supports `getWithDeleted`, otherwise calls `get`. */\nfunction getEvenDeleted(relation: any): any {\n return \"getWithDeleted\" in relation ? relation.getWithDeleted : relation.get;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"populateBatchLoader.cjs","names":["hintKey","getEmInternalApi","AliasAssigner","keyToNumber","kqDot","indexBy","isPolyHint","getRelationFromMaybePolyKey","ReactiveFieldImpl","oneToManyBatchLoader","manyToManyBatchLoader","oneToOneBatchLoader","loadBatchLoader","toArray","buildHintTree"],"sources":["../../src/batchloaders/populateBatchLoader.ts"],"sourcesContent":["import { type Entity } from \"../Entity.ts\";\nimport { type EntityMetadata } from \"../EntityMetadata.ts\";\nimport {\n AliasAssigner,\n type EntityManager,\n type ParsedFindQuery,\n addTablePerClassJoinsAndClassTag,\n getEmInternalApi,\n indexBy,\n keyToNumber,\n kqDot,\n} from \"../index.ts\";\nimport { type HintNode, buildHintTree } from \"../loading/HintTree.ts\";\nimport type { LoadHint } from \"../loading/loadHints.ts\";\nimport { hintKey } from \"../normalizeHints.ts\";\nimport { getRelationFromMaybePolyKey, isPolyHint } from \"../reactivity/reactiveHints.ts\";\nimport { ReactiveFieldImpl } from \"../relations/ReactiveField.ts\";\nimport { toArray } from \"../utils.ts\";\nimport { type BatchLoader } from \"./BatchLoader.ts\";\nimport { loadBatchLoader } from \"./loadBatchLoader.ts\";\nimport { manyToManyBatchLoader } from \"./manyToManyBatchLoader.ts\";\nimport { oneToManyBatchLoader } from \"./oneToManyBatchLoader.ts\";\nimport { oneToOneBatchLoader } from \"./oneToOneBatchLoader.ts\";\n\nexport const populateOperation = \"populate\";\n\nexport function populateBatchLoader(\n em: EntityManager,\n meta: EntityMetadata,\n hint: LoadHint<any>,\n mode: \"preload\" | \"intermixed\",\n opts: { forceReload?: boolean } = {},\n): BatchLoader<{ entity: Entity; hint: LoadHint<any> }> {\n // For batching populates, we want different levels of course-ness:\n // - preloading populates that are only SQL should be batched together as much as possible, but\n // - intermixed populates (some with custom relations) should be batched as separately as possible\n // (while still batching identical populates together) to reduce the chance of promise deadlocks.\n //\n // I.e. if we know this is a sql-only hint, we can batch all loads for a given entity\n // together do leverage join-based preloading.\n //\n // However, intermixed batches can be prone to promise deadlocking (one relation .load getting\n // stuck in a batch that is asking for its own dependencies to load), so if this is an intermixed\n // hint, then use a batch key that includes the hint itself, which will make it unlikely\n // for non-sql relations to deadlock on each other/themselves.\n const batchKey =\n mode === \"preload\" ? `${meta.tagName}:${opts.forceReload}` : `${meta.tagName}:${hintKey(hint)}:${opts.forceReload}`;\n return em.getBatchLoader(populateOperation, batchKey, async (populates) => {\n async function populateLayer(layerMeta: EntityMetadata | undefined, layerNode: HintNode<Entity>): Promise<void> {\n // Skip join-based preloading if nothing in this layer needs loading. If any entity in the list\n // needs loading, just load everything\n const { preloader } = getEmInternalApi(em);\n // We may not have a layerMeta if we're going through non-field properties\n if (preloader && layerMeta) {\n const preloadThisLayer = Object.entries(layerNode.hints).some(([key, hint]) => {\n for (const entity of hint.entities) {\n if ((entity as any)[key]?.isLoaded === false && !(entity as any)[key]?.isPreloaded) return true;\n }\n return false;\n });\n if (preloadThisLayer) {\n // Do an up-front SQL call of `select id, ...preloads... from table`,\n const assigner = new AliasAssigner();\n const meta = layerMeta;\n const alias = assigner.getAlias(meta.tableName);\n const entities = [...layerNode.entities].filter((e) => !e.isNewEntity);\n const ids = entities.map((e) => keyToNumber(meta, e.id));\n // Create a ParsedFindQuery for `addPreloading` to inject joins into\n const query: ParsedFindQuery = {\n // We already have the entities loaded, so can do just `SELECT a.id` + the preload columns\n selects: [kqDot(alias, \"id\")],\n tables: [{ alias, join: \"primary\", table: meta.tableName }],\n condition: {\n kind: \"exp\",\n op: \"and\",\n conditions: [\n { kind: \"column\", alias, column: \"id\", dbType: meta.idDbType, cond: { kind: \"in\", value: ids } },\n ],\n },\n orderBys: [],\n };\n // If we're selecting from small_publishers, join in our base class in case\n // the preloader wants to join on a column like `publishers.group_id`\n addTablePerClassJoinsAndClassTag(query, meta, alias, false);\n const hydrator = preloader.addPreloading(meta, layerNode, query);\n if (hydrator) {\n const rows = await em[\"executeFind\"](meta, populateOperation, query, {});\n const entitiesById = indexBy(entities, (e) => keyToNumber(meta, e.id));\n const entitiesInOrder = rows.map((row) => entitiesById.get(row[\"id\"]));\n hydrator(rows, entitiesInOrder);\n }\n }\n }\n\n // First pass: batch SQL relations directly, fall back to relation.load() for non-SQL\n const batchPromises = new Set<Promise<void>>();\n const relationsToPreload: { preload(): void }[] = [];\n const fallbackPromises: (Promise<any> | undefined)[] = [];\n\n for (const [key, tree] of Object.entries(layerNode.hints)) {\n const field = layerMeta?.allFields[key];\n // Hoist the poly-key parsing out of the per-entity loop; most keys are not poly\n const isPoly = isPolyHint(key);\n let loader: BatchLoader<any> | undefined;\n let batchPromise: Promise<void> | undefined;\n for (const entity of tree.entities) {\n const relation = isPoly ? getRelationFromMaybePolyKey(entity, key) : (entity as any)[key];\n // This happens to let through non-relation hints like 'name' on user, which wasn't intentional,\n // but currently doesn't blow up (somehow), and is depended on by internal tests.\n if (!relation || typeof relation.load !== \"function\") {\n // We don't want to throw on poly hints, because they're not actually loaded on the entity\n if (isPoly) continue;\n throw new Error(`Invalid load hint '${key}' on ${entity}`);\n }\n\n // If we're populating a hasReactiveField, don't bother loading it\n // if it's already been calculated (i.e. we have no reason to believe its value\n // is stale, so we should avoid pulling all of its data into memory).\n //\n // (Note that we can't do this same optimization for ReactiveReferenceImpl, because\n // as a FK, it will always need at least some `.load()` to fetch its entity from the database.\n // So we go ahead and call `.load()`, assuming it will just load its cached value, but it\n // will also check internally if it's marked for recalc, and load its load hint if necessary.\n if (relation instanceof ReactiveFieldImpl && relation.isSet) continue;\n if (relation.isLoaded && !opts.forceReload) continue;\n if (relation.isPreloaded) {\n relation.preload();\n continue;\n }\n\n // For non-derived SQL relations on existing entities, use batch loaders directly\n // (1 shared promise per batch instead of per-entity async overhead).\n // Skip new entities (no id) and derived relations (reactive m2m/m2o have extra logic in load).\n if (!entity.isNewEntity && field) {\n if (field.kind === \"o2m\") {\n batchPromise = (loader ??= oneToManyBatchLoader(em, relation)).load(entity.idTagged!);\n relationsToPreload.push(relation);\n continue;\n } else if (field.kind === \"m2m\" && !field.derived) {\n batchPromise = (loader ??= manyToManyBatchLoader(em, relation)).load(\n `${relation.columnName}=${entity.id}`,\n );\n relationsToPreload.push(relation);\n continue;\n } else if (field.kind === \"o2o\") {\n batchPromise = (loader ??= oneToOneBatchLoader(em, relation)).load(entity.idTagged!);\n relationsToPreload.push(relation);\n continue;\n } else if (field.kind === \"m2o\" && !field.derived) {\n const taggedId = relation.idTaggedMaybe;\n if (taggedId) {\n batchPromise = (loader ??= loadBatchLoader(em, field.otherMetadata())).load({\n taggedId,\n hint: undefined,\n });\n relationsToPreload.push(relation);\n continue;\n }\n }\n }\n fallbackPromises.push(relation.load(opts) as Promise<any>);\n }\n if (batchPromise) batchPromises.add(batchPromise);\n }\n\n if (batchPromises.size > 0 || fallbackPromises.length > 0) {\n await Promise.all([Promise.all(batchPromises), Promise.all(fallbackPromises)]);\n }\n for (const relation of relationsToPreload) {\n relation.preload();\n }\n\n // 2nd breadth-width pass to do nested load hints, this will fan out at the sibling level.\n // i.e. populateLayer(...reviews...) & populateLayer(...comments...)\n let nestedLoadPromises: (Promise<void> | undefined)[] | undefined;\n for (const [key, tree] of Object.entries(layerNode.hints)) {\n if (Object.keys(tree.hints).length === 0) continue;\n\n // Get the children we found, i.e. [a1, a2, a3] -> all of their books\n const childrenByParent = new Map<Entity, Entity[]>();\n for (const entity of tree.entities) {\n const relation = getRelationFromMaybePolyKey(entity, key);\n childrenByParent.set(entity, relation ? toArray(getEvenDeleted(relation)) : []);\n }\n if (childrenByParent.size === 0) continue;\n\n // Rewrite our node.entities to be the next layer of children, i.e. children will be all books, for all of\n // `[a1, a2, a3]`, but only the books of `a2` need to recurse into `book: reviews` and only the books of\n // `a3` need to recurse into `book: comments`, so swap `node.entities` (which is currently authors)\n // with the books. This is what prevents our dataloader-merged TreeHint from over-fetching and loading\n // the superset load hint for all entities.\n function rewrite(node: HintNode<Entity>) {\n const next = new Set<Entity>();\n for (const entity of node.entities) {\n const children = childrenByParent.get(entity);\n if (children) for (const child of children) next.add(child);\n }\n node.entities = next;\n Object.values(node.hints).forEach((node) => rewrite(node));\n }\n\n rewrite(tree);\n\n const nextMeta = (layerMeta?.allFields[key] as any)?.otherMetadata?.();\n (nestedLoadPromises ??= []).push(populateLayer(nextMeta, tree));\n }\n if (nestedLoadPromises) await Promise.all(nestedLoadPromises);\n }\n\n await populateLayer(meta, buildHintTree(populates));\n });\n}\n\n/** Probes `relation` to see if it's a m2o/o2m/m2m relation that supports `getWithDeleted`, otherwise calls `get`. */\nfunction getEvenDeleted(relation: any): any {\n return \"getWithDeleted\" in relation ? relation.getWithDeleted : relation.get;\n}\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BatchLoader } from "./BatchLoader.cjs";
|
|
2
|
-
import { LoadHint } from "../loadHints.cjs";
|
|
2
|
+
import { LoadHint } from "../loading/loadHints.cjs";
|
|
3
3
|
import { EntityMetadata } from "../EntityMetadata.cjs";
|
|
4
4
|
import "../index.cjs";
|
|
5
5
|
import { EntityManager } from "../EntityManager.cjs";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BatchLoader } from "./BatchLoader.mjs";
|
|
2
|
-
import { LoadHint } from "../loadHints.mjs";
|
|
2
|
+
import { LoadHint } from "../loading/loadHints.mjs";
|
|
3
3
|
import { EntityMetadata } from "../EntityMetadata.mjs";
|
|
4
4
|
import "../index.mjs";
|
|
5
5
|
import { EntityManager } from "../EntityManager.mjs";
|
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
import "./BatchLoader.js";
|
|
2
|
+
import { hintKey } from "../normalizeHints.js";
|
|
3
|
+
import { getRelationFromMaybePolyKey, isPolyHint } from "../reactivity/reactiveHints.js";
|
|
4
|
+
import { keyToNumber } from "../keys.js";
|
|
2
5
|
import { kqDot } from "../queries/sql/keywords.js";
|
|
3
|
-
import {
|
|
6
|
+
import { manyToManyBatchLoader } from "./manyToManyBatchLoader.js";
|
|
4
7
|
import { oneToManyBatchLoader } from "./oneToManyBatchLoader.js";
|
|
5
8
|
import { addTablePerClassJoinsAndClassTag } from "../queries/find/QueryParser.js";
|
|
6
|
-
import {
|
|
7
|
-
import { hintKey } from "../normalizeHints.js";
|
|
8
|
-
import "../loadHints.js";
|
|
9
|
-
import { indexBy, toArray } from "../utils.js";
|
|
10
|
-
import { manyToManyBatchLoader } from "./manyToManyBatchLoader.js";
|
|
9
|
+
import { oneToOneBatchLoader } from "./oneToOneBatchLoader.js";
|
|
11
10
|
import { ReactiveFieldImpl } from "../relations/ReactiveField.js";
|
|
12
|
-
import {
|
|
11
|
+
import { indexBy, toArray } from "../utils.js";
|
|
13
12
|
import "../EntityMetadata.js";
|
|
14
13
|
import { AliasAssigner } from "../queries/sql/AliasAssigner.js";
|
|
15
|
-
import { buildHintTree } from "../HintTree.js";
|
|
14
|
+
import { buildHintTree } from "../loading/HintTree.js";
|
|
16
15
|
import "../index.js";
|
|
17
16
|
import { loadBatchLoader } from "./loadBatchLoader.js";
|
|
18
17
|
import { getEmInternalApi } from "../EntityManager.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"populateBatchLoader.js","names":[],"sources":["../../src/batchloaders/populateBatchLoader.ts"],"sourcesContent":["import { type Entity } from \"../Entity.ts\";\nimport { type EntityMetadata } from \"../EntityMetadata.ts\";\nimport { type HintNode, buildHintTree } from \"../HintTree.ts\";\nimport {\n AliasAssigner,\n type EntityManager,\n type ParsedFindQuery,\n addTablePerClassJoinsAndClassTag,\n getEmInternalApi,\n indexBy,\n keyToNumber,\n kqDot,\n} from \"../index.ts\";\nimport { type LoadHint } from \"../loadHints.ts\";\nimport { hintKey } from \"../normalizeHints.ts\";\nimport { getRelationFromMaybePolyKey, isPolyHint } from \"../reactiveHints.ts\";\nimport { ReactiveFieldImpl } from \"../relations/ReactiveField.ts\";\nimport { toArray } from \"../utils.ts\";\nimport { type BatchLoader } from \"./BatchLoader.ts\";\nimport { loadBatchLoader } from \"./loadBatchLoader.ts\";\nimport { manyToManyBatchLoader } from \"./manyToManyBatchLoader.ts\";\nimport { oneToManyBatchLoader } from \"./oneToManyBatchLoader.ts\";\nimport { oneToOneBatchLoader } from \"./oneToOneBatchLoader.ts\";\n\nexport const populateOperation = \"populate\";\n\nexport function populateBatchLoader(\n em: EntityManager,\n meta: EntityMetadata,\n hint: LoadHint<any>,\n mode: \"preload\" | \"intermixed\",\n opts: { forceReload?: boolean } = {},\n): BatchLoader<{ entity: Entity; hint: LoadHint<any> }> {\n // For batching populates, we want different levels of course-ness:\n // - preloading populates that are only SQL should be batched together as much as possible, but\n // - intermixed populates (some with custom relations) should be batched as separately as possible\n // (while still batching identical populates together) to reduce the chance of promise deadlocks.\n //\n // I.e. if we know this is a sql-only hint, we can batch all loads for a given entity\n // together do leverage join-based preloading.\n //\n // However, intermixed batches can be prone to promise deadlocking (one relation .load getting\n // stuck in a batch that is asking for its own dependencies to load), so if this is an intermixed\n // hint, then use a batch key that includes the hint itself, which will make it unlikely\n // for non-sql relations to deadlock on each other/themselves.\n const batchKey =\n mode === \"preload\" ? `${meta.tagName}:${opts.forceReload}` : `${meta.tagName}:${hintKey(hint)}:${opts.forceReload}`;\n return em.getBatchLoader(populateOperation, batchKey, async (populates) => {\n async function populateLayer(layerMeta: EntityMetadata | undefined, layerNode: HintNode<Entity>): Promise<void> {\n // Skip join-based preloading if nothing in this layer needs loading. If any entity in the list\n // needs loading, just load everything\n const { preloader } = getEmInternalApi(em);\n // We may not have a layerMeta if we're going through non-field properties\n if (preloader && layerMeta) {\n const preloadThisLayer = Object.entries(layerNode.hints).some(([key, hint]) => {\n for (const entity of hint.entities) {\n if ((entity as any)[key]?.isLoaded === false && !(entity as any)[key]?.isPreloaded) return true;\n }\n return false;\n });\n if (preloadThisLayer) {\n // Do an up-front SQL call of `select id, ...preloads... from table`,\n const assigner = new AliasAssigner();\n const meta = layerMeta;\n const alias = assigner.getAlias(meta.tableName);\n const entities = [...layerNode.entities].filter((e) => !e.isNewEntity);\n const ids = entities.map((e) => keyToNumber(meta, e.id));\n // Create a ParsedFindQuery for `addPreloading` to inject joins into\n const query: ParsedFindQuery = {\n // We already have the entities loaded, so can do just `SELECT a.id` + the preload columns\n selects: [kqDot(alias, \"id\")],\n tables: [{ alias, join: \"primary\", table: meta.tableName }],\n condition: {\n kind: \"exp\",\n op: \"and\",\n conditions: [\n { kind: \"column\", alias, column: \"id\", dbType: meta.idDbType, cond: { kind: \"in\", value: ids } },\n ],\n },\n orderBys: [],\n };\n // If we're selecting from small_publishers, join in our base class in case\n // the preloader wants to join on a column like `publishers.group_id`\n addTablePerClassJoinsAndClassTag(query, meta, alias, false);\n const hydrator = preloader.addPreloading(meta, layerNode, query);\n if (hydrator) {\n const rows = await em[\"executeFind\"](meta, populateOperation, query, {});\n const entitiesById = indexBy(entities, (e) => keyToNumber(meta, e.id));\n const entitiesInOrder = rows.map((row) => entitiesById.get(row[\"id\"]));\n hydrator(rows, entitiesInOrder);\n }\n }\n }\n\n // First pass: batch SQL relations directly, fall back to relation.load() for non-SQL\n const batchPromises = new Set<Promise<void>>();\n const relationsToPreload: { preload(): void }[] = [];\n const fallbackPromises: (Promise<any> | undefined)[] = [];\n\n for (const [key, tree] of Object.entries(layerNode.hints)) {\n const field = layerMeta?.allFields[key];\n // Hoist the poly-key parsing out of the per-entity loop; most keys are not poly\n const isPoly = isPolyHint(key);\n let loader: BatchLoader<any> | undefined;\n let batchPromise: Promise<void> | undefined;\n for (const entity of tree.entities) {\n const relation = isPoly ? getRelationFromMaybePolyKey(entity, key) : (entity as any)[key];\n // This happens to let through non-relation hints like 'name' on user, which wasn't intentional,\n // but currently doesn't blow up (somehow), and is depended on by internal tests.\n if (!relation || typeof relation.load !== \"function\") {\n // We don't want to throw on poly hints, because they're not actually loaded on the entity\n if (isPoly) continue;\n throw new Error(`Invalid load hint '${key}' on ${entity}`);\n }\n\n // If we're populating a hasReactiveField, don't bother loading it\n // if it's already been calculated (i.e. we have no reason to believe its value\n // is stale, so we should avoid pulling all of its data into memory).\n //\n // (Note that we can't do this same optimization for ReactiveReferenceImpl, because\n // as a FK, it will always need at least some `.load()` to fetch its entity from the database.\n // So we go ahead and call `.load()`, assuming it will just load its cached value, but it\n // will also check internally if it's marked for recalc, and load its load hint if necessary.\n if (relation instanceof ReactiveFieldImpl && relation.isSet) continue;\n if (relation.isLoaded && !opts.forceReload) continue;\n if (relation.isPreloaded) {\n relation.preload();\n continue;\n }\n\n // For non-derived SQL relations on existing entities, use batch loaders directly\n // (1 shared promise per batch instead of per-entity async overhead).\n // Skip new entities (no id) and derived relations (reactive m2m/m2o have extra logic in load).\n if (!entity.isNewEntity && field) {\n if (field.kind === \"o2m\") {\n batchPromise = (loader ??= oneToManyBatchLoader(em, relation)).load(entity.idTagged!);\n relationsToPreload.push(relation);\n continue;\n } else if (field.kind === \"m2m\" && !field.derived) {\n batchPromise = (loader ??= manyToManyBatchLoader(em, relation)).load(\n `${relation.columnName}=${entity.id}`,\n );\n relationsToPreload.push(relation);\n continue;\n } else if (field.kind === \"o2o\") {\n batchPromise = (loader ??= oneToOneBatchLoader(em, relation)).load(entity.idTagged!);\n relationsToPreload.push(relation);\n continue;\n } else if (field.kind === \"m2o\" && !field.derived) {\n const taggedId = relation.idTaggedMaybe;\n if (taggedId) {\n batchPromise = (loader ??= loadBatchLoader(em, field.otherMetadata())).load({\n taggedId,\n hint: undefined,\n });\n relationsToPreload.push(relation);\n continue;\n }\n }\n }\n fallbackPromises.push(relation.load(opts) as Promise<any>);\n }\n if (batchPromise) batchPromises.add(batchPromise);\n }\n\n if (batchPromises.size > 0 || fallbackPromises.length > 0) {\n await Promise.all([Promise.all(batchPromises), Promise.all(fallbackPromises)]);\n }\n for (const relation of relationsToPreload) {\n relation.preload();\n }\n\n // 2nd breadth-width pass to do nested load hints, this will fan out at the sibling level.\n // i.e. populateLayer(...reviews...) & populateLayer(...comments...)\n let nestedLoadPromises: (Promise<void> | undefined)[] | undefined;\n for (const [key, tree] of Object.entries(layerNode.hints)) {\n if (Object.keys(tree.hints).length === 0) continue;\n\n // Get the children we found, i.e. [a1, a2, a3] -> all of their books\n const childrenByParent = new Map<Entity, Entity[]>();\n for (const entity of tree.entities) {\n const relation = getRelationFromMaybePolyKey(entity, key);\n childrenByParent.set(entity, relation ? toArray(getEvenDeleted(relation)) : []);\n }\n if (childrenByParent.size === 0) continue;\n\n // Rewrite our node.entities to be the next layer of children, i.e. children will be all books, for all of\n // `[a1, a2, a3]`, but only the books of `a2` need to recurse into `book: reviews` and only the books of\n // `a3` need to recurse into `book: comments`, so swap `node.entities` (which is currently authors)\n // with the books. This is what prevents our dataloader-merged TreeHint from over-fetching and loading\n // the superset load hint for all entities.\n function rewrite(node: HintNode<Entity>) {\n const next = new Set<Entity>();\n for (const entity of node.entities) {\n const children = childrenByParent.get(entity);\n if (children) for (const child of children) next.add(child);\n }\n node.entities = next;\n Object.values(node.hints).forEach((node) => rewrite(node));\n }\n\n rewrite(tree);\n\n const nextMeta = (layerMeta?.allFields[key] as any)?.otherMetadata?.();\n (nestedLoadPromises ??= []).push(populateLayer(nextMeta, tree));\n }\n if (nestedLoadPromises) await Promise.all(nestedLoadPromises);\n }\n\n await populateLayer(meta, buildHintTree(populates));\n });\n}\n\n/** Probes `relation` to see if it's a m2o/o2m/m2m relation that supports `getWithDeleted`, otherwise calls `get`. */\nfunction getEvenDeleted(relation: any): any {\n return \"getWithDeleted\" in relation ? relation.getWithDeleted : relation.get;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"populateBatchLoader.js","names":[],"sources":["../../src/batchloaders/populateBatchLoader.ts"],"sourcesContent":["import { type Entity } from \"../Entity.ts\";\nimport { type EntityMetadata } from \"../EntityMetadata.ts\";\nimport {\n AliasAssigner,\n type EntityManager,\n type ParsedFindQuery,\n addTablePerClassJoinsAndClassTag,\n getEmInternalApi,\n indexBy,\n keyToNumber,\n kqDot,\n} from \"../index.ts\";\nimport { type HintNode, buildHintTree } from \"../loading/HintTree.ts\";\nimport type { LoadHint } from \"../loading/loadHints.ts\";\nimport { hintKey } from \"../normalizeHints.ts\";\nimport { getRelationFromMaybePolyKey, isPolyHint } from \"../reactivity/reactiveHints.ts\";\nimport { ReactiveFieldImpl } from \"../relations/ReactiveField.ts\";\nimport { toArray } from \"../utils.ts\";\nimport { type BatchLoader } from \"./BatchLoader.ts\";\nimport { loadBatchLoader } from \"./loadBatchLoader.ts\";\nimport { manyToManyBatchLoader } from \"./manyToManyBatchLoader.ts\";\nimport { oneToManyBatchLoader } from \"./oneToManyBatchLoader.ts\";\nimport { oneToOneBatchLoader } from \"./oneToOneBatchLoader.ts\";\n\nexport const populateOperation = \"populate\";\n\nexport function populateBatchLoader(\n em: EntityManager,\n meta: EntityMetadata,\n hint: LoadHint<any>,\n mode: \"preload\" | \"intermixed\",\n opts: { forceReload?: boolean } = {},\n): BatchLoader<{ entity: Entity; hint: LoadHint<any> }> {\n // For batching populates, we want different levels of course-ness:\n // - preloading populates that are only SQL should be batched together as much as possible, but\n // - intermixed populates (some with custom relations) should be batched as separately as possible\n // (while still batching identical populates together) to reduce the chance of promise deadlocks.\n //\n // I.e. if we know this is a sql-only hint, we can batch all loads for a given entity\n // together do leverage join-based preloading.\n //\n // However, intermixed batches can be prone to promise deadlocking (one relation .load getting\n // stuck in a batch that is asking for its own dependencies to load), so if this is an intermixed\n // hint, then use a batch key that includes the hint itself, which will make it unlikely\n // for non-sql relations to deadlock on each other/themselves.\n const batchKey =\n mode === \"preload\" ? `${meta.tagName}:${opts.forceReload}` : `${meta.tagName}:${hintKey(hint)}:${opts.forceReload}`;\n return em.getBatchLoader(populateOperation, batchKey, async (populates) => {\n async function populateLayer(layerMeta: EntityMetadata | undefined, layerNode: HintNode<Entity>): Promise<void> {\n // Skip join-based preloading if nothing in this layer needs loading. If any entity in the list\n // needs loading, just load everything\n const { preloader } = getEmInternalApi(em);\n // We may not have a layerMeta if we're going through non-field properties\n if (preloader && layerMeta) {\n const preloadThisLayer = Object.entries(layerNode.hints).some(([key, hint]) => {\n for (const entity of hint.entities) {\n if ((entity as any)[key]?.isLoaded === false && !(entity as any)[key]?.isPreloaded) return true;\n }\n return false;\n });\n if (preloadThisLayer) {\n // Do an up-front SQL call of `select id, ...preloads... from table`,\n const assigner = new AliasAssigner();\n const meta = layerMeta;\n const alias = assigner.getAlias(meta.tableName);\n const entities = [...layerNode.entities].filter((e) => !e.isNewEntity);\n const ids = entities.map((e) => keyToNumber(meta, e.id));\n // Create a ParsedFindQuery for `addPreloading` to inject joins into\n const query: ParsedFindQuery = {\n // We already have the entities loaded, so can do just `SELECT a.id` + the preload columns\n selects: [kqDot(alias, \"id\")],\n tables: [{ alias, join: \"primary\", table: meta.tableName }],\n condition: {\n kind: \"exp\",\n op: \"and\",\n conditions: [\n { kind: \"column\", alias, column: \"id\", dbType: meta.idDbType, cond: { kind: \"in\", value: ids } },\n ],\n },\n orderBys: [],\n };\n // If we're selecting from small_publishers, join in our base class in case\n // the preloader wants to join on a column like `publishers.group_id`\n addTablePerClassJoinsAndClassTag(query, meta, alias, false);\n const hydrator = preloader.addPreloading(meta, layerNode, query);\n if (hydrator) {\n const rows = await em[\"executeFind\"](meta, populateOperation, query, {});\n const entitiesById = indexBy(entities, (e) => keyToNumber(meta, e.id));\n const entitiesInOrder = rows.map((row) => entitiesById.get(row[\"id\"]));\n hydrator(rows, entitiesInOrder);\n }\n }\n }\n\n // First pass: batch SQL relations directly, fall back to relation.load() for non-SQL\n const batchPromises = new Set<Promise<void>>();\n const relationsToPreload: { preload(): void }[] = [];\n const fallbackPromises: (Promise<any> | undefined)[] = [];\n\n for (const [key, tree] of Object.entries(layerNode.hints)) {\n const field = layerMeta?.allFields[key];\n // Hoist the poly-key parsing out of the per-entity loop; most keys are not poly\n const isPoly = isPolyHint(key);\n let loader: BatchLoader<any> | undefined;\n let batchPromise: Promise<void> | undefined;\n for (const entity of tree.entities) {\n const relation = isPoly ? getRelationFromMaybePolyKey(entity, key) : (entity as any)[key];\n // This happens to let through non-relation hints like 'name' on user, which wasn't intentional,\n // but currently doesn't blow up (somehow), and is depended on by internal tests.\n if (!relation || typeof relation.load !== \"function\") {\n // We don't want to throw on poly hints, because they're not actually loaded on the entity\n if (isPoly) continue;\n throw new Error(`Invalid load hint '${key}' on ${entity}`);\n }\n\n // If we're populating a hasReactiveField, don't bother loading it\n // if it's already been calculated (i.e. we have no reason to believe its value\n // is stale, so we should avoid pulling all of its data into memory).\n //\n // (Note that we can't do this same optimization for ReactiveReferenceImpl, because\n // as a FK, it will always need at least some `.load()` to fetch its entity from the database.\n // So we go ahead and call `.load()`, assuming it will just load its cached value, but it\n // will also check internally if it's marked for recalc, and load its load hint if necessary.\n if (relation instanceof ReactiveFieldImpl && relation.isSet) continue;\n if (relation.isLoaded && !opts.forceReload) continue;\n if (relation.isPreloaded) {\n relation.preload();\n continue;\n }\n\n // For non-derived SQL relations on existing entities, use batch loaders directly\n // (1 shared promise per batch instead of per-entity async overhead).\n // Skip new entities (no id) and derived relations (reactive m2m/m2o have extra logic in load).\n if (!entity.isNewEntity && field) {\n if (field.kind === \"o2m\") {\n batchPromise = (loader ??= oneToManyBatchLoader(em, relation)).load(entity.idTagged!);\n relationsToPreload.push(relation);\n continue;\n } else if (field.kind === \"m2m\" && !field.derived) {\n batchPromise = (loader ??= manyToManyBatchLoader(em, relation)).load(\n `${relation.columnName}=${entity.id}`,\n );\n relationsToPreload.push(relation);\n continue;\n } else if (field.kind === \"o2o\") {\n batchPromise = (loader ??= oneToOneBatchLoader(em, relation)).load(entity.idTagged!);\n relationsToPreload.push(relation);\n continue;\n } else if (field.kind === \"m2o\" && !field.derived) {\n const taggedId = relation.idTaggedMaybe;\n if (taggedId) {\n batchPromise = (loader ??= loadBatchLoader(em, field.otherMetadata())).load({\n taggedId,\n hint: undefined,\n });\n relationsToPreload.push(relation);\n continue;\n }\n }\n }\n fallbackPromises.push(relation.load(opts) as Promise<any>);\n }\n if (batchPromise) batchPromises.add(batchPromise);\n }\n\n if (batchPromises.size > 0 || fallbackPromises.length > 0) {\n await Promise.all([Promise.all(batchPromises), Promise.all(fallbackPromises)]);\n }\n for (const relation of relationsToPreload) {\n relation.preload();\n }\n\n // 2nd breadth-width pass to do nested load hints, this will fan out at the sibling level.\n // i.e. populateLayer(...reviews...) & populateLayer(...comments...)\n let nestedLoadPromises: (Promise<void> | undefined)[] | undefined;\n for (const [key, tree] of Object.entries(layerNode.hints)) {\n if (Object.keys(tree.hints).length === 0) continue;\n\n // Get the children we found, i.e. [a1, a2, a3] -> all of their books\n const childrenByParent = new Map<Entity, Entity[]>();\n for (const entity of tree.entities) {\n const relation = getRelationFromMaybePolyKey(entity, key);\n childrenByParent.set(entity, relation ? toArray(getEvenDeleted(relation)) : []);\n }\n if (childrenByParent.size === 0) continue;\n\n // Rewrite our node.entities to be the next layer of children, i.e. children will be all books, for all of\n // `[a1, a2, a3]`, but only the books of `a2` need to recurse into `book: reviews` and only the books of\n // `a3` need to recurse into `book: comments`, so swap `node.entities` (which is currently authors)\n // with the books. This is what prevents our dataloader-merged TreeHint from over-fetching and loading\n // the superset load hint for all entities.\n function rewrite(node: HintNode<Entity>) {\n const next = new Set<Entity>();\n for (const entity of node.entities) {\n const children = childrenByParent.get(entity);\n if (children) for (const child of children) next.add(child);\n }\n node.entities = next;\n Object.values(node.hints).forEach((node) => rewrite(node));\n }\n\n rewrite(tree);\n\n const nextMeta = (layerMeta?.allFields[key] as any)?.otherMetadata?.();\n (nestedLoadPromises ??= []).push(populateLayer(nextMeta, tree));\n }\n if (nestedLoadPromises) await Promise.all(nestedLoadPromises);\n }\n\n await populateLayer(meta, buildHintTree(populates));\n });\n}\n\n/** Probes `relation` to see if it's a m2o/o2m/m2m relation that supports `getWithDeleted`, otherwise calls `get`. */\nfunction getEvenDeleted(relation: any): any {\n return \"getWithDeleted\" in relation ? relation.getWithDeleted : relation.get;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAwBA,MAAa,oBAAoB;AAEjC,SAAgB,oBACd,IACA,MACA,MACA,MACA,OAAkC,CAAC,GACmB;CAatD,MAAM,WACJ,SAAS,YAAY,GAAG,KAAK,QAAQ,GAAG,KAAK,gBAAgB,GAAG,KAAK,QAAQ,GAAG,QAAQ,IAAI,EAAE,GAAG,KAAK;CACxG,OAAO,GAAG,eAAe,mBAAmB,UAAU,OAAO,cAAc;EACzE,eAAe,cAAc,WAAuC,WAA4C;GAG9G,MAAM,EAAE,cAAc,iBAAiB,EAAE;GAEzC,IAAI,aAAa,WACU;QAAA,OAAO,QAAQ,UAAU,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,UAAU;KAC7E,KAAK,MAAM,UAAU,KAAK,UACxB,IAAK,OAAe,IAAI,EAAE,aAAa,SAAS,CAAE,OAAe,IAAI,EAAE,aAAa,OAAO;KAE7F,OAAO;IACT,CACmB,GAAG;KAEpB,MAAM,WAAW,IAAI,cAAc;KACnC,MAAM,OAAO;KACb,MAAM,QAAQ,SAAS,SAAS,KAAK,SAAS;KAC9C,MAAM,WAAW,CAAC,GAAG,UAAU,QAAQ,CAAC,CAAC,QAAQ,MAAM,CAAC,EAAE,WAAW;KACrE,MAAM,MAAM,SAAS,KAAK,MAAM,YAAY,MAAM,EAAE,EAAE,CAAC;KAEvD,MAAM,QAAyB;MAE7B,SAAS,CAAC,MAAM,OAAO,IAAI,CAAC;MAC5B,QAAQ,CAAC;OAAE;OAAO,MAAM;OAAW,OAAO,KAAK;MAAU,CAAC;MAC1D,WAAW;OACT,MAAM;OACN,IAAI;OACJ,YAAY,CACV;QAAE,MAAM;QAAU;QAAO,QAAQ;QAAM,QAAQ,KAAK;QAAU,MAAM;SAAE,MAAM;SAAM,OAAO;QAAI;OAAE,CACjG;MACF;MACA,UAAU,CAAC;KACb;KAGA,iCAAiC,OAAO,MAAM,OAAO,KAAK;KAC1D,MAAM,WAAW,UAAU,cAAc,MAAM,WAAW,KAAK;KAC/D,IAAI,UAAU;MACZ,MAAM,OAAO,MAAM,GAAG,cAAc,CAAC,MAAM,mBAAmB,OAAO,CAAC,CAAC;MACvE,MAAM,eAAe,QAAQ,WAAW,MAAM,YAAY,MAAM,EAAE,EAAE,CAAC;MAErE,SAAS,MADe,KAAK,KAAK,QAAQ,aAAa,IAAI,IAAI,KAAK,CACvC,CAAC;KAChC;IACF;;GAIF,MAAM,gCAAgB,IAAI,IAAmB;GAC7C,MAAM,qBAA4C,CAAC;GACnD,MAAM,mBAAiD,CAAC;GAExD,KAAK,MAAM,CAAC,KAAK,SAAS,OAAO,QAAQ,UAAU,KAAK,GAAG;IACzD,MAAM,QAAQ,WAAW,UAAU;IAEnC,MAAM,SAAS,WAAW,GAAG;IAC7B,IAAI;IACJ,IAAI;IACJ,KAAK,MAAM,UAAU,KAAK,UAAU;KAClC,MAAM,WAAW,SAAS,4BAA4B,QAAQ,GAAG,IAAK,OAAe;KAGrF,IAAI,CAAC,YAAY,OAAO,SAAS,SAAS,YAAY;MAEpD,IAAI,QAAQ;MACZ,MAAM,IAAI,MAAM,sBAAsB,IAAI,OAAO,QAAQ;KAC3D;KAUA,IAAI,oBAAoB,qBAAqB,SAAS,OAAO;KAC7D,IAAI,SAAS,YAAY,CAAC,KAAK,aAAa;KAC5C,IAAI,SAAS,aAAa;MACxB,SAAS,QAAQ;MACjB;KACF;KAKA,IAAI,CAAC,OAAO,eAAe,OAAO;MAChC,IAAI,MAAM,SAAS,OAAO;OACxB,gBAAgB,WAAW,qBAAqB,IAAI,QAAQ,EAAA,CAAG,KAAK,OAAO,QAAS;OACpF,mBAAmB,KAAK,QAAQ;OAChC;MACF,OAAO,IAAI,MAAM,SAAS,SAAS,CAAC,MAAM,SAAS;OACjD,gBAAgB,WAAW,sBAAsB,IAAI,QAAQ,EAAA,CAAG,KAC9D,GAAG,SAAS,WAAW,GAAG,OAAO,IACnC;OACA,mBAAmB,KAAK,QAAQ;OAChC;MACF,OAAO,IAAI,MAAM,SAAS,OAAO;OAC/B,gBAAgB,WAAW,oBAAoB,IAAI,QAAQ,EAAA,CAAG,KAAK,OAAO,QAAS;OACnF,mBAAmB,KAAK,QAAQ;OAChC;MACF,OAAO,IAAI,MAAM,SAAS,SAAS,CAAC,MAAM,SAAS;OACjD,MAAM,WAAW,SAAS;OAC1B,IAAI,UAAU;QACZ,gBAAgB,WAAW,gBAAgB,IAAI,MAAM,cAAc,CAAC,EAAA,CAAG,KAAK;SAC1E;SACA,MAAM,KAAA;QACR,CAAC;QACD,mBAAmB,KAAK,QAAQ;QAChC;OACF;MACF;KACF;KACA,iBAAiB,KAAK,SAAS,KAAK,IAAI,CAAiB;IAC3D;IACA,IAAI,cAAc,cAAc,IAAI,YAAY;GAClD;GAEA,IAAI,cAAc,OAAO,KAAK,iBAAiB,SAAS,GACtD,MAAM,QAAQ,IAAI,CAAC,QAAQ,IAAI,aAAa,GAAG,QAAQ,IAAI,gBAAgB,CAAC,CAAC;GAE/E,KAAK,MAAM,YAAY,oBACrB,SAAS,QAAQ;GAKnB,IAAI;GACJ,KAAK,MAAM,CAAC,KAAK,SAAS,OAAO,QAAQ,UAAU,KAAK,GAAG;IACzD,IAAI,OAAO,KAAK,KAAK,KAAK,CAAC,CAAC,WAAW,GAAG;IAG1C,MAAM,mCAAmB,IAAI,IAAsB;IACnD,KAAK,MAAM,UAAU,KAAK,UAAU;KAClC,MAAM,WAAW,4BAA4B,QAAQ,GAAG;KACxD,iBAAiB,IAAI,QAAQ,WAAW,QAAQ,eAAe,QAAQ,CAAC,IAAI,CAAC,CAAC;IAChF;IACA,IAAI,iBAAiB,SAAS,GAAG;IAOjC,SAAS,QAAQ,MAAwB;KACvC,MAAM,uBAAO,IAAI,IAAY;KAC7B,KAAK,MAAM,UAAU,KAAK,UAAU;MAClC,MAAM,WAAW,iBAAiB,IAAI,MAAM;MAC5C,IAAI,UAAU,KAAK,MAAM,SAAS,UAAU,KAAK,IAAI,KAAK;KAC5D;KACA,KAAK,WAAW;KAChB,OAAO,OAAO,KAAK,KAAK,CAAC,CAAC,SAAS,SAAS,QAAQ,IAAI,CAAC;IAC3D;IAEA,QAAQ,IAAI;IAEZ,MAAM,YAAY,WAAW,UAAU,KAAA,EAAc,gBAAgB;IACrE,CAAC,uBAAuB,CAAC,EAAA,CAAG,KAAK,cAAc,UAAU,IAAI,CAAC;GAChE;GACA,IAAI,oBAAoB,MAAM,QAAQ,IAAI,kBAAkB;EAC9D;EAEA,MAAM,cAAc,MAAM,cAAc,SAAS,CAAC;CACpD,CAAC;AACH;;AAGA,SAAS,eAAe,UAAoB;CAC1C,OAAO,oBAAoB,WAAW,SAAS,iBAAiB,SAAS;AAC3E"}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
require("./BatchLoader.cjs");
|
|
3
3
|
const require_relations_Collection = require("../relations/Collection.cjs");
|
|
4
|
+
require("../relations/RecursiveCollection.cjs");
|
|
4
5
|
const require_fields = require("../fields.cjs");
|
|
6
|
+
const require_keys = require("../keys.cjs");
|
|
5
7
|
const require_queries_sql_keywords = require("../queries/sql/keywords.cjs");
|
|
6
8
|
const require_queries_find_QueryParser = require("../queries/find/QueryParser.cjs");
|
|
7
9
|
const require_relations_OneToOneReference = require("../relations/OneToOneReference.cjs");
|
|
8
|
-
require("../relations/RecursiveCollection.cjs");
|
|
9
10
|
const require_utils = require("../utils.cjs");
|
|
10
|
-
const require_keys = require("../keys.cjs");
|
|
11
11
|
const require_configure = require("../configure.cjs");
|
|
12
12
|
require("../index.cjs");
|
|
13
13
|
const require_EntityManager = require("../EntityManager.cjs");
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import "./BatchLoader.js";
|
|
2
2
|
import { isLoadedCollection } from "../relations/Collection.js";
|
|
3
|
+
import "../relations/RecursiveCollection.js";
|
|
3
4
|
import { getField } from "../fields.js";
|
|
5
|
+
import { deTagIds, maybeResolveReferenceToId } from "../keys.js";
|
|
4
6
|
import { kq } from "../queries/sql/keywords.js";
|
|
5
7
|
import { addTablePerClassJoinsAndClassTag } from "../queries/find/QueryParser.js";
|
|
6
8
|
import { isLoadedOneToOneReference } from "../relations/OneToOneReference.js";
|
|
7
|
-
import "../relations/RecursiveCollection.js";
|
|
8
9
|
import { abbreviation, groupBy } from "../utils.js";
|
|
9
|
-
import { deTagIds, maybeResolveReferenceToId } from "../keys.js";
|
|
10
10
|
import { getMetadataForType } from "../configure.js";
|
|
11
11
|
import "../index.js";
|
|
12
12
|
import { getEmInternalApi } from "../EntityManager.js";
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
require("./BatchLoader.cjs");
|
|
3
3
|
const require_relations_Collection = require("../relations/Collection.cjs");
|
|
4
|
-
const require_queries_sql_keywords = require("../queries/sql/keywords.cjs");
|
|
5
4
|
require("../relations/RecursiveCollection.cjs");
|
|
6
|
-
const require_utils = require("../utils.cjs");
|
|
7
5
|
const require_keys = require("../keys.cjs");
|
|
6
|
+
const require_queries_sql_keywords = require("../queries/sql/keywords.cjs");
|
|
7
|
+
const require_utils = require("../utils.cjs");
|
|
8
8
|
const require_EntityMetadata = require("../EntityMetadata.cjs");
|
|
9
9
|
require("../index.cjs");
|
|
10
10
|
const require_EntityManager = require("../EntityManager.cjs");
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import "./BatchLoader.js";
|
|
2
2
|
import { isLoadedCollection } from "../relations/Collection.js";
|
|
3
|
-
import { kq, kqDot, kqStar } from "../queries/sql/keywords.js";
|
|
4
3
|
import "../relations/RecursiveCollection.js";
|
|
5
|
-
import { abbreviation } from "../utils.js";
|
|
6
4
|
import { deTagIds, keyToTaggedId } from "../keys.js";
|
|
5
|
+
import { kq, kqDot, kqStar } from "../queries/sql/keywords.js";
|
|
6
|
+
import { abbreviation } from "../utils.js";
|
|
7
7
|
import { getMetadataForField } from "../EntityMetadata.js";
|
|
8
8
|
import "../index.js";
|
|
9
9
|
import { getEmInternalApi } from "../EntityManager.js";
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
require("./BatchLoader.cjs");
|
|
3
|
+
require("../relations/RecursiveCollection.cjs");
|
|
3
4
|
const require_fields = require("../fields.cjs");
|
|
5
|
+
const require_keys = require("../keys.cjs");
|
|
4
6
|
const require_queries_sql_keywords = require("../queries/sql/keywords.cjs");
|
|
5
7
|
const require_queries_find_QueryParser = require("../queries/find/QueryParser.cjs");
|
|
6
|
-
require("../relations/RecursiveCollection.cjs");
|
|
7
8
|
const require_utils = require("../utils.cjs");
|
|
8
|
-
const require_keys = require("../keys.cjs");
|
|
9
9
|
const require_configure = require("../configure.cjs");
|
|
10
10
|
require("../index.cjs");
|
|
11
11
|
require("../EntityManager.cjs");
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import "./BatchLoader.js";
|
|
2
|
+
import "../relations/RecursiveCollection.js";
|
|
2
3
|
import { getField } from "../fields.js";
|
|
4
|
+
import { deTagIds } from "../keys.js";
|
|
3
5
|
import { kq } from "../queries/sql/keywords.js";
|
|
4
6
|
import { addTablePerClassJoinsAndClassTag } from "../queries/find/QueryParser.js";
|
|
5
|
-
import "../relations/RecursiveCollection.js";
|
|
6
7
|
import { abbreviation } from "../utils.js";
|
|
7
|
-
import { deTagIds } from "../keys.js";
|
|
8
8
|
import { getMetadataForType } from "../configure.js";
|
|
9
9
|
import "../index.js";
|
|
10
10
|
import "../EntityManager.js";
|
package/build/changes.cjs
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_fields = require("./fields.cjs");
|
|
3
|
-
require("./JoinRows.cjs");
|
|
4
2
|
require("./relations/EnumCollection.cjs");
|
|
3
|
+
const require_fields = require("./fields.cjs");
|
|
5
4
|
const require_relations_LazyField = require("./relations/LazyField.cjs");
|
|
6
|
-
require("./typeMap.cjs");
|
|
7
5
|
const require_utils = require("./utils.cjs");
|
|
6
|
+
require("./typeMap.cjs");
|
|
8
7
|
const require_EntityMetadata = require("./EntityMetadata.cjs");
|
|
9
8
|
require("./index.cjs");
|
|
10
9
|
const require_EntityManager = require("./EntityManager.cjs");
|
package/build/changes.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"changes.cjs","names":["getInstanceData","getField","notLoadedValue","isEntity","isId","#entity","#m2m","#joinRows","getEmInternalApi","#added","#removed","#collection","#toCodes","#o2m","getMetadata","isChangeableField","assertNever"],"sources":["../src/changes.ts"],"sourcesContent":["import { getInstanceData } from \"./BaseEntity.ts\";\nimport { type Entity } from \"./Entity.ts\";\nimport { type IdOf } from \"./EntityManager.ts\";\nimport { getField, isChangeableField } from \"./fields.ts\";\nimport {\n type Field,\n type ManyToManyCollection,\n type OneToManyCollection,\n type RelationsOf,\n assertNever,\n getEmInternalApi,\n getMetadata,\n isEntity,\n isId,\n} from \"./index.ts\";\nimport { type JoinColumnValue, type JoinRows } from \"./JoinRows.ts\";\nimport { type EnumCollectionImpl } from \"./relations/EnumCollection.ts\";\nimport { notLoadedValue } from \"./relations/LazyField.ts\";\nimport { type FieldsOf, type OptsOf } from \"./typeMap.ts\";\n\n/** Exposes a field's changed/original value in each entity's `this.changes` property. */\nexport interface FieldStatus<T> {\n /** True if the field has been changed on either create or update. */\n hasChanged: boolean;\n /** True only if the field has been updated i.e. not on the initial create. */\n hasUpdated: boolean;\n /** The original value, will be `undefined` if the entity new. */\n originalValue?: T;\n}\n\n/**\n * Provides a base class for field/primitive & m2o status impls.\n *\n * We use a separate base class so that the primitive & m2o subtypes can have their own\n * `kind: ...` consts, that if the m2o just subtyped the primitive directly, wouldn't be\n * allowed.\n */\nabstract class BaseFieldStatusImpl<T> {\n entity: Entity;\n fieldName: string;\n\n constructor(entity: Entity, fieldName: string) {\n this.entity = entity;\n this.fieldName = fieldName;\n }\n\n get originalValue(): T | undefined {\n const { originalData, data } = getInstanceData(this.entity);\n // If `p` is in originalData, always respect that, even if it's undefined\n const value =\n this.fieldName in originalData ? originalData[this.fieldName] : getField(this.entity, this.fieldName as any);\n // An unloaded lazy column's original value is unknown\n return value === notLoadedValue ? undefined : value;\n }\n\n get hasChanged(): boolean {\n const { originalData, data } = getInstanceData(this.entity);\n // Use `__orm.data[p] !== undefined` instead of `p in entity.__orm.data` because if a new (or cloned) entity\n // sets-then-unsets a value, it will return to `undefined` but still be present in `__orm.data`.\n return this.entity.isNewEntity ? data[this.fieldName] !== undefined : this.fieldName in originalData;\n }\n\n get hasUpdated(): boolean {\n const { originalData } = getInstanceData(this.entity);\n return !this.entity.isNewEntity && this.fieldName in originalData;\n }\n}\n\n/** Field status for primitive, enum, & primary key columns. */\nexport interface PrimitiveFieldStatus<T> extends FieldStatus<T> {\n kind: \"field\";\n}\n\n/** Implements the primitive field status, by extend base & just adding our `kind` const. */\nclass PrimitiveFieldStatusImpl<T> extends BaseFieldStatusImpl<T> implements PrimitiveFieldStatus<T> {\n kind = \"field\" as const;\n}\n\n/** Implements the m2o field status, by extend base, & adding `originalEntity`. */\nclass ManyToOneFieldStatusImpl<T extends Entity>\n extends BaseFieldStatusImpl<IdOf<T>>\n implements ManyToOneFieldStatus<T>\n{\n kind = \"m2o\" as const;\n\n /**\n * Returns our original entity's `id`.\n *\n * We can know `originalValue` has an `id` available (i.e. is not new), b/c original values defacto\n * came from the database.\n *\n * We also return the id so that `entity.changes.m2oField.originalValue` has a consistent API,\n * regardless of whether the `entity.m2oField` itself is loaded or unloaded.\n */\n get originalValue() {\n const originalValue = super.originalValue;\n return isEntity(originalValue) ? (originalValue.id as IdOf<T>) : originalValue;\n }\n\n /** Returns the original entity instance, which may not be loaded into memory yet, or undefined. */\n get originalEntity(): Promise<T | undefined> {\n const originalValue = this.originalValue;\n if (isId(originalValue)) {\n return this.entity.em.load(originalValue) as Promise<T>;\n } else {\n return Promise.resolve(undefined);\n }\n }\n}\n\n/** Provides a m2o/reference field's changed/original value in the entity's `this.changes` property. */\nexport interface ManyToOneFieldStatus<T extends Entity> extends FieldStatus<IdOf<T>> {\n kind: \"m2o\";\n /** The original entity, will be `undefined` if the entity new or the m2o was `null`. */\n originalEntity: Promise<T | undefined>;\n}\n\n/** Provides a m2m relation's added/removed/changed/original values. */\nexport interface ManyToManyFieldStatus<U extends Entity> {\n kind: \"m2m\";\n added: U[];\n removed: U[];\n changed: U[];\n hasChanged: boolean;\n hasUpdated: boolean;\n originalEntities: Promise<readonly U[]>;\n}\n\nclass ManyToManyFieldStatusImpl<T extends Entity, U extends Entity> implements ManyToManyFieldStatus<U> {\n readonly kind = \"m2m\";\n readonly #entity: T;\n readonly #m2m: ManyToManyCollection<T, any>;\n readonly #joinRows: JoinRows;\n\n constructor(entity: T, fieldName: keyof T) {\n this.#entity = entity;\n this.#m2m = entity[fieldName] as ManyToManyCollection<T, any>;\n this.#joinRows = getEmInternalApi(entity.em).joinRows(this.#m2m);\n }\n\n // Similar to the o2m.added/removed methods, these don't have to be async because currently\n // any m2m mutation requires having both entities in-memory anyway, i.e. we can't do id-only/unloaded\n // mutation of m2m relations.\n get added(): U[] {\n return this.#added.sort(entityCompare);\n }\n\n get removed(): U[] {\n return this.#removed.sort(entityCompare);\n }\n\n get changed(): U[] {\n return [...this.#added, ...this.#removed].sort(entityCompare);\n }\n\n get hasChanged(): boolean {\n // Calculating `changed: U[]` can be expensive if we sort, so just look at added/removed\n return this.#added.length > 0 || this.#removed.length > 0;\n }\n\n get hasUpdated(): boolean {\n return !this.#entity.isNewEntity && this.hasChanged;\n }\n\n get originalEntities(): Promise<readonly U[]> {\n return this.#m2m.load().then((list) => {\n const set = new Set(this.added);\n const copy = list.filter((e) => !set.has(e));\n copy.push(...this.removed);\n return copy.sort(entityCompare);\n });\n }\n\n get #added(): U[] {\n return this.#joinRows.addedFor(this.#m2m, this.#entity) as U[];\n }\n\n get #removed(): U[] {\n return this.#joinRows.removedFor(this.#m2m, this.#entity) as U[];\n }\n}\n\n/** Provides an enum m2m's added/removed/changed/original codes (e.g. `Publisher.logoColors`). */\nexport interface EnumCollectionFieldStatus<E> {\n kind: \"m2mEnum\";\n added: E[];\n removed: E[];\n changed: E[];\n hasChanged: boolean;\n hasUpdated: boolean;\n originalValues: Promise<readonly E[]>;\n}\n\nclass EnumCollectionFieldStatusImpl<T extends Entity, E> implements EnumCollectionFieldStatus<E> {\n readonly kind = \"m2mEnum\";\n readonly #entity: T;\n readonly #collection: EnumCollectionImpl<T, E>;\n readonly #joinRows: JoinRows;\n\n constructor(entity: T, fieldName: keyof T) {\n this.#entity = entity;\n this.#collection = entity[fieldName] as EnumCollectionImpl<T, E>;\n this.#joinRows = getEmInternalApi(entity.em).joinRows(this.#collection);\n }\n\n get added(): E[] {\n return this.#toCodes(this.#joinRows.addedFor(this.#collection, this.#entity));\n }\n\n get removed(): E[] {\n return this.#toCodes(this.#joinRows.removedFor(this.#collection, this.#entity));\n }\n\n get changed(): E[] {\n return [...this.added, ...this.removed];\n }\n\n get hasChanged(): boolean {\n return this.added.length > 0 || this.removed.length > 0;\n }\n\n get hasUpdated(): boolean {\n return !this.#entity.isNewEntity && this.hasChanged;\n }\n\n get originalValues(): Promise<readonly E[]> {\n // `load()` ensures the persisted (db) rows are in memory, then diff out our pending changes.\n return this.#collection.load().then((codes) => {\n const added = new Set(this.added);\n const enumDetail = this.#collection.otherEnum;\n return [...codes.filter((c) => !added.has(c)), ...this.removed].sort(\n (a, b) => enumDetail.getByCode(a).id - enumDetail.getByCode(b).id,\n );\n });\n }\n\n /** Maps the join rows' enum ids to codes, sorted by enum id for a stable order. */\n #toCodes(ids: JoinColumnValue[]): E[] {\n const enumDetail = this.#collection.otherEnum;\n return (ids as number[]).sort((a, b) => a - b).map((id) => enumDetail.findById(id)!.code as E);\n }\n}\n\nexport interface OneToManyFieldStatus<U extends Entity> {\n kind: \"o2m\";\n added: U[];\n removed: U[];\n changed: U[];\n hasChanged: boolean;\n hasUpdated: boolean;\n originalEntities: Promise<readonly U[]>;\n}\n\n/** Provides access to an o2m relation's added/removed/changed entities. */\nclass OneToManyFieldStatusImpl<T extends Entity, U extends Entity> implements OneToManyFieldStatus<U> {\n readonly kind = \"o2m\";\n readonly #entity: T;\n readonly #o2m: OneToManyCollection<T, U>;\n\n constructor(entity: T, fieldName: keyof T) {\n this.#entity = entity;\n this.#o2m = entity[fieldName] as OneToManyCollection<T, U>;\n }\n\n // This doesn't have to be a promise, b/c even if o2m is unloaded, to mutate\n // the o2m (calling `add(other)` or `remove(other)` or `other.otherField = me`), all\n // require having the \"other entity\" in memory.\n get added(): U[] {\n return this.#added.sort(entityCompare);\n }\n\n get removed(): U[] {\n return this.#removed.sort(entityCompare);\n }\n\n get changed(): U[] {\n return [...this.added, ...this.removed].sort(entityCompare);\n }\n\n get hasChanged(): boolean {\n // Calculating `changed: U[]` can be expensive if we sort, so just look at added/removed\n return this.#added.length > 0 || this.#removed.length > 0;\n }\n\n get hasUpdated(): boolean {\n return !this.#entity.isNewEntity && this.hasChanged;\n }\n\n get originalEntities(): Promise<readonly U[]> {\n return this.#o2m.load().then((list) => {\n const set = new Set(this.added);\n const copy = list.filter((e) => !set.has(e));\n copy.push(...this.removed);\n return copy.sort(entityCompare);\n });\n }\n\n // This doesn't have to be a promise, b/c even if o2m is unloaded, to mutate\n // the o2m (calling `add(other)` or `remove(other)` or `other.otherField = me`), all\n // require having the \"other entity\" in memory.\n get #added(): U[] {\n return [\n ...this.#o2m.added(),\n ...(this.#entity.isNewEntity\n ? []\n : ((getEmInternalApi(this.#entity.em).pendingPercolate.get(this.#entity.idTagged)?.get(this.#o2m.fieldName)\n ?.adds as U[]) ?? [])),\n ];\n }\n\n get #removed(): U[] {\n return [\n ...this.#o2m.removed(),\n ...(this.#entity.isNewEntity\n ? []\n : ((getEmInternalApi(this.#entity.em).pendingPercolate.get(this.#entity.idTagged)?.get(this.#o2m.fieldName)\n ?.removes as U[]) ?? [])),\n ];\n }\n}\n\n/**\n * Creates the `this.changes.firstName` changes API for a given entity `T`.\n *\n * We use `FieldsOf` because that already excludes collection, and then also\n * convert reference fields to `ManyToOneFieldStatus` to be the id type\n * because the reference may not be loaded.\n *\n * @typeParam K The fields of the entity, or potentially the union of the entity and its subtypes,\n * i.e. `Publisher.changes` is typed as `Changes<Publisher, keyof Publisher | keyof SmallPub | keyof LargePub>`\n * @typeParam R An optional list of restrictions, i.e for `Reacted` for to provide `changes` to its subset of fields.\n */\nexport type Changes<T extends Entity, K = keyof (FieldsOf<T> & RelationsOf<T>), R = K> = {\n /** Array of changed field names. */\n fields: NonNullable<K>[];\n /** Array of changed field names w/o o2m & m2m relations (which can be expensive). */\n fieldsWithoutRelations: NonNullable<K>[];\n} & {\n // The o2o branch is `never`: the FK lives on the other entity, so changes never track o2o fields.\n // (The exclusion must stay in the value position: a conditional in the `[P in ...]` clause defers on\n // a generic `T` and collapses `keyof Changes<T>` at inference time, i.e. in `cannotBeUpdated` calls.)\n [P in keyof FieldsOf<T> & R]: FieldsOf<T>[P] extends { kind: \"m2m\"; type: infer U extends Entity }\n ? ManyToManyFieldStatus<U>\n : FieldsOf<T>[P] extends { kind: \"m2mEnum\"; type: infer E }\n ? EnumCollectionFieldStatus<E>\n : FieldsOf<T>[P] extends { kind: \"o2m\"; type: infer U extends Entity }\n ? OneToManyFieldStatus<U>\n : FieldsOf<T>[P] extends { kind: \"o2o\" }\n ? never\n : FieldsOf<T>[P] extends { type: (infer U) | undefined }\n ? U extends Entity\n ? ManyToOneFieldStatus<U>\n : PrimitiveFieldStatus<U>\n : never;\n};\n\n// type A1 = never extends string ? 1 : 2;\n// type A2 = Book extends Entity | undefined ? 1 : 2;\n// type A2a = string extends Entity | string | undefined ? 1 : 2;\n// type A3 = Book | undefined extends Entity ? 1 : 2;\n// type A4 = Book | undefined | null extends (infer E extends Entity) | undefined | null ? E : 2;\n\n/**\n * A strongly-typed Entity with its changes field.\n *\n * Ideally this would live on `Entity` directly, but results in circular type issues.\n */\nexport interface EntityChanges<T extends Entity> {\n changes: Changes<T>;\n}\n\nexport function newChangesProxy<T extends Entity>(entity: T): Changes<T> {\n return new Proxy(entity, {\n get(\n target,\n p: PropertyKey & string,\n ):\n | FieldStatus<any>\n | ManyToOneFieldStatus<any>\n | ManyToManyFieldStatus<any>\n | OneToManyFieldStatus<any>\n | (keyof OptsOf<T>)[] {\n if (p === \"fields\") {\n return getChangedFieldNames(entity, true);\n } else if (p === \"fieldsWithoutRelations\") {\n return getChangedFieldNames(entity, false);\n } else if (typeof p === \"symbol\") {\n throw new Error(`Unsupported call to ${String(p)}`);\n }\n const kind = getMetadata(entity).allFields[p]?.kind;\n if (kind === \"m2m\") {\n return new ManyToManyFieldStatusImpl(entity, p as keyof T);\n } else if (kind === \"m2mEnum\") {\n return new EnumCollectionFieldStatusImpl(entity, p as keyof T);\n } else if (kind === \"o2m\") {\n return new OneToManyFieldStatusImpl(entity, p as keyof T);\n } else if (!isChangeableField(entity, p as any)) {\n throw new Error(`Invalid changes field ${p}`);\n } else if (kind === \"m2o\" || kind === \"poly\") {\n return new ManyToOneFieldStatusImpl(entity, p);\n } else if (kind === \"enum\" || kind === \"primitive\" || kind === \"primaryKey\") {\n return new PrimitiveFieldStatusImpl(entity, p);\n } else if (kind === \"lo2m\" || kind === \"o2o\") {\n throw new Error(`changes are not supported for ${kind} ${p}`);\n } else {\n return assertNever(kind);\n }\n },\n }) as any;\n}\n\nconst addOriginalEntity: Record<Field[\"kind\"], boolean> = {\n m2o: true,\n poly: true,\n m2mEnum: false,\n enum: false,\n lo2m: false,\n m2m: false,\n o2m: false,\n o2o: false,\n primaryKey: false,\n primitive: false,\n};\n\n/** Scans `entity` for changes primitive + m2m/o2m fields. */\nfunction getChangedFieldNames<T extends Entity>(entity: T, includeRelations: boolean): (keyof OptsOf<T>)[] {\n const fieldsChanged = entity.isNewEntity\n ? // Cloning sometimes leaves unset keys in data as undefined, so drop them\n Object.entries(getInstanceData(entity).data)\n .filter(([, value]) => value !== undefined)\n .map(([key]) => key)\n : Object.keys(getInstanceData(entity).originalData);\n\n // Calling `hasChanged` on these can be expensive, and we call this in a loop during em.flush\n if (includeRelations) {\n for (const field of Object.values(getMetadata(entity).allFields)) {\n if (field.kind === \"m2m\") {\n const status = new ManyToManyFieldStatusImpl(entity, field.fieldName as keyof T);\n if (status.hasChanged) {\n fieldsChanged.push(field.fieldName);\n }\n } else if (field.kind === \"o2m\") {\n const status = new OneToManyFieldStatusImpl(entity, field.fieldName as keyof T);\n if (status.hasChanged) {\n fieldsChanged.push(field.fieldName);\n }\n } else if (field.kind === \"m2mEnum\") {\n const status = new EnumCollectionFieldStatusImpl(entity, field.fieldName as keyof T);\n if (status.hasChanged) {\n fieldsChanged.push(field.fieldName);\n }\n }\n }\n }\n\n return fieldsChanged as any;\n}\n\nconst entityCompare: (a: Entity, b: Entity) => number = (a, b) => {\n return a.toString().localeCompare(b.toString());\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"changes.cjs","names":["getInstanceData","getField","notLoadedValue","isEntity","isId","#entity","#m2m","#joinRows","getEmInternalApi","#added","#removed","#collection","#toCodes","#o2m","getMetadata","isChangeableField","assertNever"],"sources":["../src/changes.ts"],"sourcesContent":["import { getInstanceData } from \"./BaseEntity.ts\";\nimport { type Entity } from \"./Entity.ts\";\nimport { type IdOf } from \"./EntityManager.ts\";\nimport { getField, isChangeableField } from \"./fields.ts\";\nimport type { JoinColumnValue, JoinRows } from \"./flush/JoinRows.ts\";\nimport {\n type Field,\n type ManyToManyCollection,\n type OneToManyCollection,\n type RelationsOf,\n assertNever,\n getEmInternalApi,\n getMetadata,\n isEntity,\n isId,\n} from \"./index.ts\";\nimport { type EnumCollectionImpl } from \"./relations/EnumCollection.ts\";\nimport { notLoadedValue } from \"./relations/LazyField.ts\";\nimport { type FieldsOf, type OptsOf } from \"./typeMap.ts\";\n\n/** Exposes a field's changed/original value in each entity's `this.changes` property. */\nexport interface FieldStatus<T> {\n /** True if the field has been changed on either create or update. */\n hasChanged: boolean;\n /** True only if the field has been updated i.e. not on the initial create. */\n hasUpdated: boolean;\n /** The original value, will be `undefined` if the entity new. */\n originalValue?: T;\n}\n\n/**\n * Provides a base class for field/primitive & m2o status impls.\n *\n * We use a separate base class so that the primitive & m2o subtypes can have their own\n * `kind: ...` consts, that if the m2o just subtyped the primitive directly, wouldn't be\n * allowed.\n */\nabstract class BaseFieldStatusImpl<T> {\n entity: Entity;\n fieldName: string;\n\n constructor(entity: Entity, fieldName: string) {\n this.entity = entity;\n this.fieldName = fieldName;\n }\n\n get originalValue(): T | undefined {\n const { originalData, data } = getInstanceData(this.entity);\n // If `p` is in originalData, always respect that, even if it's undefined\n const value =\n this.fieldName in originalData ? originalData[this.fieldName] : getField(this.entity, this.fieldName as any);\n // An unloaded lazy column's original value is unknown\n return value === notLoadedValue ? undefined : value;\n }\n\n get hasChanged(): boolean {\n const { originalData, data } = getInstanceData(this.entity);\n // Use `__orm.data[p] !== undefined` instead of `p in entity.__orm.data` because if a new (or cloned) entity\n // sets-then-unsets a value, it will return to `undefined` but still be present in `__orm.data`.\n return this.entity.isNewEntity ? data[this.fieldName] !== undefined : this.fieldName in originalData;\n }\n\n get hasUpdated(): boolean {\n const { originalData } = getInstanceData(this.entity);\n return !this.entity.isNewEntity && this.fieldName in originalData;\n }\n}\n\n/** Field status for primitive, enum, & primary key columns. */\nexport interface PrimitiveFieldStatus<T> extends FieldStatus<T> {\n kind: \"field\";\n}\n\n/** Implements the primitive field status, by extend base & just adding our `kind` const. */\nclass PrimitiveFieldStatusImpl<T> extends BaseFieldStatusImpl<T> implements PrimitiveFieldStatus<T> {\n kind = \"field\" as const;\n}\n\n/** Implements the m2o field status, by extend base, & adding `originalEntity`. */\nclass ManyToOneFieldStatusImpl<T extends Entity>\n extends BaseFieldStatusImpl<IdOf<T>>\n implements ManyToOneFieldStatus<T>\n{\n kind = \"m2o\" as const;\n\n /**\n * Returns our original entity's `id`.\n *\n * We can know `originalValue` has an `id` available (i.e. is not new), b/c original values defacto\n * came from the database.\n *\n * We also return the id so that `entity.changes.m2oField.originalValue` has a consistent API,\n * regardless of whether the `entity.m2oField` itself is loaded or unloaded.\n */\n get originalValue() {\n const originalValue = super.originalValue;\n return isEntity(originalValue) ? (originalValue.id as IdOf<T>) : originalValue;\n }\n\n /** Returns the original entity instance, which may not be loaded into memory yet, or undefined. */\n get originalEntity(): Promise<T | undefined> {\n const originalValue = this.originalValue;\n if (isId(originalValue)) {\n return this.entity.em.load(originalValue) as Promise<T>;\n } else {\n return Promise.resolve(undefined);\n }\n }\n}\n\n/** Provides a m2o/reference field's changed/original value in the entity's `this.changes` property. */\nexport interface ManyToOneFieldStatus<T extends Entity> extends FieldStatus<IdOf<T>> {\n kind: \"m2o\";\n /** The original entity, will be `undefined` if the entity new or the m2o was `null`. */\n originalEntity: Promise<T | undefined>;\n}\n\n/** Provides a m2m relation's added/removed/changed/original values. */\nexport interface ManyToManyFieldStatus<U extends Entity> {\n kind: \"m2m\";\n added: U[];\n removed: U[];\n changed: U[];\n hasChanged: boolean;\n hasUpdated: boolean;\n originalEntities: Promise<readonly U[]>;\n}\n\nclass ManyToManyFieldStatusImpl<T extends Entity, U extends Entity> implements ManyToManyFieldStatus<U> {\n readonly kind = \"m2m\";\n readonly #entity: T;\n readonly #m2m: ManyToManyCollection<T, any>;\n readonly #joinRows: JoinRows;\n\n constructor(entity: T, fieldName: keyof T) {\n this.#entity = entity;\n this.#m2m = entity[fieldName] as ManyToManyCollection<T, any>;\n this.#joinRows = getEmInternalApi(entity.em).joinRows(this.#m2m);\n }\n\n // Similar to the o2m.added/removed methods, these don't have to be async because currently\n // any m2m mutation requires having both entities in-memory anyway, i.e. we can't do id-only/unloaded\n // mutation of m2m relations.\n get added(): U[] {\n return this.#added.sort(entityCompare);\n }\n\n get removed(): U[] {\n return this.#removed.sort(entityCompare);\n }\n\n get changed(): U[] {\n return [...this.#added, ...this.#removed].sort(entityCompare);\n }\n\n get hasChanged(): boolean {\n // Calculating `changed: U[]` can be expensive if we sort, so just look at added/removed\n return this.#added.length > 0 || this.#removed.length > 0;\n }\n\n get hasUpdated(): boolean {\n return !this.#entity.isNewEntity && this.hasChanged;\n }\n\n get originalEntities(): Promise<readonly U[]> {\n return this.#m2m.load().then((list) => {\n const set = new Set(this.added);\n const copy = list.filter((e) => !set.has(e));\n copy.push(...this.removed);\n return copy.sort(entityCompare);\n });\n }\n\n get #added(): U[] {\n return this.#joinRows.addedFor(this.#m2m, this.#entity) as U[];\n }\n\n get #removed(): U[] {\n return this.#joinRows.removedFor(this.#m2m, this.#entity) as U[];\n }\n}\n\n/** Provides an enum m2m's added/removed/changed/original codes (e.g. `Publisher.logoColors`). */\nexport interface EnumCollectionFieldStatus<E> {\n kind: \"m2mEnum\";\n added: E[];\n removed: E[];\n changed: E[];\n hasChanged: boolean;\n hasUpdated: boolean;\n originalValues: Promise<readonly E[]>;\n}\n\nclass EnumCollectionFieldStatusImpl<T extends Entity, E> implements EnumCollectionFieldStatus<E> {\n readonly kind = \"m2mEnum\";\n readonly #entity: T;\n readonly #collection: EnumCollectionImpl<T, E>;\n readonly #joinRows: JoinRows;\n\n constructor(entity: T, fieldName: keyof T) {\n this.#entity = entity;\n this.#collection = entity[fieldName] as EnumCollectionImpl<T, E>;\n this.#joinRows = getEmInternalApi(entity.em).joinRows(this.#collection);\n }\n\n get added(): E[] {\n return this.#toCodes(this.#joinRows.addedFor(this.#collection, this.#entity));\n }\n\n get removed(): E[] {\n return this.#toCodes(this.#joinRows.removedFor(this.#collection, this.#entity));\n }\n\n get changed(): E[] {\n return [...this.added, ...this.removed];\n }\n\n get hasChanged(): boolean {\n return this.added.length > 0 || this.removed.length > 0;\n }\n\n get hasUpdated(): boolean {\n return !this.#entity.isNewEntity && this.hasChanged;\n }\n\n get originalValues(): Promise<readonly E[]> {\n // `load()` ensures the persisted (db) rows are in memory, then diff out our pending changes.\n return this.#collection.load().then((codes) => {\n const added = new Set(this.added);\n const enumDetail = this.#collection.otherEnum;\n return [...codes.filter((c) => !added.has(c)), ...this.removed].sort(\n (a, b) => enumDetail.getByCode(a).id - enumDetail.getByCode(b).id,\n );\n });\n }\n\n /** Maps the join rows' enum ids to codes, sorted by enum id for a stable order. */\n #toCodes(ids: JoinColumnValue[]): E[] {\n const enumDetail = this.#collection.otherEnum;\n return (ids as number[]).sort((a, b) => a - b).map((id) => enumDetail.findById(id)!.code as E);\n }\n}\n\nexport interface OneToManyFieldStatus<U extends Entity> {\n kind: \"o2m\";\n added: U[];\n removed: U[];\n changed: U[];\n hasChanged: boolean;\n hasUpdated: boolean;\n originalEntities: Promise<readonly U[]>;\n}\n\n/** Provides access to an o2m relation's added/removed/changed entities. */\nclass OneToManyFieldStatusImpl<T extends Entity, U extends Entity> implements OneToManyFieldStatus<U> {\n readonly kind = \"o2m\";\n readonly #entity: T;\n readonly #o2m: OneToManyCollection<T, U>;\n\n constructor(entity: T, fieldName: keyof T) {\n this.#entity = entity;\n this.#o2m = entity[fieldName] as OneToManyCollection<T, U>;\n }\n\n // This doesn't have to be a promise, b/c even if o2m is unloaded, to mutate\n // the o2m (calling `add(other)` or `remove(other)` or `other.otherField = me`), all\n // require having the \"other entity\" in memory.\n get added(): U[] {\n return this.#added.sort(entityCompare);\n }\n\n get removed(): U[] {\n return this.#removed.sort(entityCompare);\n }\n\n get changed(): U[] {\n return [...this.added, ...this.removed].sort(entityCompare);\n }\n\n get hasChanged(): boolean {\n // Calculating `changed: U[]` can be expensive if we sort, so just look at added/removed\n return this.#added.length > 0 || this.#removed.length > 0;\n }\n\n get hasUpdated(): boolean {\n return !this.#entity.isNewEntity && this.hasChanged;\n }\n\n get originalEntities(): Promise<readonly U[]> {\n return this.#o2m.load().then((list) => {\n const set = new Set(this.added);\n const copy = list.filter((e) => !set.has(e));\n copy.push(...this.removed);\n return copy.sort(entityCompare);\n });\n }\n\n // This doesn't have to be a promise, b/c even if o2m is unloaded, to mutate\n // the o2m (calling `add(other)` or `remove(other)` or `other.otherField = me`), all\n // require having the \"other entity\" in memory.\n get #added(): U[] {\n return [\n ...this.#o2m.added(),\n ...(this.#entity.isNewEntity\n ? []\n : ((getEmInternalApi(this.#entity.em).pendingPercolate.get(this.#entity.idTagged)?.get(this.#o2m.fieldName)\n ?.adds as U[]) ?? [])),\n ];\n }\n\n get #removed(): U[] {\n return [\n ...this.#o2m.removed(),\n ...(this.#entity.isNewEntity\n ? []\n : ((getEmInternalApi(this.#entity.em).pendingPercolate.get(this.#entity.idTagged)?.get(this.#o2m.fieldName)\n ?.removes as U[]) ?? [])),\n ];\n }\n}\n\n/**\n * Creates the `this.changes.firstName` changes API for a given entity `T`.\n *\n * We use `FieldsOf` because that already excludes collection, and then also\n * convert reference fields to `ManyToOneFieldStatus` to be the id type\n * because the reference may not be loaded.\n *\n * @typeParam K The fields of the entity, or potentially the union of the entity and its subtypes,\n * i.e. `Publisher.changes` is typed as `Changes<Publisher, keyof Publisher | keyof SmallPub | keyof LargePub>`\n * @typeParam R An optional list of restrictions, i.e for `Reacted` for to provide `changes` to its subset of fields.\n */\nexport type Changes<T extends Entity, K = keyof (FieldsOf<T> & RelationsOf<T>), R = K> = {\n /** Array of changed field names. */\n fields: NonNullable<K>[];\n /** Array of changed field names w/o o2m & m2m relations (which can be expensive). */\n fieldsWithoutRelations: NonNullable<K>[];\n} & {\n // The o2o branch is `never`: the FK lives on the other entity, so changes never track o2o fields.\n // (The exclusion must stay in the value position: a conditional in the `[P in ...]` clause defers on\n // a generic `T` and collapses `keyof Changes<T>` at inference time, i.e. in `cannotBeUpdated` calls.)\n [P in keyof FieldsOf<T> & R]: FieldsOf<T>[P] extends { kind: \"m2m\"; type: infer U extends Entity }\n ? ManyToManyFieldStatus<U>\n : FieldsOf<T>[P] extends { kind: \"m2mEnum\"; type: infer E }\n ? EnumCollectionFieldStatus<E>\n : FieldsOf<T>[P] extends { kind: \"o2m\"; type: infer U extends Entity }\n ? OneToManyFieldStatus<U>\n : FieldsOf<T>[P] extends { kind: \"o2o\" }\n ? never\n : FieldsOf<T>[P] extends { type: (infer U) | undefined }\n ? U extends Entity\n ? ManyToOneFieldStatus<U>\n : PrimitiveFieldStatus<U>\n : never;\n};\n\n// type A1 = never extends string ? 1 : 2;\n// type A2 = Book extends Entity | undefined ? 1 : 2;\n// type A2a = string extends Entity | string | undefined ? 1 : 2;\n// type A3 = Book | undefined extends Entity ? 1 : 2;\n// type A4 = Book | undefined | null extends (infer E extends Entity) | undefined | null ? E : 2;\n\n/**\n * A strongly-typed Entity with its changes field.\n *\n * Ideally this would live on `Entity` directly, but results in circular type issues.\n */\nexport interface EntityChanges<T extends Entity> {\n changes: Changes<T>;\n}\n\nexport function newChangesProxy<T extends Entity>(entity: T): Changes<T> {\n return new Proxy(entity, {\n get(\n target,\n p: PropertyKey & string,\n ):\n | FieldStatus<any>\n | ManyToOneFieldStatus<any>\n | ManyToManyFieldStatus<any>\n | OneToManyFieldStatus<any>\n | (keyof OptsOf<T>)[] {\n if (p === \"fields\") {\n return getChangedFieldNames(entity, true);\n } else if (p === \"fieldsWithoutRelations\") {\n return getChangedFieldNames(entity, false);\n } else if (typeof p === \"symbol\") {\n throw new Error(`Unsupported call to ${String(p)}`);\n }\n const kind = getMetadata(entity).allFields[p]?.kind;\n if (kind === \"m2m\") {\n return new ManyToManyFieldStatusImpl(entity, p as keyof T);\n } else if (kind === \"m2mEnum\") {\n return new EnumCollectionFieldStatusImpl(entity, p as keyof T);\n } else if (kind === \"o2m\") {\n return new OneToManyFieldStatusImpl(entity, p as keyof T);\n } else if (!isChangeableField(entity, p as any)) {\n throw new Error(`Invalid changes field ${p}`);\n } else if (kind === \"m2o\" || kind === \"poly\") {\n return new ManyToOneFieldStatusImpl(entity, p);\n } else if (kind === \"enum\" || kind === \"primitive\" || kind === \"primaryKey\") {\n return new PrimitiveFieldStatusImpl(entity, p);\n } else if (kind === \"lo2m\" || kind === \"o2o\") {\n throw new Error(`changes are not supported for ${kind} ${p}`);\n } else {\n return assertNever(kind);\n }\n },\n }) as any;\n}\n\nconst addOriginalEntity: Record<Field[\"kind\"], boolean> = {\n m2o: true,\n poly: true,\n m2mEnum: false,\n enum: false,\n lo2m: false,\n m2m: false,\n o2m: false,\n o2o: false,\n primaryKey: false,\n primitive: false,\n};\n\n/** Scans `entity` for changes primitive + m2m/o2m fields. */\nfunction getChangedFieldNames<T extends Entity>(entity: T, includeRelations: boolean): (keyof OptsOf<T>)[] {\n const fieldsChanged = entity.isNewEntity\n ? // Cloning sometimes leaves unset keys in data as undefined, so drop them\n Object.entries(getInstanceData(entity).data)\n .filter(([, value]) => value !== undefined)\n .map(([key]) => key)\n : Object.keys(getInstanceData(entity).originalData);\n\n // Calling `hasChanged` on these can be expensive, and we call this in a loop during em.flush\n if (includeRelations) {\n for (const field of Object.values(getMetadata(entity).allFields)) {\n if (field.kind === \"m2m\") {\n const status = new ManyToManyFieldStatusImpl(entity, field.fieldName as keyof T);\n if (status.hasChanged) {\n fieldsChanged.push(field.fieldName);\n }\n } else if (field.kind === \"o2m\") {\n const status = new OneToManyFieldStatusImpl(entity, field.fieldName as keyof T);\n if (status.hasChanged) {\n fieldsChanged.push(field.fieldName);\n }\n } else if (field.kind === \"m2mEnum\") {\n const status = new EnumCollectionFieldStatusImpl(entity, field.fieldName as keyof T);\n if (status.hasChanged) {\n fieldsChanged.push(field.fieldName);\n }\n }\n }\n }\n\n return fieldsChanged as any;\n}\n\nconst entityCompare: (a: Entity, b: Entity) => number = (a, b) => {\n return a.toString().localeCompare(b.toString());\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAqCA,IAAe,sBAAf,MAAsC;CACpC;CACA;CAEA,YAAY,QAAgB,WAAmB;EAC7C,KAAK,SAAS;EACd,KAAK,YAAY;CACnB;CAEA,IAAI,gBAA+B;EACjC,MAAM,EAAE,cAAc,SAASA,mBAAAA,gBAAgB,KAAK,MAAM;EAE1D,MAAM,QACJ,KAAK,aAAa,eAAe,aAAa,KAAK,aAAaC,eAAAA,SAAS,KAAK,QAAQ,KAAK,SAAgB;EAE7G,OAAO,UAAUC,4BAAAA,iBAAiB,KAAA,IAAY;CAChD;CAEA,IAAI,aAAsB;EACxB,MAAM,EAAE,cAAc,SAASF,mBAAAA,gBAAgB,KAAK,MAAM;EAG1D,OAAO,KAAK,OAAO,cAAc,KAAK,KAAK,eAAe,KAAA,IAAY,KAAK,aAAa;CAC1F;CAEA,IAAI,aAAsB;EACxB,MAAM,EAAE,iBAAiBA,mBAAAA,gBAAgB,KAAK,MAAM;EACpD,OAAO,CAAC,KAAK,OAAO,eAAe,KAAK,aAAa;CACvD;AACF;;AAQA,IAAM,2BAAN,cAA0C,oBAA0D;CAClG,OAAO;AACT;;AAGA,IAAM,2BAAN,cACU,oBAEV;CACE,OAAO;;;;;;;;;;CAWP,IAAI,gBAAgB;EAClB,MAAM,gBAAgB,MAAM;EAC5B,OAAOG,eAAAA,SAAS,aAAa,IAAK,cAAc,KAAiB;CACnE;;CAGA,IAAI,iBAAyC;EAC3C,MAAM,gBAAgB,KAAK;EAC3B,IAAIC,sBAAAA,KAAK,aAAa,GACpB,OAAO,KAAK,OAAO,GAAG,KAAK,aAAa;OAExC,OAAO,QAAQ,QAAQ,KAAA,CAAS;CAEpC;AACF;AAoBA,IAAM,4BAAN,MAAwG;CACtG,OAAgB;CAChB;CACA;CACA;CAEA,YAAY,QAAW,WAAoB;EACzC,KAAKC,UAAU;EACf,KAAKC,OAAO,OAAO;EACnB,KAAKC,YAAYC,sBAAAA,iBAAiB,OAAO,EAAE,CAAC,CAAC,SAAS,KAAKF,IAAI;CACjE;CAKA,IAAI,QAAa;EACf,OAAO,KAAKG,OAAO,KAAK,aAAa;CACvC;CAEA,IAAI,UAAe;EACjB,OAAO,KAAKC,SAAS,KAAK,aAAa;CACzC;CAEA,IAAI,UAAe;EACjB,OAAO,CAAC,GAAG,KAAKD,QAAQ,GAAG,KAAKC,QAAQ,CAAC,CAAC,KAAK,aAAa;CAC9D;CAEA,IAAI,aAAsB;EAExB,OAAO,KAAKD,OAAO,SAAS,KAAK,KAAKC,SAAS,SAAS;CAC1D;CAEA,IAAI,aAAsB;EACxB,OAAO,CAAC,KAAKL,QAAQ,eAAe,KAAK;CAC3C;CAEA,IAAI,mBAA0C;EAC5C,OAAO,KAAKC,KAAK,KAAK,CAAC,CAAC,MAAM,SAAS;GACrC,MAAM,MAAM,IAAI,IAAI,KAAK,KAAK;GAC9B,MAAM,OAAO,KAAK,QAAQ,MAAM,CAAC,IAAI,IAAI,CAAC,CAAC;GAC3C,KAAK,KAAK,GAAG,KAAK,OAAO;GACzB,OAAO,KAAK,KAAK,aAAa;EAChC,CAAC;CACH;CAEA,IAAIG,SAAc;EAChB,OAAO,KAAKF,UAAU,SAAS,KAAKD,MAAM,KAAKD,OAAO;CACxD;CAEA,IAAIK,WAAgB;EAClB,OAAO,KAAKH,UAAU,WAAW,KAAKD,MAAM,KAAKD,OAAO;CAC1D;AACF;AAaA,IAAM,gCAAN,MAAiG;CAC/F,OAAgB;CAChB;CACA;CACA;CAEA,YAAY,QAAW,WAAoB;EACzC,KAAKA,UAAU;EACf,KAAKM,cAAc,OAAO;EAC1B,KAAKJ,YAAYC,sBAAAA,iBAAiB,OAAO,EAAE,CAAC,CAAC,SAAS,KAAKG,WAAW;CACxE;CAEA,IAAI,QAAa;EACf,OAAO,KAAKC,SAAS,KAAKL,UAAU,SAAS,KAAKI,aAAa,KAAKN,OAAO,CAAC;CAC9E;CAEA,IAAI,UAAe;EACjB,OAAO,KAAKO,SAAS,KAAKL,UAAU,WAAW,KAAKI,aAAa,KAAKN,OAAO,CAAC;CAChF;CAEA,IAAI,UAAe;EACjB,OAAO,CAAC,GAAG,KAAK,OAAO,GAAG,KAAK,OAAO;CACxC;CAEA,IAAI,aAAsB;EACxB,OAAO,KAAK,MAAM,SAAS,KAAK,KAAK,QAAQ,SAAS;CACxD;CAEA,IAAI,aAAsB;EACxB,OAAO,CAAC,KAAKA,QAAQ,eAAe,KAAK;CAC3C;CAEA,IAAI,iBAAwC;EAE1C,OAAO,KAAKM,YAAY,KAAK,CAAC,CAAC,MAAM,UAAU;GAC7C,MAAM,QAAQ,IAAI,IAAI,KAAK,KAAK;GAChC,MAAM,aAAa,KAAKA,YAAY;GACpC,OAAO,CAAC,GAAG,MAAM,QAAQ,MAAM,CAAC,MAAM,IAAI,CAAC,CAAC,GAAG,GAAG,KAAK,OAAO,CAAC,CAAC,MAC7D,GAAG,MAAM,WAAW,UAAU,CAAC,CAAC,CAAC,KAAK,WAAW,UAAU,CAAC,CAAC,CAAC,EACjE;EACF,CAAC;CACH;;CAGA,SAAS,KAA6B;EACpC,MAAM,aAAa,KAAKA,YAAY;EACpC,OAAQ,IAAiB,MAAM,GAAG,MAAM,IAAI,CAAC,CAAC,CAAC,KAAK,OAAO,WAAW,SAAS,EAAE,CAAC,CAAE,IAAS;CAC/F;AACF;;AAaA,IAAM,2BAAN,MAAsG;CACpG,OAAgB;CAChB;CACA;CAEA,YAAY,QAAW,WAAoB;EACzC,KAAKN,UAAU;EACf,KAAKQ,OAAO,OAAO;CACrB;CAKA,IAAI,QAAa;EACf,OAAO,KAAKJ,OAAO,KAAK,aAAa;CACvC;CAEA,IAAI,UAAe;EACjB,OAAO,KAAKC,SAAS,KAAK,aAAa;CACzC;CAEA,IAAI,UAAe;EACjB,OAAO,CAAC,GAAG,KAAK,OAAO,GAAG,KAAK,OAAO,CAAC,CAAC,KAAK,aAAa;CAC5D;CAEA,IAAI,aAAsB;EAExB,OAAO,KAAKD,OAAO,SAAS,KAAK,KAAKC,SAAS,SAAS;CAC1D;CAEA,IAAI,aAAsB;EACxB,OAAO,CAAC,KAAKL,QAAQ,eAAe,KAAK;CAC3C;CAEA,IAAI,mBAA0C;EAC5C,OAAO,KAAKQ,KAAK,KAAK,CAAC,CAAC,MAAM,SAAS;GACrC,MAAM,MAAM,IAAI,IAAI,KAAK,KAAK;GAC9B,MAAM,OAAO,KAAK,QAAQ,MAAM,CAAC,IAAI,IAAI,CAAC,CAAC;GAC3C,KAAK,KAAK,GAAG,KAAK,OAAO;GACzB,OAAO,KAAK,KAAK,aAAa;EAChC,CAAC;CACH;CAKA,IAAIJ,SAAc;EAChB,OAAO,CACL,GAAG,KAAKI,KAAK,MAAM,GACnB,GAAI,KAAKR,QAAQ,cACb,CAAC,IACCG,sBAAAA,iBAAiB,KAAKH,QAAQ,EAAE,CAAC,CAAC,iBAAiB,IAAI,KAAKA,QAAQ,QAAQ,CAAC,EAAE,IAAI,KAAKQ,KAAK,SAAS,CAAC,EACrG,QAAgB,CAAC,CAC3B;CACF;CAEA,IAAIH,WAAgB;EAClB,OAAO,CACL,GAAG,KAAKG,KAAK,QAAQ,GACrB,GAAI,KAAKR,QAAQ,cACb,CAAC,IACCG,sBAAAA,iBAAiB,KAAKH,QAAQ,EAAE,CAAC,CAAC,iBAAiB,IAAI,KAAKA,QAAQ,QAAQ,CAAC,EAAE,IAAI,KAAKQ,KAAK,SAAS,CAAC,EACrG,WAAmB,CAAC,CAC9B;CACF;AACF;AAoDA,SAAgB,gBAAkC,QAAuB;CACvE,OAAO,IAAI,MAAM,QAAQ,EACvB,IACE,QACA,GAMsB;EACtB,IAAI,MAAM,UACR,OAAO,qBAAqB,QAAQ,IAAI;OACnC,IAAI,MAAM,0BACf,OAAO,qBAAqB,QAAQ,KAAK;OACpC,IAAI,OAAO,MAAM,UACtB,MAAM,IAAI,MAAM,uBAAuB,OAAO,CAAC,GAAG;EAEpD,MAAM,OAAOC,uBAAAA,YAAY,MAAM,CAAC,CAAC,UAAU,EAAE,EAAE;EAC/C,IAAI,SAAS,OACX,OAAO,IAAI,0BAA0B,QAAQ,CAAY;OACpD,IAAI,SAAS,WAClB,OAAO,IAAI,8BAA8B,QAAQ,CAAY;OACxD,IAAI,SAAS,OAClB,OAAO,IAAI,yBAAyB,QAAQ,CAAY;OACnD,IAAI,CAACC,eAAAA,kBAAkB,QAAQ,CAAQ,GAC5C,MAAM,IAAI,MAAM,yBAAyB,GAAG;OACvC,IAAI,SAAS,SAAS,SAAS,QACpC,OAAO,IAAI,yBAAyB,QAAQ,CAAC;OACxC,IAAI,SAAS,UAAU,SAAS,eAAe,SAAS,cAC7D,OAAO,IAAI,yBAAyB,QAAQ,CAAC;OACxC,IAAI,SAAS,UAAU,SAAS,OACrC,MAAM,IAAI,MAAM,iCAAiC,KAAK,GAAG,GAAG;OAE5D,OAAOC,cAAAA,YAAY,IAAI;CAE3B,EACF,CAAC;AACH;;AAgBA,SAAS,qBAAuC,QAAW,kBAAgD;CACzG,MAAM,gBAAgB,OAAO,cAEzB,OAAO,QAAQhB,mBAAAA,gBAAgB,MAAM,CAAC,CAAC,IAAI,CAAC,CACzC,QAAQ,GAAG,WAAW,UAAU,KAAA,CAAS,CAAC,CAC1C,KAAK,CAAC,SAAS,GAAG,IACrB,OAAO,KAAKA,mBAAAA,gBAAgB,MAAM,CAAC,CAAC,YAAY;CAGpD,IAAI,kBACG;OAAA,MAAM,SAAS,OAAO,OAAOc,uBAAAA,YAAY,MAAM,CAAC,CAAC,SAAS,GAC7D,IAAI,MAAM,SAAS,OAEb;OAAA,IADe,0BAA0B,QAAQ,MAAM,SAClD,CAAC,CAAC,YACT,cAAc,KAAK,MAAM,SAAS;EAAA,OAE/B,IAAI,MAAM,SAAS,OAEpB;OAAA,IADe,yBAAyB,QAAQ,MAAM,SACjD,CAAC,CAAC,YACT,cAAc,KAAK,MAAM,SAAS;EAAA,OAE/B,IAAI,MAAM,SAAS,WAEpB;OAAA,IADe,8BAA8B,QAAQ,MAAM,SACtD,CAAC,CAAC,YACT,cAAc,KAAK,MAAM,SAAS;EAAA;CACpC;CAKN,OAAO;AACT;AAEA,MAAM,iBAAmD,GAAG,MAAM;CAChE,OAAO,EAAE,SAAS,CAAC,CAAC,cAAc,EAAE,SAAS,CAAC;AAChD"}
|
package/build/changes.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { getField, isChangeableField } from "./fields.js";
|
|
2
|
-
import "./JoinRows.js";
|
|
3
1
|
import "./relations/EnumCollection.js";
|
|
2
|
+
import { getField, isChangeableField } from "./fields.js";
|
|
4
3
|
import { notLoadedValue } from "./relations/LazyField.js";
|
|
5
|
-
import "./typeMap.js";
|
|
6
4
|
import { assertNever } from "./utils.js";
|
|
5
|
+
import "./typeMap.js";
|
|
7
6
|
import { getMetadata } from "./EntityMetadata.js";
|
|
8
7
|
import "./index.js";
|
|
9
8
|
import { getEmInternalApi, isId } from "./EntityManager.js";
|