joist-core 2.3.0-next.4 → 2.3.0-next.40
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/AliasAssigner.d.ts +0 -1
- package/build/AliasAssigner.js.map +1 -1
- package/build/Aliases.d.ts +1 -2
- package/build/Aliases.js +2 -2
- package/build/Aliases.js.map +1 -1
- package/build/BaseEntity.d.ts +0 -1
- package/build/BaseEntity.js.map +1 -1
- package/build/ConditionBuilder.d.ts +0 -1
- package/build/ConditionBuilder.js +1 -1
- package/build/ConditionBuilder.js.map +1 -1
- package/build/Entity.d.ts +0 -1
- package/build/Entity.js.map +1 -1
- package/build/EntityFields.d.ts +0 -1
- package/build/EntityFilter.d.ts +0 -1
- package/build/EntityFilter.js.map +1 -1
- package/build/EntityGraphQLFilter.d.ts +0 -1
- package/build/EntityManager.d.ts +41 -7
- package/build/EntityManager.js +410 -143
- package/build/EntityManager.js.map +1 -1
- package/build/EntityMetadata.d.ts +22 -3
- package/build/EntityMetadata.js +5 -3
- package/build/EntityMetadata.js.map +1 -1
- package/build/EnumMetadata.d.ts +0 -1
- package/build/FlushLock.d.ts +0 -1
- package/build/FlushLock.js.map +1 -1
- package/build/HintTree.d.ts +0 -1
- package/build/HintTree.js.map +1 -1
- package/build/IndexManager.d.ts +4 -3
- package/build/IndexManager.js +4 -2
- package/build/IndexManager.js.map +1 -1
- package/build/InstanceData.d.ts +25 -10
- package/build/InstanceData.js +76 -28
- package/build/InstanceData.js.map +1 -1
- package/build/IsLoadedCache.d.ts +0 -1
- package/build/IsLoadedCache.js.map +1 -1
- package/build/JoinRows.d.ts +0 -1
- package/build/JoinRows.js.map +1 -1
- package/build/PendingChanges.d.ts +0 -1
- package/build/PluginManager.d.ts +13 -5
- package/build/PluginManager.js +4 -0
- package/build/PluginManager.js.map +1 -1
- package/build/QueryParser.collectionJoins.d.ts +0 -1
- package/build/QueryParser.collectionJoins.js.map +1 -1
- package/build/QueryParser.d.ts +7 -1
- package/build/QueryParser.js +44 -10
- package/build/QueryParser.js.map +1 -1
- package/build/QueryParser.pruning.d.ts +0 -1
- package/build/QueryParser.pruning.js.map +1 -1
- package/build/QueryVisitor.d.ts +0 -1
- package/build/QueryVisitor.js.map +1 -1
- package/build/ReactionsManager.d.ts +13 -4
- package/build/ReactionsManager.js +77 -50
- package/build/ReactionsManager.js.map +1 -1
- package/build/RowData.d.ts +49 -0
- package/build/RowData.js +37 -0
- package/build/RowData.js.map +1 -0
- package/build/Todo.d.ts +1 -2
- package/build/Todo.js +16 -5
- package/build/Todo.js.map +1 -1
- package/build/batchloaders/BatchLoader.d.ts +0 -1
- package/build/batchloaders/BatchLoader.js.map +1 -1
- package/build/batchloaders/enumCollectionBatchLoader.d.ts +0 -1
- package/build/batchloaders/enumCollectionBatchLoader.js.map +1 -1
- package/build/batchloaders/loadBatchLoader.d.ts +0 -1
- package/build/batchloaders/loadBatchLoader.js +21 -15
- package/build/batchloaders/loadBatchLoader.js.map +1 -1
- package/build/batchloaders/manyToManyBatchLoader.d.ts +0 -1
- package/build/batchloaders/manyToManyBatchLoader.js.map +1 -1
- package/build/batchloaders/oneToManyBatchLoader.d.ts +0 -1
- package/build/batchloaders/oneToManyBatchLoader.js +2 -2
- package/build/batchloaders/oneToManyBatchLoader.js.map +1 -1
- package/build/batchloaders/oneToOneBatchLoader.d.ts +0 -1
- package/build/batchloaders/oneToOneBatchLoader.js +2 -2
- package/build/batchloaders/oneToOneBatchLoader.js.map +1 -1
- package/build/batchloaders/populateBatchLoader.d.ts +0 -1
- package/build/batchloaders/populateBatchLoader.js +12 -22
- package/build/batchloaders/populateBatchLoader.js.map +1 -1
- package/build/batchloaders/recursiveChildrenBatchLoader.d.ts +0 -1
- package/build/batchloaders/recursiveChildrenBatchLoader.js +5 -3
- package/build/batchloaders/recursiveChildrenBatchLoader.js.map +1 -1
- package/build/batchloaders/recursiveM2mBatchLoader.d.ts +0 -1
- package/build/batchloaders/recursiveM2mBatchLoader.js +1 -1
- package/build/batchloaders/recursiveM2mBatchLoader.js.map +1 -1
- package/build/batchloaders/recursiveParentsBatchLoader.d.ts +0 -1
- package/build/batchloaders/recursiveParentsBatchLoader.js +3 -3
- package/build/batchloaders/recursiveParentsBatchLoader.js.map +1 -1
- package/build/changes.d.ts +1 -2
- package/build/changes.js +4 -1
- package/build/changes.js.map +1 -1
- package/build/config.d.ts +21 -3
- package/build/config.js +36 -23
- package/build/config.js.map +1 -1
- package/build/configure.d.ts +0 -1
- package/build/configure.js +67 -3
- package/build/configure.js.map +1 -1
- package/build/dataloaders/fastWhereFilterHash.d.ts +0 -1
- package/build/dataloaders/fastWhereFilterHash.js.map +1 -1
- package/build/dataloaders/findByUniqueDataLoader.d.ts +0 -1
- package/build/dataloaders/findByUniqueDataLoader.js.map +1 -1
- package/build/dataloaders/findCountDataLoader.d.ts +0 -1
- package/build/dataloaders/findCountDataLoader.js.map +1 -1
- package/build/dataloaders/findDataLoader.d.ts +2 -3
- package/build/dataloaders/findDataLoader.js +41 -38
- package/build/dataloaders/findDataLoader.js.map +1 -1
- package/build/dataloaders/findIdsDataLoader.d.ts +0 -1
- package/build/dataloaders/findIdsDataLoader.js.map +1 -1
- package/build/dataloaders/findOrCreateDataLoader.d.ts +0 -1
- package/build/dataloaders/findOrCreateDataLoader.js +2 -2
- package/build/dataloaders/findOrCreateDataLoader.js.map +1 -1
- package/build/dataloaders/findPaginatedDataLoader.d.ts +0 -1
- package/build/dataloaders/findPaginatedDataLoader.js.map +1 -1
- package/build/dataloaders/lensDataLoader.d.ts +0 -1
- package/build/dataloaders/lensDataLoader.js +1 -1
- package/build/dataloaders/lensDataLoader.js.map +1 -1
- package/build/dataloaders/manyToManyFindDataLoader.d.ts +0 -1
- package/build/dataloaders/manyToManyFindDataLoader.js.map +1 -1
- package/build/dataloaders/oneToManyFindDataLoader.d.ts +0 -1
- package/build/dataloaders/oneToManyFindDataLoader.js.map +1 -1
- package/build/defaults.d.ts +0 -1
- package/build/defaults.js.map +1 -1
- package/build/driverApi.d.ts +1 -2
- package/build/driverApi.js +1 -1
- package/build/driverApi.js.map +1 -1
- package/build/drivers/Driver.d.ts +13 -2
- package/build/drivers/EntityWriter.d.ts +0 -1
- package/build/drivers/EntityWriter.js +32 -3
- package/build/drivers/EntityWriter.js.map +1 -1
- package/build/drivers/IdAssigner.d.ts +0 -1
- package/build/drivers/IdAssigner.js.map +1 -1
- package/build/drivers/buildRawQuery.d.ts +0 -1
- package/build/drivers/buildRawQuery.js +1 -3
- package/build/drivers/buildRawQuery.js.map +1 -1
- package/build/drivers/buildUtils.d.ts +0 -1
- package/build/drivers/buildUtils.js.map +1 -1
- package/build/drivers/index.d.ts +0 -1
- package/build/fields.d.ts +0 -1
- package/build/fields.js +5 -4
- package/build/fields.js.map +1 -1
- package/build/getProperties.d.ts +3 -4
- package/build/getProperties.js +3 -3
- package/build/getProperties.js.map +1 -1
- package/build/index.d.ts +6 -6
- package/build/index.js +11 -4
- package/build/index.js.map +1 -1
- package/build/json.d.ts +0 -1
- package/build/json.js +2 -2
- package/build/json.js.map +1 -1
- package/build/k.d.ts +0 -1
- package/build/keys.d.ts +7 -7
- package/build/keys.js +102 -46
- package/build/keys.js.map +1 -1
- package/build/keywords.d.ts +0 -1
- package/build/keywords.js.map +1 -1
- package/build/loadHints.d.ts +0 -1
- package/build/loadHints.js.map +1 -1
- package/build/loadLens.d.ts +0 -1
- package/build/loadLens.js +1 -1
- package/build/loadLens.js.map +1 -1
- package/build/logging/FactoryLogger.d.ts +0 -1
- package/build/logging/FactoryLogger.js.map +1 -1
- package/build/logging/FieldLogger.d.ts +2 -1
- package/build/logging/FieldLogger.js +11 -2
- package/build/logging/FieldLogger.js.map +1 -1
- package/build/logging/ReactionLogger.d.ts +2 -1
- package/build/logging/ReactionLogger.js +15 -1
- package/build/logging/ReactionLogger.js.map +1 -1
- package/build/newEntity.d.ts +2 -3
- package/build/newEntity.js +10 -8
- package/build/newEntity.js.map +1 -1
- package/build/newTestInstance.d.ts +0 -1
- package/build/newTestInstance.js +5 -1
- package/build/newTestInstance.js.map +1 -1
- package/build/normalizeHints.d.ts +0 -1
- package/build/normalizeHints.js.map +1 -1
- package/build/plugins/ImmutableEntitiesPlugin.d.ts +0 -1
- package/build/plugins/ImmutableEntitiesPlugin.js.map +1 -1
- package/build/plugins/PreloadPlugin.d.ts +10 -6
- package/build/preloading/JsonAggregatePreloader.d.ts +0 -1
- package/build/preloading/JsonAggregatePreloader.js +14 -9
- package/build/preloading/JsonAggregatePreloader.js.map +1 -1
- package/build/preloading/canPreload.d.ts +0 -1
- package/build/preloading/canPreload.js.map +1 -1
- package/build/preloading/partitionHint.d.ts +0 -1
- package/build/preloading/partitionHint.js.map +1 -1
- package/build/reactiveHints.d.ts +2 -3
- package/build/reactiveHints.js.map +1 -1
- package/build/relations/AbstractPropertyImpl.d.ts +0 -1
- package/build/relations/AbstractPropertyImpl.js.map +1 -1
- package/build/relations/AbstractRelationImpl.d.ts +0 -1
- package/build/relations/AbstractRelationImpl.js.map +1 -1
- package/build/relations/AsyncProperty.d.ts +0 -1
- package/build/relations/AsyncProperty.js.map +1 -1
- package/build/relations/AsyncReactiveField.d.ts +13 -9
- package/build/relations/AsyncReactiveField.js +22 -19
- package/build/relations/AsyncReactiveField.js.map +1 -1
- package/build/relations/Collection.d.ts +0 -1
- package/build/relations/Collection.js.map +1 -1
- package/build/relations/CustomCollection.d.ts +0 -1
- package/build/relations/CustomCollection.js.map +1 -1
- package/build/relations/CustomReference.d.ts +0 -1
- package/build/relations/CustomReference.js.map +1 -1
- package/build/relations/EnumCollection.d.ts +0 -1
- package/build/relations/EnumCollection.js.map +1 -1
- package/build/relations/LargeCollection.d.ts +0 -1
- package/build/relations/LazyField.d.ts +46 -0
- package/build/relations/LazyField.js +135 -0
- package/build/relations/LazyField.js.map +1 -0
- package/build/relations/ManyToManyCollection.d.ts +0 -1
- package/build/relations/ManyToManyCollection.js +5 -3
- package/build/relations/ManyToManyCollection.js.map +1 -1
- package/build/relations/ManyToManyLargeCollection.d.ts +0 -1
- package/build/relations/ManyToManyLargeCollection.js.map +1 -1
- package/build/relations/ManyToOneReference.d.ts +0 -1
- package/build/relations/ManyToOneReference.js.map +1 -1
- package/build/relations/OneToManyCollection.d.ts +0 -1
- package/build/relations/OneToManyCollection.js +3 -1
- package/build/relations/OneToManyCollection.js.map +1 -1
- package/build/relations/OneToManyLargeCollection.d.ts +0 -1
- package/build/relations/OneToManyLargeCollection.js.map +1 -1
- package/build/relations/OneToOneReference.d.ts +0 -1
- package/build/relations/OneToOneReference.js +1 -1
- package/build/relations/OneToOneReference.js.map +1 -1
- package/build/relations/PolymorphicReference.d.ts +0 -1
- package/build/relations/PolymorphicReference.js +1 -1
- package/build/relations/PolymorphicReference.js.map +1 -1
- package/build/relations/ReactiveField.d.ts +0 -1
- package/build/relations/ReactiveField.js.map +1 -1
- package/build/relations/ReactiveGetter.d.ts +0 -1
- package/build/relations/ReactiveGetter.js.map +1 -1
- package/build/relations/ReactiveManyToMany.d.ts +0 -1
- package/build/relations/ReactiveManyToMany.js +1 -1
- package/build/relations/ReactiveManyToMany.js.map +1 -1
- package/build/relations/ReactiveManyToManyOtherSide.d.ts +0 -1
- package/build/relations/ReactiveManyToManyOtherSide.js +1 -1
- package/build/relations/ReactiveManyToManyOtherSide.js.map +1 -1
- package/build/relations/ReactiveReference.d.ts +0 -1
- package/build/relations/ReactiveReference.js +1 -1
- package/build/relations/ReactiveReference.js.map +1 -1
- package/build/relations/ReadOnlyCollection.d.ts +0 -1
- package/build/relations/ReadOnlyCollection.js.map +1 -1
- package/build/relations/RecursiveCollection.d.ts +0 -1
- package/build/relations/RecursiveCollection.js.map +1 -1
- package/build/relations/RecursiveCycleError.d.ts +0 -1
- package/build/relations/RecursiveCycleError.js.map +1 -1
- package/build/relations/Reference.d.ts +0 -1
- package/build/relations/Reference.js.map +1 -1
- package/build/relations/Relation.d.ts +0 -1
- package/build/relations/Relation.js.map +1 -1
- package/build/relations/hasAsyncMethod.d.ts +0 -1
- package/build/relations/hasAsyncMethod.js.map +1 -1
- package/build/relations/hasManyDerived.d.ts +0 -1
- package/build/relations/hasManyDerived.js.map +1 -1
- package/build/relations/hasManyThrough.d.ts +0 -1
- package/build/relations/hasManyThrough.js.map +1 -1
- package/build/relations/hasOneDerived.d.ts +0 -1
- package/build/relations/hasOneDerived.js.map +1 -1
- package/build/relations/hasOneThrough.d.ts +0 -1
- package/build/relations/hasOneThrough.js.map +1 -1
- package/build/relations/hasProperty.d.ts +0 -1
- package/build/relations/hasProperty.js.map +1 -1
- package/build/relations/index.d.ts +1 -1
- package/build/relations/index.js +7 -2
- package/build/relations/index.js.map +1 -1
- package/build/resurrection.d.ts +0 -1
- package/build/resurrection.js.map +1 -1
- package/build/rules.d.ts +8 -1
- package/build/rules.js +17 -0
- package/build/rules.js.map +1 -1
- package/build/runtimeConfig.d.ts +4 -1
- package/build/runtimeConfig.js +5 -0
- package/build/runtimeConfig.js.map +1 -1
- package/build/scopes.d.ts +1 -2
- package/build/scopes.js.map +1 -1
- package/build/serde.d.ts +29 -17
- package/build/serde.js +46 -33
- package/build/serde.js.map +1 -1
- package/build/temporal.d.ts +0 -1
- package/build/temporal.js.map +1 -1
- package/build/temporalMappers.d.ts +11 -4
- package/build/temporalMappers.js +22 -11
- package/build/temporalMappers.js.map +1 -1
- package/build/trusted.d.ts +0 -1
- package/build/trusted.js.map +1 -1
- package/build/typeMap.d.ts +0 -1
- package/build/unnest.d.ts +0 -1
- package/build/unnest.js.map +1 -1
- package/build/upsert.d.ts +0 -1
- package/build/upsert.js.map +1 -1
- package/build/utils.d.ts +2 -1
- package/build/utils.js +7 -0
- package/build/utils.js.map +1 -1
- package/build/withLoaded.d.ts +1 -2
- package/build/withLoaded.js.map +1 -1
- package/package.json +6 -10
- package/build/AliasAssigner.d.ts.map +0 -1
- package/build/Aliases.d.ts.map +0 -1
- package/build/BaseEntity.d.ts.map +0 -1
- package/build/ConditionBuilder.d.ts.map +0 -1
- package/build/Entity.d.ts.map +0 -1
- package/build/EntityFields.d.ts.map +0 -1
- package/build/EntityFilter.d.ts.map +0 -1
- package/build/EntityGraphQLFilter.d.ts.map +0 -1
- package/build/EntityManager.d.ts.map +0 -1
- package/build/EntityMetadata.d.ts.map +0 -1
- package/build/EnumMetadata.d.ts.map +0 -1
- package/build/FlushLock.d.ts.map +0 -1
- package/build/HintTree.d.ts.map +0 -1
- package/build/IndexManager.d.ts.map +0 -1
- package/build/InstanceData.d.ts.map +0 -1
- package/build/IsLoadedCache.d.ts.map +0 -1
- package/build/JoinRows.d.ts.map +0 -1
- package/build/PendingChanges.d.ts.map +0 -1
- package/build/PluginManager.d.ts.map +0 -1
- package/build/QueryParser.collectionJoins.d.ts.map +0 -1
- package/build/QueryParser.collectionJoins.test.d.ts +0 -2
- package/build/QueryParser.collectionJoins.test.d.ts.map +0 -1
- package/build/QueryParser.collectionJoins.test.js +0 -772
- package/build/QueryParser.collectionJoins.test.js.map +0 -1
- package/build/QueryParser.d.ts.map +0 -1
- package/build/QueryParser.pruning.d.ts.map +0 -1
- package/build/QueryParser.pruning.test.d.ts +0 -2
- package/build/QueryParser.pruning.test.d.ts.map +0 -1
- package/build/QueryParser.pruning.test.js +0 -106
- package/build/QueryParser.pruning.test.js.map +0 -1
- package/build/QueryParser.test.d.ts +0 -2
- package/build/QueryParser.test.d.ts.map +0 -1
- package/build/QueryParser.test.js +0 -27
- package/build/QueryParser.test.js.map +0 -1
- package/build/QueryVisitor.d.ts.map +0 -1
- package/build/ReactionsManager.d.ts.map +0 -1
- package/build/Todo.d.ts.map +0 -1
- package/build/batchloaders/BatchLoader.d.ts.map +0 -1
- package/build/batchloaders/enumCollectionBatchLoader.d.ts.map +0 -1
- package/build/batchloaders/loadBatchLoader.d.ts.map +0 -1
- package/build/batchloaders/manyToManyBatchLoader.d.ts.map +0 -1
- package/build/batchloaders/oneToManyBatchLoader.d.ts.map +0 -1
- package/build/batchloaders/oneToOneBatchLoader.d.ts.map +0 -1
- package/build/batchloaders/populateBatchLoader.d.ts.map +0 -1
- package/build/batchloaders/recursiveChildrenBatchLoader.d.ts.map +0 -1
- package/build/batchloaders/recursiveM2mBatchLoader.d.ts.map +0 -1
- package/build/batchloaders/recursiveParentsBatchLoader.d.ts.map +0 -1
- package/build/changes.d.ts.map +0 -1
- package/build/config.d.ts.map +0 -1
- package/build/config.test.d.ts +0 -2
- package/build/config.test.d.ts.map +0 -1
- package/build/config.test.js +0 -53
- package/build/config.test.js.map +0 -1
- package/build/configure.d.ts.map +0 -1
- package/build/dataloaders/fastWhereFilterHash.d.ts.map +0 -1
- package/build/dataloaders/fastWhereFilterHash.test.d.ts +0 -2
- package/build/dataloaders/fastWhereFilterHash.test.d.ts.map +0 -1
- package/build/dataloaders/fastWhereFilterHash.test.js +0 -59
- package/build/dataloaders/fastWhereFilterHash.test.js.map +0 -1
- package/build/dataloaders/findByUniqueDataLoader.d.ts.map +0 -1
- package/build/dataloaders/findCountDataLoader.d.ts.map +0 -1
- package/build/dataloaders/findDataLoader.d.ts.map +0 -1
- package/build/dataloaders/findIdsDataLoader.d.ts.map +0 -1
- package/build/dataloaders/findOrCreateDataLoader.d.ts.map +0 -1
- package/build/dataloaders/findPaginatedDataLoader.d.ts.map +0 -1
- package/build/dataloaders/lensDataLoader.d.ts.map +0 -1
- package/build/dataloaders/manyToManyFindDataLoader.d.ts.map +0 -1
- package/build/dataloaders/oneToManyFindDataLoader.d.ts.map +0 -1
- package/build/defaults.d.ts.map +0 -1
- package/build/driverApi.d.ts.map +0 -1
- package/build/drivers/Driver.d.ts.map +0 -1
- package/build/drivers/EntityWriter.d.ts.map +0 -1
- package/build/drivers/IdAssigner.d.ts.map +0 -1
- package/build/drivers/buildRawQuery.d.ts.map +0 -1
- package/build/drivers/buildUtils.d.ts.map +0 -1
- package/build/drivers/index.d.ts.map +0 -1
- package/build/fields.d.ts.map +0 -1
- package/build/getProperties.d.ts.map +0 -1
- package/build/index.d.ts.map +0 -1
- package/build/json.d.ts.map +0 -1
- package/build/k.d.ts.map +0 -1
- package/build/keys.d.ts.map +0 -1
- package/build/keywords.d.ts.map +0 -1
- package/build/loadHints.d.ts.map +0 -1
- package/build/loadLens.d.ts.map +0 -1
- package/build/logging/FactoryLogger.d.ts.map +0 -1
- package/build/logging/FieldLogger.d.ts.map +0 -1
- package/build/logging/ReactionLogger.d.ts.map +0 -1
- package/build/newEntity.d.ts.map +0 -1
- package/build/newTestInstance.d.ts.map +0 -1
- package/build/normalizeHints.d.ts.map +0 -1
- package/build/plugins/ImmutableEntitiesPlugin.d.ts.map +0 -1
- package/build/plugins/PreloadPlugin.d.ts.map +0 -1
- package/build/preloading/JsonAggregatePreloader.d.ts.map +0 -1
- package/build/preloading/canPreload.d.ts.map +0 -1
- package/build/preloading/partitionHint.d.ts.map +0 -1
- package/build/reactiveHints.d.ts.map +0 -1
- package/build/relations/AbstractPropertyImpl.d.ts.map +0 -1
- package/build/relations/AbstractRelationImpl.d.ts.map +0 -1
- package/build/relations/AsyncProperty.d.ts.map +0 -1
- package/build/relations/AsyncReactiveField.d.ts.map +0 -1
- package/build/relations/Collection.d.ts.map +0 -1
- package/build/relations/CustomCollection.d.ts.map +0 -1
- package/build/relations/CustomReference.d.ts.map +0 -1
- package/build/relations/EnumCollection.d.ts.map +0 -1
- package/build/relations/LargeCollection.d.ts.map +0 -1
- package/build/relations/ManyToManyCollection.d.ts.map +0 -1
- package/build/relations/ManyToManyLargeCollection.d.ts.map +0 -1
- package/build/relations/ManyToOneReference.d.ts.map +0 -1
- package/build/relations/OneToManyCollection.d.ts.map +0 -1
- package/build/relations/OneToManyLargeCollection.d.ts.map +0 -1
- package/build/relations/OneToOneReference.d.ts.map +0 -1
- package/build/relations/PolymorphicReference.d.ts.map +0 -1
- package/build/relations/ReactiveField.d.ts.map +0 -1
- package/build/relations/ReactiveGetter.d.ts.map +0 -1
- package/build/relations/ReactiveManyToMany.d.ts.map +0 -1
- package/build/relations/ReactiveManyToManyOtherSide.d.ts.map +0 -1
- package/build/relations/ReactiveReference.d.ts.map +0 -1
- package/build/relations/ReadOnlyCollection.d.ts.map +0 -1
- package/build/relations/RecursiveCollection.d.ts.map +0 -1
- package/build/relations/RecursiveCycleError.d.ts.map +0 -1
- package/build/relations/Reference.d.ts.map +0 -1
- package/build/relations/Relation.d.ts.map +0 -1
- package/build/relations/hasAsyncMethod.d.ts.map +0 -1
- package/build/relations/hasManyDerived.d.ts.map +0 -1
- package/build/relations/hasManyThrough.d.ts.map +0 -1
- package/build/relations/hasOneDerived.d.ts.map +0 -1
- package/build/relations/hasOneThrough.d.ts.map +0 -1
- package/build/relations/hasProperty.d.ts.map +0 -1
- package/build/relations/index.d.ts.map +0 -1
- package/build/resurrection.d.ts.map +0 -1
- package/build/rules.d.ts.map +0 -1
- package/build/runtimeConfig.d.ts.map +0 -1
- package/build/scopes.d.ts.map +0 -1
- package/build/serde.d.ts.map +0 -1
- package/build/temporal.d.ts.map +0 -1
- package/build/temporalMappers.d.ts.map +0 -1
- package/build/trusted.d.ts.map +0 -1
- package/build/typeMap.d.ts.map +0 -1
- package/build/unnest.d.ts.map +0 -1
- package/build/upsert.d.ts.map +0 -1
- package/build/utils.d.ts.map +0 -1
- package/build/withLoaded.d.ts.map +0 -1
package/build/EntityManager.js
CHANGED
|
@@ -23,10 +23,6 @@ exports.createRowFromEntityData = createRowFromEntityData;
|
|
|
23
23
|
const dataloader_1 = __importDefault(require("dataloader"));
|
|
24
24
|
const BaseEntity_1 = require("./BaseEntity");
|
|
25
25
|
const BatchLoader_1 = require("./batchloaders/BatchLoader");
|
|
26
|
-
const defaults_1 = require("./defaults");
|
|
27
|
-
const fields_1 = require("./fields");
|
|
28
|
-
const IndexManager_1 = require("./IndexManager");
|
|
29
|
-
// We alias `Entity => EntityW` to denote "Entity wide" i.e. the non-narrowed Entity
|
|
30
26
|
const loadBatchLoader_1 = require("./batchloaders/loadBatchLoader");
|
|
31
27
|
const populateBatchLoader_1 = require("./batchloaders/populateBatchLoader");
|
|
32
28
|
const config_1 = require("./config");
|
|
@@ -37,12 +33,17 @@ const findDataLoader_1 = require("./dataloaders/findDataLoader");
|
|
|
37
33
|
const findIdsDataLoader_1 = require("./dataloaders/findIdsDataLoader");
|
|
38
34
|
const findOrCreateDataLoader_1 = require("./dataloaders/findOrCreateDataLoader");
|
|
39
35
|
const findPaginatedDataLoader_1 = require("./dataloaders/findPaginatedDataLoader");
|
|
36
|
+
const defaults_1 = require("./defaults");
|
|
37
|
+
// We alias `Entity => EntityW` to denote "Entity wide" i.e. the non-narrowed Entity
|
|
40
38
|
const Entity_1 = require("./Entity");
|
|
39
|
+
const fields_1 = require("./fields");
|
|
41
40
|
const FlushLock_1 = require("./FlushLock");
|
|
42
41
|
const index_1 = require("./index");
|
|
42
|
+
const IndexManager_1 = require("./IndexManager");
|
|
43
43
|
const IsLoadedCache_1 = require("./IsLoadedCache");
|
|
44
44
|
const JoinRows_1 = require("./JoinRows");
|
|
45
45
|
const loadHints_1 = require("./loadHints");
|
|
46
|
+
const FieldLogger_1 = require("./logging/FieldLogger");
|
|
46
47
|
const newEntity_1 = require("./newEntity");
|
|
47
48
|
const newTestInstance_1 = require("./newTestInstance");
|
|
48
49
|
const PluginManager_1 = require("./PluginManager");
|
|
@@ -51,7 +52,9 @@ const reactiveHints_1 = require("./reactiveHints");
|
|
|
51
52
|
const relations_1 = require("./relations");
|
|
52
53
|
const AsyncProperty_1 = require("./relations/AsyncProperty");
|
|
53
54
|
const hasAsyncMethod_1 = require("./relations/hasAsyncMethod");
|
|
55
|
+
const LazyField_1 = require("./relations/LazyField");
|
|
54
56
|
const RecursiveCollection_1 = require("./relations/RecursiveCollection");
|
|
57
|
+
const RowData_1 = require("./RowData");
|
|
55
58
|
const scopes_1 = require("./scopes");
|
|
56
59
|
const Todo_1 = require("./Todo");
|
|
57
60
|
const trusted_1 = require("./trusted");
|
|
@@ -98,6 +101,8 @@ class EntityManager {
|
|
|
98
101
|
// Provides field-based indexing for entity types with >1000 entities to optimize findWithNewOrChanged
|
|
99
102
|
#indexManager = new IndexManager_1.IndexManager();
|
|
100
103
|
#isValidating = false;
|
|
104
|
+
// Set while regular (pre-flush) validation rules run, so `em.find*` can fail fast (see #assertFindAllowed)
|
|
105
|
+
#findRestricted = false;
|
|
101
106
|
#pendingPercolate = new Map();
|
|
102
107
|
#preloadedRelations = new Map();
|
|
103
108
|
/**
|
|
@@ -124,7 +129,7 @@ class EntityManager {
|
|
|
124
129
|
afterCommit: [],
|
|
125
130
|
};
|
|
126
131
|
#preloader;
|
|
127
|
-
#fieldLogger;
|
|
132
|
+
#fieldLogger = FieldLogger_1.noopFieldLogger;
|
|
128
133
|
#isLoadedCache = new IsLoadedCache_1.IsLoadedCache();
|
|
129
134
|
#merging;
|
|
130
135
|
/** Track `a#1`, `a#2`, etc indexes for `em.create`-d entities. */
|
|
@@ -265,6 +270,7 @@ class EntityManager {
|
|
|
265
270
|
return this.#entitiesById.get(id);
|
|
266
271
|
}
|
|
267
272
|
async find(type, where, options) {
|
|
273
|
+
this.#assertFindAllowed("find");
|
|
268
274
|
const { populate, ...rest } = options || {};
|
|
269
275
|
const normalized = (0, index_1.mergeFindOptions)(where, rest);
|
|
270
276
|
const settings = { where: normalized.where, ...normalized.options };
|
|
@@ -278,11 +284,24 @@ class EntityManager {
|
|
|
278
284
|
}
|
|
279
285
|
return result;
|
|
280
286
|
}
|
|
287
|
+
/** Fails fast if `em.${method}` is called from a regular validation rule, i.e. steers to `config.addCommitRule`. */
|
|
288
|
+
#assertFindAllowed(method) {
|
|
289
|
+
if (this.#findRestricted) {
|
|
290
|
+
throw new Error(`em.${method} cannot be called from a validation rule (added via config.addRule), because rules run ` +
|
|
291
|
+
`before the flush and would query stale, pre-flush data. Use config.addCommitRule instead, which runs ` +
|
|
292
|
+
`after the INSERT/UPDATE/DELETEs are flushed (within the same transaction) and so sees the changed state.`);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
281
295
|
/** Runs the post-parse find pipeline: plugins mutate the logical AST, then Joist optimizes/prunes before SQL. */
|
|
282
296
|
async executeFind(meta, operation, parsed, settings) {
|
|
283
297
|
const { checkLimit, findSettings } = this.prepareFind(meta, operation, parsed, settings);
|
|
284
298
|
return this.executePreparedFind(meta, operation, parsed, findSettings, checkLimit);
|
|
285
299
|
}
|
|
300
|
+
/** Like {@link executeFind}, but returns a {@link RowData}; see {@link executePreparedFindRowData}. */
|
|
301
|
+
async executeFindRowData(meta, operation, parsed, settings) {
|
|
302
|
+
const { checkLimit, findSettings } = this.prepareFind(meta, operation, parsed, settings);
|
|
303
|
+
return this.executePreparedFindRowData(meta, operation, parsed, findSettings, checkLimit);
|
|
304
|
+
}
|
|
286
305
|
/** Executes a query that has already had find hooks and optimizations applied. */
|
|
287
306
|
async executePreparedFind(meta, operation, parsed, findSettings, checkLimit) {
|
|
288
307
|
const { pluginManager } = getEmInternalApi(this);
|
|
@@ -292,9 +311,33 @@ class EntityManager {
|
|
|
292
311
|
if (shouldCheck && rows.length >= this.entityLimit) {
|
|
293
312
|
throw new Error(`Query returned more than ${this.entityLimit} entityLimit rows`);
|
|
294
313
|
}
|
|
295
|
-
pluginManager.afterFind
|
|
314
|
+
if (pluginManager.hasHook("afterFind")) {
|
|
315
|
+
pluginManager.afterFind(meta, operation, new RowData_1.PojoRowData(rows));
|
|
316
|
+
}
|
|
296
317
|
return rows;
|
|
297
318
|
}
|
|
319
|
+
/**
|
|
320
|
+
* Like {@link executePreparedFind}, but returns a {@link RowData}: lazy wire rows when the
|
|
321
|
+
* driver has `lazyRows` enabled, else classic POJO rows wrapped in a `PojoRowData` — so
|
|
322
|
+
* loaders can hydrate from one shape without caring which the driver provides.
|
|
323
|
+
*/
|
|
324
|
+
async executePreparedFindRowData(meta, operation, parsed, findSettings, checkLimit) {
|
|
325
|
+
const { executeFindRowData } = this.driver;
|
|
326
|
+
if (executeFindRowData === undefined) {
|
|
327
|
+
return new RowData_1.PojoRowData(await this.executePreparedFind(meta, operation, parsed, findSettings, checkLimit));
|
|
328
|
+
}
|
|
329
|
+
const { pluginManager } = getEmInternalApi(this);
|
|
330
|
+
const rowData = await executeFindRowData.call(this.driver, this, parsed, findSettings);
|
|
331
|
+
// Check by default unless explicitly disabled or the caller removed the LIMIT via `limit: undefined`
|
|
332
|
+
const shouldCheck = checkLimit ?? !("limit" in findSettings && findSettings.limit === undefined);
|
|
333
|
+
if (shouldCheck && rowData.rowCount >= this.entityLimit) {
|
|
334
|
+
throw new Error(`Query returned more than ${this.entityLimit} entityLimit rows`);
|
|
335
|
+
}
|
|
336
|
+
// The hook receives the lazy RowData view directly, so observe-only hooks (i.e. metrics
|
|
337
|
+
// reading `rowCount`) never materialize cells; hooks call `toRows()` if they want POJOs
|
|
338
|
+
pluginManager.afterFind(meta, operation, rowData);
|
|
339
|
+
return rowData;
|
|
340
|
+
}
|
|
298
341
|
/**
|
|
299
342
|
* Runs pre-SQL find hooks and optimizations against a parsed query.
|
|
300
343
|
*
|
|
@@ -348,6 +391,7 @@ class EntityManager {
|
|
|
348
391
|
return list[0];
|
|
349
392
|
}
|
|
350
393
|
async findByUnique(type, where, options = {}) {
|
|
394
|
+
this.#assertFindAllowed("findByUnique");
|
|
351
395
|
const { populate, softDeletes = "exclude" } = options;
|
|
352
396
|
const entries = Object.entries(where);
|
|
353
397
|
if (entries.length !== 1) {
|
|
@@ -383,6 +427,7 @@ class EntityManager {
|
|
|
383
427
|
* Note: this method is not currently auto-batched, so it will cause N+1s if called in a loop.
|
|
384
428
|
*/
|
|
385
429
|
async findCount(type, where, options = {}) {
|
|
430
|
+
this.#assertFindAllowed("findCount");
|
|
386
431
|
const normalized = (0, findCountDataLoader_1.mergeCountOptions)(where, options);
|
|
387
432
|
const settings = { where: normalized.where, ...normalized.options };
|
|
388
433
|
let count = await (0, findCountDataLoader_1.findCountDataLoader)(this, type, settings).catch(function findCount(err) {
|
|
@@ -416,6 +461,7 @@ class EntityManager {
|
|
|
416
461
|
* as `em.find`.
|
|
417
462
|
*/
|
|
418
463
|
async findIds(type, where, options = {}) {
|
|
464
|
+
this.#assertFindAllowed("findIds");
|
|
419
465
|
const normalized = (0, findCountDataLoader_1.mergeCountOptions)(where, options);
|
|
420
466
|
const settings = { where: normalized.where, ...normalized.options };
|
|
421
467
|
return (0, findIdsDataLoader_1.findIdsDataLoader)(this, type, settings).catch(function findIds(err) {
|
|
@@ -709,14 +755,13 @@ class EntityManager {
|
|
|
709
755
|
}
|
|
710
756
|
async loadAll(type, _ids, hint) {
|
|
711
757
|
const meta = (0, index_1.getMetadata)(type);
|
|
712
|
-
// Use pre-allocated arrays/for loops instead of `.filter`s since this can be a hot spot
|
|
713
|
-
|
|
758
|
+
// Use pre-allocated arrays/for loops instead of `.filter`s since this can be a hot spot;
|
|
759
|
+
// misses remember their tagged id + position so the all-hit path skips the `ids` copy entirely
|
|
714
760
|
const entities = new Array(_ids.length);
|
|
715
761
|
let idsToLoad;
|
|
716
762
|
let positionsToLoad;
|
|
717
763
|
for (let i = 0; i < _ids.length; i++) {
|
|
718
764
|
const id = (0, index_1.tagId)(meta, _ids[i]);
|
|
719
|
-
ids[i] = id;
|
|
720
765
|
const entity = this.findExistingInstance(id);
|
|
721
766
|
if (entity) {
|
|
722
767
|
entities[i] = entity;
|
|
@@ -726,25 +771,23 @@ class EntityManager {
|
|
|
726
771
|
(positionsToLoad ??= []).push(i);
|
|
727
772
|
}
|
|
728
773
|
}
|
|
729
|
-
if (idsToLoad
|
|
774
|
+
if (idsToLoad) {
|
|
730
775
|
await (0, loadBatchLoader_1.loadBatchLoader)(this, meta)
|
|
731
776
|
.loadAll(idsToLoad.map((id) => ({ taggedId: id, hint })))
|
|
732
777
|
.catch(function loadAll(err) {
|
|
733
778
|
throw appendStack(err, new Error());
|
|
734
779
|
});
|
|
735
|
-
|
|
736
|
-
|
|
780
|
+
let idsNotFound;
|
|
781
|
+
for (let j = 0; j < positionsToLoad.length; j++) {
|
|
782
|
+
const entity = this.findExistingInstance(idsToLoad[j]);
|
|
783
|
+
entities[positionsToLoad[j]] = entity;
|
|
784
|
+
if (entity === undefined)
|
|
785
|
+
(idsNotFound ??= []).push(idsToLoad[j]);
|
|
737
786
|
}
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
for (let i = 0; i < entities.length; i++) {
|
|
741
|
-
if (entities[i] === undefined) {
|
|
742
|
-
(idsNotFound ??= []).push(ids[i]);
|
|
787
|
+
if (idsNotFound) {
|
|
788
|
+
throw new NotFoundError(`${idsNotFound.join(",")} were not found`);
|
|
743
789
|
}
|
|
744
790
|
}
|
|
745
|
-
if (idsNotFound) {
|
|
746
|
-
throw new NotFoundError(`${idsNotFound.join(",")} were not found`);
|
|
747
|
-
}
|
|
748
791
|
const loadedEntities = entities;
|
|
749
792
|
if (hint) {
|
|
750
793
|
await this.populate(loadedEntities, hint);
|
|
@@ -759,36 +802,43 @@ class EntityManager {
|
|
|
759
802
|
}
|
|
760
803
|
async loadAllIfExists(type, _ids, hint) {
|
|
761
804
|
const meta = (0, index_1.getMetadata)(type);
|
|
762
|
-
// Use pre-allocated arrays/for loops instead of `.filter`s since this can be a hot spot
|
|
763
|
-
|
|
764
|
-
const
|
|
805
|
+
// Use pre-allocated arrays/for loops instead of `.filter`s since this can be a hot spot;
|
|
806
|
+
// the all-hit path returns the single pre-sized array without any id copies or rebuilds
|
|
807
|
+
const maybeEntities = new Array(_ids.length);
|
|
765
808
|
let idsToLoad;
|
|
809
|
+
let positionsToLoad;
|
|
766
810
|
// Ensure the ids are tagged, and find any not-yet-loaded
|
|
767
811
|
for (let i = 0; i < _ids.length; i++) {
|
|
768
812
|
const id = (0, index_1.tagId)(meta, _ids[i]);
|
|
769
|
-
ids[i] = id;
|
|
770
813
|
const entity = this.findExistingInstance(id);
|
|
771
814
|
if (entity) {
|
|
772
|
-
|
|
815
|
+
maybeEntities[i] = entity;
|
|
773
816
|
}
|
|
774
817
|
else {
|
|
775
818
|
(idsToLoad ??= []).push(id);
|
|
819
|
+
(positionsToLoad ??= []).push(i);
|
|
776
820
|
}
|
|
777
821
|
}
|
|
778
|
-
|
|
822
|
+
let entities;
|
|
823
|
+
if (idsToLoad) {
|
|
779
824
|
await (0, loadBatchLoader_1.loadBatchLoader)(this, meta)
|
|
780
825
|
.loadAll(idsToLoad.map((id) => ({ taggedId: id, hint })))
|
|
781
826
|
.catch(function loadAllIfExists(err) {
|
|
782
827
|
throw appendStack(err, new Error());
|
|
783
828
|
});
|
|
784
|
-
//
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
829
|
+
// Fill in whichever missed ids actually exist, then compact out the not-founds
|
|
830
|
+
for (let j = 0; j < positionsToLoad.length; j++) {
|
|
831
|
+
maybeEntities[positionsToLoad[j]] = this.findExistingInstance(idsToLoad[j]);
|
|
832
|
+
}
|
|
833
|
+
entities = [];
|
|
834
|
+
for (const entity of maybeEntities) {
|
|
835
|
+
if (entity !== undefined)
|
|
789
836
|
entities.push(entity);
|
|
790
837
|
}
|
|
791
838
|
}
|
|
839
|
+
else {
|
|
840
|
+
entities = maybeEntities;
|
|
841
|
+
}
|
|
792
842
|
if (hint) {
|
|
793
843
|
await this.populate(entities, hint);
|
|
794
844
|
}
|
|
@@ -996,12 +1046,12 @@ class EntityManager {
|
|
|
996
1046
|
// each time, so that for an INSERT-then-UPDATE the triggers don't think the
|
|
997
1047
|
// UPDATE forgot to self-bump updatedAt, and then "helpfully" bump it for us.
|
|
998
1048
|
if (alreadyRanHooks.size > 0) {
|
|
999
|
-
maybeBumpUpdatedAt(this.#rm, (0, Todo_1.createTodos)(
|
|
1049
|
+
maybeBumpUpdatedAt(this.#rm, (0, Todo_1.createTodos)(alreadyRanHooks), now);
|
|
1000
1050
|
}
|
|
1001
1051
|
// Run hooks in a series of loops until things "settle down"
|
|
1002
1052
|
while (pendingHooks.size > 0) {
|
|
1003
1053
|
await this.#fl.allowWrites(async () => {
|
|
1004
|
-
let todos = (0, Todo_1.createTodos)(
|
|
1054
|
+
let todos = (0, Todo_1.createTodos)(pendingHooks);
|
|
1005
1055
|
await (0, defaults_1.setAsyncDefaults)(suppressedDefaultTypeErrors, this.ctx, Todo_1.Todo.groupInsertsByTypeAndSubType(todos));
|
|
1006
1056
|
maybeBumpUpdatedAt(this.#rm, todos, now);
|
|
1007
1057
|
// Run our hooks
|
|
@@ -1059,16 +1109,25 @@ class EntityManager {
|
|
|
1059
1109
|
this.#isValidating = true;
|
|
1060
1110
|
await pluginManager.beforeValidate(changedEntities);
|
|
1061
1111
|
if (validate) {
|
|
1062
|
-
//
|
|
1063
|
-
// `
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1112
|
+
// Regular rules run pre-flush, so `em.find*` would query stale data; block it and steer
|
|
1113
|
+
// users to `config.addCommitRule` (which runs post-flush). Only rules are restricted —
|
|
1114
|
+
// `afterValidation` hooks & plugins below are left free to query.
|
|
1115
|
+
this.#findRestricted = true;
|
|
1116
|
+
try {
|
|
1117
|
+
// Run simple rules first b/c it includes not-null/required rules, so that then when we run
|
|
1118
|
+
// `validateReactiveRules` next, the app's lambdas won't see fundamentally invalid entities & NPE.
|
|
1119
|
+
await validateSimpleRules(entityTodos);
|
|
1120
|
+
// After we've let any "author is not set" simple rules fail before prematurely throwing
|
|
1121
|
+
// the "of course that caused an NPE" `TypeError`s, if all the authors *were* valid/set,
|
|
1122
|
+
// and we still have TypeErrors (from derived valeus), they were real, unrelated errors
|
|
1123
|
+
// that the user should see.
|
|
1124
|
+
if (suppressedDefaultTypeErrors.length > 0)
|
|
1125
|
+
throw suppressedDefaultTypeErrors[0];
|
|
1126
|
+
await validateReactiveRules(this, this.#rm.logger, entityTodos, joinRowTodos);
|
|
1127
|
+
}
|
|
1128
|
+
finally {
|
|
1129
|
+
this.#findRestricted = false;
|
|
1130
|
+
}
|
|
1072
1131
|
await afterValidation(this.ctx, entityTodos);
|
|
1073
1132
|
}
|
|
1074
1133
|
await pluginManager.afterValidate(changedEntities);
|
|
@@ -1077,6 +1136,61 @@ class EntityManager {
|
|
|
1077
1136
|
this.#isValidating = false;
|
|
1078
1137
|
}
|
|
1079
1138
|
};
|
|
1139
|
+
// Runs `addCommitRule`s, which look exactly like regular validation rules but fire here,
|
|
1140
|
+
// after the INSERT/UPDATE/DELETEs have hit the db (but before COMMIT), so that any
|
|
1141
|
+
// `em.find`s they make query the just-flushed/changed state within the transaction.
|
|
1142
|
+
const runCommitValidation = async (entityTodos, joinRowTodos) => {
|
|
1143
|
+
// Drop the pre-flush find/query caches so commit rules' `em.find`s re-query and see the
|
|
1144
|
+
// just-flushed rows, instead of stale results cached earlier in this same flush.
|
|
1145
|
+
this.#dataloaders = {};
|
|
1146
|
+
this.#batchLoaders = {};
|
|
1147
|
+
this.#preloadedRelations = new Map();
|
|
1148
|
+
try {
|
|
1149
|
+
// `isValidating` keeps derived-field reads from creating (now-unflushable) dirty state.
|
|
1150
|
+
this.#isValidating = true;
|
|
1151
|
+
await validateSimpleRules(entityTodos, (config) => config.commitRules);
|
|
1152
|
+
await validateReactiveRules(this, this.#rm.logger, entityTodos, joinRowTodos, (meta) => meta.reactiveCommitRules);
|
|
1153
|
+
}
|
|
1154
|
+
finally {
|
|
1155
|
+
this.#isValidating = false;
|
|
1156
|
+
}
|
|
1157
|
+
};
|
|
1158
|
+
// Commit rules run once, at the end, over just the flushed entities/join-rows whose type has
|
|
1159
|
+
// commit rules (see `meta.hasCommitRules`). We accumulate that subset here as we flush — deduping
|
|
1160
|
+
// across the RQF loop below (which resets entity dirty-state) — so we neither keep every todo
|
|
1161
|
+
// nor re-scan/merge them afterwards; apps without commit rules leave these empty and pay ~nothing.
|
|
1162
|
+
const commitRuleTodos = {};
|
|
1163
|
+
const commitRuleJoinRowTodos = {};
|
|
1164
|
+
const commitRuleSeen = new Set();
|
|
1165
|
+
function collectCommitRuleTodos(entityTodos, joinRowTodos) {
|
|
1166
|
+
if (skipValidation)
|
|
1167
|
+
return;
|
|
1168
|
+
for (const todo of Object.values(entityTodos)) {
|
|
1169
|
+
if (!todo.metadata.hasCommitRules)
|
|
1170
|
+
continue;
|
|
1171
|
+
for (const key of ["inserts", "updates", "deletes"]) {
|
|
1172
|
+
for (const e of todo[key]) {
|
|
1173
|
+
if (!commitRuleSeen.has(e)) {
|
|
1174
|
+
commitRuleSeen.add(e);
|
|
1175
|
+
(0, Todo_1.getTodo)(commitRuleTodos, e)[key].push(e);
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
for (const [table, todo] of Object.entries(joinRowTodos)) {
|
|
1181
|
+
if (!todo.m2m.meta.hasCommitRules && !todo.m2m.otherMeta?.hasCommitRules)
|
|
1182
|
+
continue;
|
|
1183
|
+
const into = (commitRuleJoinRowTodos[table] ??= { ...todo, newRows: [], deletedRows: [] });
|
|
1184
|
+
for (const key of ["newRows", "deletedRows"]) {
|
|
1185
|
+
for (const r of todo[key]) {
|
|
1186
|
+
if (!commitRuleSeen.has(r)) {
|
|
1187
|
+
commitRuleSeen.add(r);
|
|
1188
|
+
into[key].push(r);
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1080
1194
|
// Run hooks (in iterative loops if hooks mutate new entities) on pending entities
|
|
1081
1195
|
let entitiesToFlush = await runHooksOnPendingEntities();
|
|
1082
1196
|
for (const e of entitiesToFlush)
|
|
@@ -1095,6 +1209,8 @@ class EntityManager {
|
|
|
1095
1209
|
do {
|
|
1096
1210
|
if (Object.keys(entityTodos).length > 0 || Object.keys(joinRowTodos).length > 0) {
|
|
1097
1211
|
await this.driver.flush(this, entityTodos, joinRowTodos);
|
|
1212
|
+
// Accumulate the commit-rule-relevant subset before the RQF loop clobbers/resets these
|
|
1213
|
+
collectCommitRuleTodos(entityTodos, joinRowTodos);
|
|
1098
1214
|
}
|
|
1099
1215
|
// Now that we've flushed, we can let plugins know what we've done.
|
|
1100
1216
|
pluginManager.afterWrite(entityTodos, joinRowTodos);
|
|
@@ -1130,6 +1246,18 @@ class EntityManager {
|
|
|
1130
1246
|
entityTodos = {};
|
|
1131
1247
|
}
|
|
1132
1248
|
} while (Object.keys(entityTodos).length > 0);
|
|
1249
|
+
// Now that all SQL has hit the db (but before COMMIT), run any `addCommitRule`s so their
|
|
1250
|
+
// `em.find`s see the changed state. `commitRuleTodos` is only non-empty if a flushed entity
|
|
1251
|
+
// actually has commit rules, so apps without them skip this (and its logging) entirely.
|
|
1252
|
+
if (Object.keys(commitRuleTodos).length > 0 || Object.keys(commitRuleJoinRowTodos).length > 0) {
|
|
1253
|
+
// Make just-inserted entities findable by id (like the RQF loop does) so commit rules'
|
|
1254
|
+
// `em.find`s return these same instances rather than hydrating duplicate ones.
|
|
1255
|
+
for (const e of allFlushedEntities) {
|
|
1256
|
+
if (e.isNewEntity && !e.isDeletedEntity)
|
|
1257
|
+
this.#entitiesById.set(e.idTagged, e);
|
|
1258
|
+
}
|
|
1259
|
+
await runCommitValidation(commitRuleTodos, commitRuleJoinRowTodos);
|
|
1260
|
+
}
|
|
1133
1261
|
// Run `beforeCommit once right before COMMIT
|
|
1134
1262
|
await beforeCommit(this.ctx, allFlushedEntities);
|
|
1135
1263
|
if (this.mode === "in-memory-writes") {
|
|
@@ -1141,13 +1269,17 @@ class EntityManager {
|
|
|
1141
1269
|
await afterCommit(this.ctx, allFlushedEntities);
|
|
1142
1270
|
// Update the `#orm` field to reflect the new state
|
|
1143
1271
|
for (const e of allFlushedEntities) {
|
|
1272
|
+
const instanceData = (0, BaseEntity_1.getInstanceData)(e);
|
|
1144
1273
|
if (e.isNewEntity && !e.isDeletedEntity)
|
|
1145
1274
|
this.#entitiesById.set(e.idTagged, e);
|
|
1146
|
-
|
|
1147
|
-
// Reset AsyncQueryProperties since DB state may have changed
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1275
|
+
instanceData.resetAfterFlushed();
|
|
1276
|
+
// Reset AsyncQueryProperties & LazyFields since DB state may have changed
|
|
1277
|
+
const { relations } = instanceData;
|
|
1278
|
+
if (relations) {
|
|
1279
|
+
for (const rel of Object.values(relations)) {
|
|
1280
|
+
if (rel instanceof AsyncProperty_1.AsyncPropertyImpl || rel instanceof LazyField_1.LazyFieldImpl)
|
|
1281
|
+
rel.resetAfterFlush();
|
|
1282
|
+
}
|
|
1151
1283
|
}
|
|
1152
1284
|
}
|
|
1153
1285
|
// Update the joinRows refs to reflect the new state
|
|
@@ -1185,16 +1317,28 @@ class EntityManager {
|
|
|
1185
1317
|
}
|
|
1186
1318
|
}
|
|
1187
1319
|
}
|
|
1320
|
+
// node-pg keeps the DETAIL line (i.e. "Key (identity_id, final_id)=(102591, null) already exists")
|
|
1321
|
+
// on a separate `detail` field, so keep it to suffix onto messages to help debugging.
|
|
1322
|
+
const detail = e && typeof e === "object" && "detail" in e && typeof e.detail === "string" ? e.detail : undefined;
|
|
1188
1323
|
if (e && typeof e === "object" && "constraint" in e && typeof e.constraint === "string") {
|
|
1189
1324
|
// node-pg errors use `constraint` to indicate the constraint name
|
|
1190
1325
|
const message = config_1.constraintNameToValidationError[e.constraint];
|
|
1191
1326
|
if (message) {
|
|
1192
|
-
|
|
1327
|
+
const errors = new index_1.ValidationErrors(message);
|
|
1328
|
+
// Keep the user-facing `errors[].message` pretty, but suffix the DETAIL onto the top-level
|
|
1329
|
+
// `message` so the debugging context survives into logs/stack traces.
|
|
1330
|
+
if (detail)
|
|
1331
|
+
errors.message = `${message} — ${detail}`;
|
|
1332
|
+
throw errors;
|
|
1193
1333
|
}
|
|
1194
1334
|
}
|
|
1195
1335
|
if (e instanceof InMemoryRollbackError) {
|
|
1196
1336
|
return [...allFlushedEntities].sort((a, b) => (0, BaseEntity_1.getInstanceData)(a).entityIndex - (0, BaseEntity_1.getInstanceData)(b).entityIndex);
|
|
1197
1337
|
}
|
|
1338
|
+
// For raw (non-pretty) pg errors, fold the DETAIL into `message` so it survives into logs.
|
|
1339
|
+
if (detail && e instanceof Error) {
|
|
1340
|
+
e.message = `${e.message} — ${detail}`;
|
|
1341
|
+
}
|
|
1198
1342
|
throw e;
|
|
1199
1343
|
}
|
|
1200
1344
|
finally {
|
|
@@ -1301,22 +1445,57 @@ class EntityManager {
|
|
|
1301
1445
|
* the WIP entity state.
|
|
1302
1446
|
*/
|
|
1303
1447
|
hydrate(type, rows, options) {
|
|
1448
|
+
return this.hydrateFromRowData(type, new RowData_1.PojoRowData(rows), options);
|
|
1449
|
+
}
|
|
1450
|
+
/**
|
|
1451
|
+
* Hydrates a query's `RowData`, running any sidecar reads, and always `finalize`s the result.
|
|
1452
|
+
*
|
|
1453
|
+
* This is the internal API for Joist's find/load loaders: `sidecars` is where reads of
|
|
1454
|
+
* non-entity columns (preload aggregates, `_tags`, found-id checks) belong — they must run
|
|
1455
|
+
* before `finalize`, because compaction drops unretained rows — and the `try/finally`
|
|
1456
|
+
* guarantees a failed hydration or sidecar read still releases the result's unretained
|
|
1457
|
+
* buffers instead of pinning them.
|
|
1458
|
+
*/
|
|
1459
|
+
hydrateAndFinalize(type, rowData, opts) {
|
|
1460
|
+
try {
|
|
1461
|
+
const entities = this.hydrateFromRowData(type, rowData, opts);
|
|
1462
|
+
opts?.sidecars?.(entities);
|
|
1463
|
+
return entities;
|
|
1464
|
+
}
|
|
1465
|
+
finally {
|
|
1466
|
+
rowData.finalize?.();
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
/**
|
|
1470
|
+
* Like {@link hydrate}, but reads rows from a {@link RowData}, i.e. a driver-produced
|
|
1471
|
+
* lazy query result, instead of an array of POJO rows.
|
|
1472
|
+
*
|
|
1473
|
+
* This is an internal API used by Joist's own find loaders (usually via
|
|
1474
|
+
* {@link hydrateAndFinalize}); rows whose entities are kept (newly created or
|
|
1475
|
+
* overwrite-refreshed) are `retain`-ed on the `RowData` so the caller can later `finalize`
|
|
1476
|
+
* (trim/compact) the result.
|
|
1477
|
+
*/
|
|
1478
|
+
hydrateFromRowData(type, rowData, options) {
|
|
1304
1479
|
const maybeBaseMeta = (0, index_1.getMetadata)(type);
|
|
1305
|
-
const taggedIdPrefix = `${maybeBaseMeta.tagName}:`;
|
|
1306
1480
|
const overwriteExisting = options?.overwriteExisting === true;
|
|
1307
|
-
|
|
1308
|
-
const entities = new Array(
|
|
1309
|
-
for (
|
|
1310
|
-
const id =
|
|
1311
|
-
const taggedId = id === undefined || id === null ? (0, utils_1.fail)("No id column was available") :
|
|
1481
|
+
const count = rowData.rowCount;
|
|
1482
|
+
const entities = new Array(count);
|
|
1483
|
+
for (let i = 0; i < count; i++) {
|
|
1484
|
+
const id = rowData.get(i, "id");
|
|
1485
|
+
const taggedId = id === undefined || id === null ? (0, utils_1.fail)("No id column was available") : (0, index_1.keyToTaggedId)(maybeBaseMeta, id);
|
|
1312
1486
|
// See if this is already in our UoW
|
|
1313
1487
|
let entity = this.findExistingInstance(taggedId);
|
|
1314
1488
|
if (!entity) {
|
|
1315
1489
|
// Look for __class from the driver telling us which subtype to instantiate
|
|
1316
|
-
const meta = findConcreteMeta(maybeBaseMeta,
|
|
1490
|
+
const meta = findConcreteMeta(maybeBaseMeta, rowData, i);
|
|
1317
1491
|
// Pass id as a hint that we're in hydrate mode
|
|
1318
1492
|
entity = (0, newEntity_1.newEntity)(this, (0, index_1.asConcreteCstr)(meta.cstr), false);
|
|
1319
|
-
(0, BaseEntity_1.getInstanceData)(entity)
|
|
1493
|
+
const instanceData = (0, BaseEntity_1.getInstanceData)(entity);
|
|
1494
|
+
instanceData.rowData = rowData;
|
|
1495
|
+
instanceData.rowIndex = i;
|
|
1496
|
+
rowData.retain?.(i);
|
|
1497
|
+
// Seed the id to share the identity-map key string and skip the id serde on first read
|
|
1498
|
+
instanceData.data["id"] = taggedId;
|
|
1320
1499
|
this.#doRegister(entity, taggedId, meta, true);
|
|
1321
1500
|
}
|
|
1322
1501
|
else if (overwriteExisting) {
|
|
@@ -1324,24 +1503,34 @@ class EntityManager {
|
|
|
1324
1503
|
// `EntityManager.refresh` is telling us to explicitly load the latest data.
|
|
1325
1504
|
// First swap out the old row with the new row
|
|
1326
1505
|
const instanceData = (0, BaseEntity_1.getInstanceData)(entity);
|
|
1327
|
-
instanceData.
|
|
1506
|
+
instanceData.rowData = rowData;
|
|
1507
|
+
instanceData.rowIndex = i;
|
|
1508
|
+
rowData.retain?.(i);
|
|
1328
1509
|
// And then only refresh the data keys that have already been serde-d from rows
|
|
1329
1510
|
// (this keeps us from deserializing data out of rows that we don't need).
|
|
1330
1511
|
const { data } = instanceData;
|
|
1331
1512
|
const dataKeys = Object.keys(data);
|
|
1332
|
-
|
|
1513
|
+
// `id` is seeded at hydrate and can never change, so it never needs refreshing
|
|
1514
|
+
if (dataKeys.length > ("id" in data ? 1 : 0)) {
|
|
1333
1515
|
const allFields = (0, index_1.getMetadata)(entity).allFields;
|
|
1334
|
-
|
|
1516
|
+
// For existing entities this is exactly `changes.fieldsWithoutRelations`, minus the proxy allocation
|
|
1517
|
+
const changedFields = instanceData.isNewEntity
|
|
1518
|
+
? entity.changes.fieldsWithoutRelations
|
|
1519
|
+
: Object.keys(instanceData.originalData);
|
|
1335
1520
|
if (changedFields.length === 0) {
|
|
1336
1521
|
for (const fieldName of dataKeys) {
|
|
1522
|
+
if (fieldName === "id")
|
|
1523
|
+
continue;
|
|
1337
1524
|
const serde = allFields[fieldName].serde ?? (0, utils_1.fail)(`Missing serde for ${fieldName}`);
|
|
1338
|
-
serde.
|
|
1525
|
+
serde.setOnEntityFromRowData(data, rowData, i);
|
|
1339
1526
|
}
|
|
1340
1527
|
}
|
|
1341
1528
|
else {
|
|
1342
1529
|
for (const fieldName of dataKeys) {
|
|
1530
|
+
if (fieldName === "id")
|
|
1531
|
+
continue;
|
|
1343
1532
|
const serde = allFields[fieldName].serde ?? (0, utils_1.fail)(`Missing serde for ${fieldName}`);
|
|
1344
|
-
serde.
|
|
1533
|
+
serde.setOnEntityFromRowData(data, rowData, i);
|
|
1345
1534
|
// Make the field look not-dirty
|
|
1346
1535
|
if (changedFields.includes(fieldName)) {
|
|
1347
1536
|
instanceData.markFieldClean(fieldName);
|
|
@@ -1350,7 +1539,7 @@ class EntityManager {
|
|
|
1350
1539
|
}
|
|
1351
1540
|
}
|
|
1352
1541
|
}
|
|
1353
|
-
entities[i
|
|
1542
|
+
entities[i] = entity;
|
|
1354
1543
|
}
|
|
1355
1544
|
return entities;
|
|
1356
1545
|
}
|
|
@@ -1359,15 +1548,28 @@ class EntityManager {
|
|
|
1359
1548
|
(0, BaseEntity_1.getInstanceData)(entity).markTouched();
|
|
1360
1549
|
}
|
|
1361
1550
|
}
|
|
1362
|
-
async recalc(entityOrEntities) {
|
|
1551
|
+
async recalc(entityOrEntities, reactionName) {
|
|
1552
|
+
const entities = (0, utils_1.toArray)(entityOrEntities);
|
|
1553
|
+
if (reactionName !== undefined) {
|
|
1554
|
+
// Resolve every reaction before invoking any of them so a mixed list cannot partially run.
|
|
1555
|
+
const reactions = entities.map((entity) => {
|
|
1556
|
+
const reaction = (0, index_1.getBaseAndSelfMetas)((0, index_1.getMetadata)(entity))
|
|
1557
|
+
.flatMap((meta) => meta.config.__data.reactions)
|
|
1558
|
+
.find((reaction) => reaction.name === reactionName);
|
|
1559
|
+
return reaction ?? (0, utils_1.fail)(`Reaction '${reactionName}' not found on ${(0, index_1.getMetadata)(entity).type}`);
|
|
1560
|
+
});
|
|
1561
|
+
await (0, trusted_1.runInTrustedContext)(() => Promise.allSettled(reactions.map((reaction, i) => reaction.fn(entities[i], this.ctx))).then(utils_1.failIfAnyRejected));
|
|
1562
|
+
await this.#rm.recalcPendingReactables("reactables");
|
|
1563
|
+
return;
|
|
1564
|
+
}
|
|
1363
1565
|
// Look for async reactive fields
|
|
1364
|
-
const relations =
|
|
1566
|
+
const relations = entities.flatMap((entity) => Object.values((0, index_1.getMetadata)(entity).allFields)
|
|
1365
1567
|
.filter((f) => "derived" in f && f.derived === "async")
|
|
1366
1568
|
.map((field) => entity[field.fieldName]));
|
|
1367
1569
|
// Use forceReload: true to tell ReactiveReferences to recalc against their full graph
|
|
1368
1570
|
await Promise.all(relations.map((r) => r.load({ forceReload: true })));
|
|
1369
1571
|
// And also sync reactive fields
|
|
1370
|
-
|
|
1572
|
+
entities.flatMap((entity) => Object.values((0, index_1.getMetadata)(entity).allFields)
|
|
1371
1573
|
.filter((f) => "derived" in f && f.derived === "sync")
|
|
1372
1574
|
.forEach((field) => {
|
|
1373
1575
|
// This will be a noop if the value is the same
|
|
@@ -1460,7 +1662,7 @@ class EntityManager {
|
|
|
1460
1662
|
}
|
|
1461
1663
|
const writeFn = getDefaultWriteFn(this.ctx);
|
|
1462
1664
|
if (typeof arg === "boolean") {
|
|
1463
|
-
this.#fieldLogger = arg ? new index_1.FieldLogger([], writeFn) :
|
|
1665
|
+
this.#fieldLogger = arg ? new index_1.FieldLogger([], writeFn) : FieldLogger_1.noopFieldLogger;
|
|
1464
1666
|
}
|
|
1465
1667
|
else if (typeof arg === "string" || Array.isArray(arg)) {
|
|
1466
1668
|
const specs = Array.isArray(arg) ? arg : [arg];
|
|
@@ -1495,15 +1697,18 @@ class EntityManager {
|
|
|
1495
1697
|
const entities = this.#entitiesByTag.get(meta.tagName) ?? [];
|
|
1496
1698
|
// Don't bother filtering if there's no where clause (particularly b/c IndexManager.findMatching
|
|
1497
1699
|
// really expects there to be at least 1 condition)
|
|
1498
|
-
if (
|
|
1700
|
+
if (!(0, utils_1.hasAnyKey)(where)) {
|
|
1499
1701
|
return entities.filter((e) => e instanceof cstr && !e.isDeletedEntity);
|
|
1500
1702
|
}
|
|
1501
1703
|
if (this.#indexManager.shouldIndexType(entities.length)) {
|
|
1502
1704
|
this.#indexManager.enableIndexingForType(meta, entities, where);
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1705
|
+
// Build the final array in one pass, still filtering `instanceof cstr` to handle subtyping
|
|
1706
|
+
const result = [];
|
|
1707
|
+
for (const e of this.#indexManager.findMatching(meta, entities, where)) {
|
|
1708
|
+
if (e instanceof cstr && !e.isDeletedEntity)
|
|
1709
|
+
result.push(e);
|
|
1710
|
+
}
|
|
1711
|
+
return result;
|
|
1507
1712
|
}
|
|
1508
1713
|
else {
|
|
1509
1714
|
return entities
|
|
@@ -1626,6 +1831,8 @@ class EntityManager {
|
|
|
1626
1831
|
continue;
|
|
1627
1832
|
}
|
|
1628
1833
|
const { relations } = oldEntity.__data;
|
|
1834
|
+
if (!relations)
|
|
1835
|
+
continue;
|
|
1629
1836
|
for (const [field, relation] of Object.entries(relations)) {
|
|
1630
1837
|
// With lazyRelation, custom relations are inserted into the `relations` map. Custom relations don't
|
|
1631
1838
|
// store any data, so we can ignore them by checking if the relation implements `import`
|
|
@@ -1742,7 +1949,7 @@ class EntityManager {
|
|
|
1742
1949
|
}
|
|
1743
1950
|
// api will be undefined during getFakeInstance
|
|
1744
1951
|
const api = getEmInternalApi(this);
|
|
1745
|
-
api?.fieldLogger
|
|
1952
|
+
api?.fieldLogger.logCreate(entity);
|
|
1746
1953
|
(0, index_1.setOpts)(entity, opts, { partial, calledFromConstructor: true });
|
|
1747
1954
|
// Apply any synchronous defaults, after the opts have been applied
|
|
1748
1955
|
if (!this.fakeInstance) {
|
|
@@ -1851,8 +2058,10 @@ exports.TooManyError = TooManyError;
|
|
|
1851
2058
|
* from the currently-changed entities back to each rule's originally-defined-in entity,
|
|
1852
2059
|
* and ensure those entities are added to `todos`.
|
|
1853
2060
|
*/
|
|
1854
|
-
async function validateReactiveRules(em, logger, todos, joinRowTodos
|
|
1855
|
-
|
|
2061
|
+
async function validateReactiveRules(em, logger, todos, joinRowTodos,
|
|
2062
|
+
// Which list of reactive rules to run, i.e. the pre-flush `reactiveRules` or the post-flush `reactiveCommitRules`
|
|
2063
|
+
getRules = (meta) => meta.reactiveRules) {
|
|
2064
|
+
logger.logStartingValidate(em, todos);
|
|
1856
2065
|
// Use a map of rule -> Set<Entity> so that we only invoke a rule once per entity,
|
|
1857
2066
|
// even if it was triggered by multiple changed fields.
|
|
1858
2067
|
const fns = new Map();
|
|
@@ -1869,15 +2078,18 @@ async function validateReactiveRules(em, logger, todos, joinRowTodos) {
|
|
|
1869
2078
|
entities = new Set();
|
|
1870
2079
|
fns.set(rule.fn, entities);
|
|
1871
2080
|
}
|
|
1872
|
-
logger
|
|
2081
|
+
logger.logWalked(triggered, rule, found, "validate");
|
|
1873
2082
|
found.forEach((entity) => {
|
|
1874
2083
|
entities.add(entity);
|
|
1875
2084
|
});
|
|
1876
2085
|
}
|
|
1877
2086
|
const p1 = Object.values(todos).flatMap((todo) => {
|
|
1878
|
-
const entities = [...todo.inserts, ...todo.updates, ...todo.deletes];
|
|
1879
2087
|
// Find each statically-declared reactive rule for the given entity type
|
|
1880
|
-
const rules = todo.metadata
|
|
2088
|
+
const rules = getRules(todo.metadata);
|
|
2089
|
+
// Skip the entities spread for rule-less types
|
|
2090
|
+
if (rules.length === 0)
|
|
2091
|
+
return [];
|
|
2092
|
+
const entities = [...todo.inserts, ...todo.updates, ...todo.deletes];
|
|
1881
2093
|
return rules.map((rule) => {
|
|
1882
2094
|
// Of all changed entities of this type, how many specifically trigger this rule?
|
|
1883
2095
|
const triggered = entities.filter((e) => {
|
|
@@ -1887,10 +2099,12 @@ async function validateReactiveRules(em, logger, todos, joinRowTodos) {
|
|
|
1887
2099
|
// Any new-or-deleted entity fires every rule (reactiveRules has already filtered out read-only)
|
|
1888
2100
|
if (e.isNewEntity || e.isDeletedEntity)
|
|
1889
2101
|
return true;
|
|
1890
|
-
// Otherwise see if the changed fields
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
2102
|
+
// Otherwise see if the changed fields overlap with the rule's fields; for existing entities
|
|
2103
|
+
// `changes.fieldsWithoutRelations` is exactly `Object.keys(originalData)`, so probe originalData
|
|
2104
|
+
// directly instead of allocating a proxy + keys array per entity per rule.
|
|
2105
|
+
const { originalData } = (0, BaseEntity_1.getInstanceData)(e);
|
|
2106
|
+
for (const field of rule.fields) {
|
|
2107
|
+
if (field in originalData)
|
|
1894
2108
|
return true;
|
|
1895
2109
|
}
|
|
1896
2110
|
return false;
|
|
@@ -1903,8 +2117,8 @@ async function validateReactiveRules(em, logger, todos, joinRowTodos) {
|
|
|
1903
2117
|
// For enum m2ms the columns include the enum's numeric id, so keep only the entity values.
|
|
1904
2118
|
const entities = [...todo.newRows, ...todo.deletedRows].flatMap((jr) => Object.values(jr.columns)).filter(Entity_1.isEntity);
|
|
1905
2119
|
// The owning-entity side (applies to both entity-to-entity and entity-to-enum m2ms).
|
|
1906
|
-
const a = todo.m2m.meta
|
|
1907
|
-
.
|
|
2120
|
+
const a = getRules(todo.m2m.meta)
|
|
2121
|
+
.filter((rule) => rule.fields.includes(todo.m2m.fieldName))
|
|
1908
2122
|
.map((rule) => {
|
|
1909
2123
|
const triggered = entities.filter((e) => e instanceof todo.m2m.meta.cstr);
|
|
1910
2124
|
return followAndQueue(triggered, rule);
|
|
@@ -1912,8 +2126,8 @@ async function validateReactiveRules(em, logger, todos, joinRowTodos) {
|
|
|
1912
2126
|
// The "other" side only exists for entity-to-entity m2ms; enums have no reverse field.
|
|
1913
2127
|
const { otherMeta, otherFieldName } = todo.m2m;
|
|
1914
2128
|
const b = otherMeta
|
|
1915
|
-
? otherMeta
|
|
1916
|
-
.
|
|
2129
|
+
? getRules(otherMeta)
|
|
2130
|
+
.filter((rule) => rule.fields.includes(otherFieldName))
|
|
1917
2131
|
.map((rule) => {
|
|
1918
2132
|
const triggered = entities.filter((e) => e instanceof otherMeta.cstr);
|
|
1919
2133
|
return followAndQueue(triggered, rule);
|
|
@@ -1932,22 +2146,37 @@ async function validateReactiveRules(em, logger, todos, joinRowTodos) {
|
|
|
1932
2146
|
// Run *non-reactive* (those with `fields: undefined`) rules of explicitly mutated entities,
|
|
1933
2147
|
// because even for reactive validations on mutated entities, we defer to addReactiveValidations
|
|
1934
2148
|
// to mark only the rules that need to run.
|
|
1935
|
-
async function validateSimpleRules(todos
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
2149
|
+
async function validateSimpleRules(todos,
|
|
2150
|
+
// Which config list to pull rules from, i.e. the pre-flush `rules` or the post-flush `commitRules`
|
|
2151
|
+
getRules = (config) => config.rules) {
|
|
2152
|
+
let p = undefined;
|
|
2153
|
+
for (const todoKey in todos) {
|
|
2154
|
+
const { inserts, updates } = todos[todoKey];
|
|
2155
|
+
for (const entities of [inserts, updates]) {
|
|
2156
|
+
for (const entity of entities) {
|
|
2157
|
+
if (entity.isDeletedEntity)
|
|
2158
|
+
continue;
|
|
2159
|
+
for (const m of (0, index_1.getBaseAndSelfMetas)((0, index_1.getMetadata)(entity))) {
|
|
2160
|
+
for (const rule of getRules(m.config.__data)) {
|
|
2161
|
+
if (rule.hint !== undefined)
|
|
2162
|
+
continue;
|
|
2163
|
+
(p ??= []).push(invokeRule(entity, rule.fn));
|
|
2164
|
+
}
|
|
2165
|
+
}
|
|
2166
|
+
}
|
|
2167
|
+
}
|
|
2168
|
+
}
|
|
2169
|
+
if (!p)
|
|
2170
|
+
return;
|
|
1946
2171
|
const errors = (0, utils_1.failIfAnyRejected)(await Promise.allSettled(p)).flat();
|
|
1947
2172
|
if (errors.length > 0) {
|
|
1948
2173
|
throw new index_1.ValidationErrors(errors);
|
|
1949
2174
|
}
|
|
1950
2175
|
}
|
|
2176
|
+
/** Invokes a single validation rule, coercing its result/sync throws into a promise. */
|
|
2177
|
+
async function invokeRule(entity, fn) {
|
|
2178
|
+
return coerceError(entity, await fn(entity));
|
|
2179
|
+
}
|
|
1951
2180
|
function driverBeforeBegin(em, txn) {
|
|
1952
2181
|
return Promise.all(getEmInternalApi(em).hooks.beforeBegin.map((fn) => fn(em, txn)));
|
|
1953
2182
|
}
|
|
@@ -1961,23 +2190,54 @@ function driverAfterCommit(em, txn) {
|
|
|
1961
2190
|
return Promise.all(getEmInternalApi(em).hooks.afterCommit.map((fn) => fn(em, txn)));
|
|
1962
2191
|
}
|
|
1963
2192
|
async function runHookOnTodos(ctx, hook, todos, keys) {
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
2193
|
+
// Only collect entities for todos whose type hierarchy actually has this hook, so that
|
|
2194
|
+
// hook-less bulk flushes skip the per-entity array building entirely.
|
|
2195
|
+
let entities = undefined;
|
|
2196
|
+
for (const todoKey in todos) {
|
|
2197
|
+
const todo = todos[todoKey];
|
|
2198
|
+
if (!metaHasHook(todo.metadata, hook))
|
|
2199
|
+
continue;
|
|
2200
|
+
for (const k of keys) {
|
|
2201
|
+
for (const e of todo[k]) {
|
|
2202
|
+
if (k !== "deletes" && e.isDeletedEntity)
|
|
2203
|
+
continue;
|
|
2204
|
+
(entities ??= []).push(e);
|
|
2205
|
+
}
|
|
2206
|
+
}
|
|
2207
|
+
}
|
|
2208
|
+
if (entities)
|
|
2209
|
+
return runHook(ctx, hook, entities);
|
|
2210
|
+
}
|
|
2211
|
+
/** Returns whether `meta`'s base/self/sub hierarchy has any `hook` functions registered. */
|
|
2212
|
+
function metaHasHook(meta, hook) {
|
|
2213
|
+
for (const m of (0, index_1.getBaseSelfAndSubMetas)(meta)) {
|
|
2214
|
+
if (m.config.__data.hooks[hook].length > 0)
|
|
2215
|
+
return true;
|
|
2216
|
+
}
|
|
2217
|
+
return false;
|
|
1968
2218
|
}
|
|
1969
2219
|
async function runHook(ctx, hook, entities) {
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
2220
|
+
let p = undefined;
|
|
2221
|
+
for (const entity of entities) {
|
|
2222
|
+
for (const m of (0, index_1.getBaseAndSelfMetas)((0, index_1.getMetadata)(entity))) {
|
|
2223
|
+
for (const fn of m.config.__data.hooks[hook]) {
|
|
2224
|
+
// Use an explicit `async` invoke to ensure all hooks are promises, i.e. so that a
|
|
2225
|
+
// non-promise hook blowing up doesn't orphan the others.
|
|
2226
|
+
(p ??= []).push(invokeHook(fn, entity, ctx));
|
|
2227
|
+
}
|
|
2228
|
+
}
|
|
2229
|
+
}
|
|
2230
|
+
if (!p)
|
|
2231
|
+
return;
|
|
1976
2232
|
// Use `allSettled` so that even if 1 hook blows up, we don't orphan other hooks mid-flush
|
|
1977
2233
|
// (causes weird errors when/if they try to access the EntityManager that has "moved on")
|
|
1978
2234
|
const results = await Promise.allSettled(p);
|
|
1979
2235
|
(0, utils_1.failIfAnyRejected)(results);
|
|
1980
2236
|
}
|
|
2237
|
+
/** Invokes a single hook fn, coercing sync throws into rejections. */
|
|
2238
|
+
async function invokeHook(fn, entity, ctx) {
|
|
2239
|
+
return fn(entity, ctx);
|
|
2240
|
+
}
|
|
1981
2241
|
function beforeDelete(ctx, todos) {
|
|
1982
2242
|
return runHookOnTodos(ctx, "beforeDelete", todos, ["deletes"]);
|
|
1983
2243
|
}
|
|
@@ -1997,16 +2257,21 @@ function afterValidation(ctx, todos) {
|
|
|
1997
2257
|
function entitiesFromTodos(entityTodos, joinRowTodos) {
|
|
1998
2258
|
const entities = new Set();
|
|
1999
2259
|
for (const todo of Object.values(entityTodos)) {
|
|
2000
|
-
[
|
|
2260
|
+
for (const list of [todo.inserts, todo.updates, todo.deletes]) {
|
|
2261
|
+
for (const entity of list)
|
|
2262
|
+
entities.add(entity);
|
|
2263
|
+
}
|
|
2001
2264
|
}
|
|
2002
2265
|
for (const todo of Object.values(joinRowTodos)) {
|
|
2003
|
-
[
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2266
|
+
for (const rows of [todo.newRows, todo.deletedRows]) {
|
|
2267
|
+
for (const row of rows) {
|
|
2268
|
+
// For enum m2ms, a column value is the enum's numeric id rather than an entity.
|
|
2269
|
+
for (const value of Object.values(row.columns)) {
|
|
2270
|
+
if ((0, Entity_1.isEntity)(value))
|
|
2271
|
+
entities.add(value);
|
|
2272
|
+
}
|
|
2273
|
+
}
|
|
2274
|
+
}
|
|
2010
2275
|
}
|
|
2011
2276
|
return [...entities];
|
|
2012
2277
|
}
|
|
@@ -2040,23 +2305,19 @@ function coerceError(entity, maybeError) {
|
|
|
2040
2305
|
}
|
|
2041
2306
|
/** Evaluates each (non-async) derived field to see if it's value has changed. */
|
|
2042
2307
|
function recalcSynchronousDerivedFields(todos) {
|
|
2043
|
-
const
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
derivedFields.forEach((fieldName) => {
|
|
2057
|
-
// setField will intelligently mark/not mark the field as dirty.
|
|
2058
|
-
(0, fields_1.setField)(entity, fieldName, entity[fieldName]);
|
|
2059
|
-
});
|
|
2308
|
+
for (const todoKey in todos) {
|
|
2309
|
+
const todo = todos[todoKey];
|
|
2310
|
+
for (const entities of [todo.inserts, todo.updates]) {
|
|
2311
|
+
for (const entity of entities) {
|
|
2312
|
+
if (entity.isDeletedEntity)
|
|
2313
|
+
continue;
|
|
2314
|
+
// Use the per-meta cached field list instead of rebuilding a map per hook loop
|
|
2315
|
+
for (const fieldName of (0, index_1.getMetadata)(entity).syncDerivedFields) {
|
|
2316
|
+
// setField will intelligently mark/not mark the field as dirty.
|
|
2317
|
+
(0, fields_1.setField)(entity, fieldName, entity[fieldName]);
|
|
2318
|
+
}
|
|
2319
|
+
}
|
|
2320
|
+
}
|
|
2060
2321
|
}
|
|
2061
2322
|
}
|
|
2062
2323
|
/** Recursively crawls through `entity`, with the given populate `deep` hint, and adds anything found to `found`. */
|
|
@@ -2174,22 +2435,28 @@ function getNow() {
|
|
|
2174
2435
|
return now;
|
|
2175
2436
|
}
|
|
2176
2437
|
/** Given a `row` from the db, resolves the CTI/STI subtype, if applicable. */
|
|
2177
|
-
function findConcreteMeta(maybeBaseMeta,
|
|
2178
|
-
// Common case of no CTI or STI inheritance
|
|
2179
|
-
|
|
2438
|
+
function findConcreteMeta(maybeBaseMeta, rowData, rowIndex) {
|
|
2439
|
+
// Common case of no CTI or STI inheritance; only probe __class for CTI metas so that
|
|
2440
|
+
// lazy/columnar stores don't pay a missing-column lookup per row for regular entities
|
|
2441
|
+
if (maybeBaseMeta.inheritanceType === undefined) {
|
|
2442
|
+
return maybeBaseMeta;
|
|
2443
|
+
}
|
|
2444
|
+
const __class = rowData.get(rowIndex, "__class");
|
|
2445
|
+
if (!__class && maybeBaseMeta.inheritanceType !== "sti") {
|
|
2180
2446
|
return maybeBaseMeta;
|
|
2181
2447
|
}
|
|
2182
|
-
if (
|
|
2183
|
-
if (
|
|
2184
|
-
|
|
2448
|
+
if (__class) {
|
|
2449
|
+
if (__class === "_" && maybeBaseMeta.ctiAbstract) {
|
|
2450
|
+
const id = rowData.get(rowIndex, "id");
|
|
2451
|
+
throw new Error(`${maybeBaseMeta.type} ${(0, index_1.tagId)(maybeBaseMeta, id)} must be instantiated via a subtype`);
|
|
2185
2452
|
}
|
|
2186
2453
|
// Look for the CTI __class from the driver telling us which subtype to instantiate
|
|
2187
|
-
return maybeBaseMeta.subTypesByType.get(
|
|
2454
|
+
return maybeBaseMeta.subTypesByType.get(__class) ?? maybeBaseMeta;
|
|
2188
2455
|
}
|
|
2189
2456
|
else if (maybeBaseMeta.inheritanceType === "sti") {
|
|
2190
2457
|
// Look for the STI discriminator value
|
|
2191
2458
|
const baseMeta = (0, index_1.getBaseMeta)(maybeBaseMeta);
|
|
2192
|
-
const value =
|
|
2459
|
+
const value = rowData.get(rowIndex, baseMeta.stiDiscriminatorColumnName);
|
|
2193
2460
|
return baseMeta.subTypesByStiValue.get(value) ?? baseMeta;
|
|
2194
2461
|
}
|
|
2195
2462
|
else {
|
|
@@ -2278,7 +2545,7 @@ const fieldMap = {};
|
|
|
2278
2545
|
// Generates what a row from the db would look like for a given entity
|
|
2279
2546
|
function createRowFromEntityData(e, opts = {}) {
|
|
2280
2547
|
const { preferOriginalData = true } = opts;
|
|
2281
|
-
const {
|
|
2548
|
+
const { rowData, rowIndex, data, originalData } = e.__data;
|
|
2282
2549
|
const __class = e.constructor.name;
|
|
2283
2550
|
const { metadata: meta } = e.__data;
|
|
2284
2551
|
if (!fieldMap[__class]) {
|
|
@@ -2303,8 +2570,8 @@ function createRowFromEntityData(e, opts = {}) {
|
|
|
2303
2570
|
// reflect what would come from the db if we queried it right now, so use originalData when present
|
|
2304
2571
|
column.rowValue(preferOriginalData && field.fieldName in originalData ? originalData : data)
|
|
2305
2572
|
: // `data` is lazy and isn't set until it's accessed, so if the field isn't present there, then we should
|
|
2306
|
-
// be safe to pull the raw data out of
|
|
2307
|
-
|
|
2573
|
+
// be safe to pull the raw data out of the as-loaded RowData
|
|
2574
|
+
rowData.get(rowIndex, column.columnName);
|
|
2308
2575
|
row[column.columnName] = value ?? null;
|
|
2309
2576
|
}
|
|
2310
2577
|
return row;
|