joist-core 2.3.0-next.98 → 2.3.0-next.99

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (313) hide show
  1. package/build/BaseEntity.cjs.map +1 -1
  2. package/build/BaseEntity.js.map +1 -1
  3. package/build/Entity.cjs.map +1 -1
  4. package/build/Entity.js.map +1 -1
  5. package/build/EntityManager.cjs.map +1 -1
  6. package/build/EntityManager.d.cts.map +1 -1
  7. package/build/EntityManager.d.mts.map +1 -1
  8. package/build/EntityManager.js.map +1 -1
  9. package/build/EntityMetadata.cjs.map +1 -1
  10. package/build/EntityMetadata.js.map +1 -1
  11. package/build/IndexManager.cjs.map +1 -1
  12. package/build/IndexManager.js.map +1 -1
  13. package/build/InstanceData.cjs.map +1 -1
  14. package/build/InstanceData.js.map +1 -1
  15. package/build/PluginManager.cjs.map +1 -1
  16. package/build/PluginManager.js.map +1 -1
  17. package/build/batchloaders/enumCollectionBatchLoader.cjs.map +1 -1
  18. package/build/batchloaders/enumCollectionBatchLoader.js.map +1 -1
  19. package/build/batchloaders/loadBatchLoader.cjs.map +1 -1
  20. package/build/batchloaders/loadBatchLoader.js.map +1 -1
  21. package/build/batchloaders/manyToManyBatchLoader.cjs.map +1 -1
  22. package/build/batchloaders/manyToManyBatchLoader.js.map +1 -1
  23. package/build/batchloaders/oneToManyBatchLoader.cjs.map +1 -1
  24. package/build/batchloaders/oneToManyBatchLoader.js.map +1 -1
  25. package/build/batchloaders/oneToOneBatchLoader.cjs.map +1 -1
  26. package/build/batchloaders/oneToOneBatchLoader.js.map +1 -1
  27. package/build/batchloaders/populateBatchLoader.cjs.map +1 -1
  28. package/build/batchloaders/populateBatchLoader.js.map +1 -1
  29. package/build/batchloaders/recursiveChildrenBatchLoader.cjs.map +1 -1
  30. package/build/batchloaders/recursiveChildrenBatchLoader.js.map +1 -1
  31. package/build/batchloaders/recursiveM2mBatchLoader.cjs.map +1 -1
  32. package/build/batchloaders/recursiveM2mBatchLoader.js.map +1 -1
  33. package/build/batchloaders/recursiveParentsBatchLoader.cjs.map +1 -1
  34. package/build/batchloaders/recursiveParentsBatchLoader.js.map +1 -1
  35. package/build/changes.cjs.map +1 -1
  36. package/build/changes.js.map +1 -1
  37. package/build/config.cjs.map +1 -1
  38. package/build/config.js.map +1 -1
  39. package/build/configure.cjs.map +1 -1
  40. package/build/configure.js.map +1 -1
  41. package/build/dataloaders/fastWhereFilterHash.cjs.map +1 -1
  42. package/build/dataloaders/fastWhereFilterHash.js.map +1 -1
  43. package/build/dataloaders/findByUniqueDataLoader.cjs.map +1 -1
  44. package/build/dataloaders/findByUniqueDataLoader.d.cts.map +1 -1
  45. package/build/dataloaders/findByUniqueDataLoader.d.mts.map +1 -1
  46. package/build/dataloaders/findByUniqueDataLoader.js.map +1 -1
  47. package/build/dataloaders/findCountDataLoader.cjs.map +1 -1
  48. package/build/dataloaders/findCountDataLoader.js.map +1 -1
  49. package/build/dataloaders/findDataLoader.cjs +1 -1
  50. package/build/dataloaders/findDataLoader.cjs.map +1 -1
  51. package/build/dataloaders/findDataLoader.js +1 -1
  52. package/build/dataloaders/findDataLoader.js.map +1 -1
  53. package/build/dataloaders/findIdsDataLoader.cjs.map +1 -1
  54. package/build/dataloaders/findIdsDataLoader.js.map +1 -1
  55. package/build/dataloaders/findOrCreateDataLoader.cjs.map +1 -1
  56. package/build/dataloaders/findOrCreateDataLoader.d.cts.map +1 -1
  57. package/build/dataloaders/findOrCreateDataLoader.d.mts.map +1 -1
  58. package/build/dataloaders/findOrCreateDataLoader.js.map +1 -1
  59. package/build/dataloaders/findPaginatedDataLoader.cjs.map +1 -1
  60. package/build/dataloaders/findPaginatedDataLoader.js.map +1 -1
  61. package/build/dataloaders/lensDataLoader.cjs.map +1 -1
  62. package/build/dataloaders/lensDataLoader.d.cts.map +1 -1
  63. package/build/dataloaders/lensDataLoader.d.mts.map +1 -1
  64. package/build/dataloaders/lensDataLoader.js.map +1 -1
  65. package/build/dataloaders/manyToManyFindDataLoader.cjs.map +1 -1
  66. package/build/dataloaders/manyToManyFindDataLoader.d.cts.map +1 -1
  67. package/build/dataloaders/manyToManyFindDataLoader.d.mts.map +1 -1
  68. package/build/dataloaders/manyToManyFindDataLoader.js.map +1 -1
  69. package/build/dataloaders/oneToManyFindDataLoader.cjs.map +1 -1
  70. package/build/dataloaders/oneToManyFindDataLoader.d.cts.map +1 -1
  71. package/build/dataloaders/oneToManyFindDataLoader.d.mts.map +1 -1
  72. package/build/dataloaders/oneToManyFindDataLoader.js.map +1 -1
  73. package/build/defaults.cjs.map +1 -1
  74. package/build/defaults.d.cts.map +1 -1
  75. package/build/defaults.d.mts.map +1 -1
  76. package/build/defaults.js.map +1 -1
  77. package/build/drivers/EntityWriter.cjs.map +1 -1
  78. package/build/drivers/EntityWriter.js.map +1 -1
  79. package/build/drivers/IdAssigner.cjs.map +1 -1
  80. package/build/drivers/IdAssigner.js.map +1 -1
  81. package/build/drivers/driverApi.cjs.map +1 -1
  82. package/build/drivers/driverApi.js.map +1 -1
  83. package/build/fields.cjs.map +1 -1
  84. package/build/fields.js.map +1 -1
  85. package/build/flush/JoinRows.cjs.map +1 -1
  86. package/build/flush/JoinRows.js.map +1 -1
  87. package/build/flush/Todo.cjs.map +1 -1
  88. package/build/flush/Todo.js.map +1 -1
  89. package/build/getProperties.cjs.map +1 -1
  90. package/build/getProperties.js.map +1 -1
  91. package/build/index.cjs +2 -2
  92. package/build/index.cjs.map +1 -1
  93. package/build/index.js +2 -2
  94. package/build/index.js.map +1 -1
  95. package/build/json.cjs.map +1 -1
  96. package/build/json.js.map +1 -1
  97. package/build/keys.cjs.map +1 -1
  98. package/build/keys.js.map +1 -1
  99. package/build/loading/HintTree.cjs.map +1 -1
  100. package/build/loading/HintTree.js.map +1 -1
  101. package/build/loading/IsLoadedCache.cjs.map +1 -1
  102. package/build/loading/IsLoadedCache.js.map +1 -1
  103. package/build/loading/loadHints.cjs.map +1 -1
  104. package/build/loading/loadHints.js.map +1 -1
  105. package/build/loading/loadLens.cjs.map +1 -1
  106. package/build/loading/loadLens.js.map +1 -1
  107. package/build/loading/withLoaded.cjs.map +1 -1
  108. package/build/loading/withLoaded.js.map +1 -1
  109. package/build/logging/FactoryLogger.cjs.map +1 -1
  110. package/build/logging/FactoryLogger.d.cts.map +1 -1
  111. package/build/logging/FactoryLogger.d.mts.map +1 -1
  112. package/build/logging/FactoryLogger.js.map +1 -1
  113. package/build/logging/FieldLogger.cjs.map +1 -1
  114. package/build/logging/FieldLogger.d.cts.map +1 -1
  115. package/build/logging/FieldLogger.d.mts.map +1 -1
  116. package/build/logging/FieldLogger.js.map +1 -1
  117. package/build/logging/ReactionLogger.cjs.map +1 -1
  118. package/build/logging/ReactionLogger.d.cts.map +1 -1
  119. package/build/logging/ReactionLogger.d.mts.map +1 -1
  120. package/build/logging/ReactionLogger.js.map +1 -1
  121. package/build/newEntity.cjs.map +1 -1
  122. package/build/newEntity.js.map +1 -1
  123. package/build/newTestInstance.cjs.map +1 -1
  124. package/build/newTestInstance.d.cts.map +1 -1
  125. package/build/newTestInstance.d.mts.map +1 -1
  126. package/build/newTestInstance.js.map +1 -1
  127. package/build/normalizeHints.cjs.map +1 -1
  128. package/build/normalizeHints.js.map +1 -1
  129. package/build/nowUTC.cjs.map +1 -1
  130. package/build/nowUTC.js.map +1 -1
  131. package/build/plugins/ImmutableEntitiesPlugin.cjs.map +1 -1
  132. package/build/plugins/ImmutableEntitiesPlugin.js.map +1 -1
  133. package/build/preloading/JsonAggregatePreloader.cjs.map +1 -1
  134. package/build/preloading/JsonAggregatePreloader.d.cts.map +1 -1
  135. package/build/preloading/JsonAggregatePreloader.d.mts.map +1 -1
  136. package/build/preloading/JsonAggregatePreloader.js.map +1 -1
  137. package/build/preloading/canPreload.cjs.map +1 -1
  138. package/build/preloading/canPreload.js.map +1 -1
  139. package/build/preloading/partitionHint.cjs.map +1 -1
  140. package/build/preloading/partitionHint.js.map +1 -1
  141. package/build/queries/ConditionBuilder.cjs.map +1 -1
  142. package/build/queries/ConditionBuilder.js.map +1 -1
  143. package/build/queries/conditions.cjs.map +1 -1
  144. package/build/queries/conditions.js.map +1 -1
  145. package/build/queries/entityQueryUtils.cjs.map +1 -1
  146. package/build/queries/entityQueryUtils.js.map +1 -1
  147. package/build/queries/find/Aliases.cjs.map +1 -1
  148. package/build/queries/find/Aliases.d.cts.map +1 -1
  149. package/build/queries/find/Aliases.d.mts.map +1 -1
  150. package/build/queries/find/Aliases.js.map +1 -1
  151. package/build/queries/find/DeferredAlias.cjs.map +1 -1
  152. package/build/queries/find/DeferredAlias.js.map +1 -1
  153. package/build/queries/find/EntityFilter.cjs.map +1 -1
  154. package/build/queries/find/EntityFilter.js.map +1 -1
  155. package/build/queries/find/EntityGraphQLFilter.cjs.map +1 -1
  156. package/build/queries/find/EntityGraphQLFilter.js.map +1 -1
  157. package/build/queries/find/QueryParser.cjs +1 -1
  158. package/build/queries/find/QueryParser.cjs.map +1 -1
  159. package/build/queries/find/QueryParser.collectionJoins.cjs.map +1 -1
  160. package/build/queries/find/QueryParser.collectionJoins.js.map +1 -1
  161. package/build/queries/find/QueryParser.d.cts.map +1 -1
  162. package/build/queries/find/QueryParser.d.mts.map +1 -1
  163. package/build/queries/find/QueryParser.js +1 -1
  164. package/build/queries/find/QueryParser.js.map +1 -1
  165. package/build/queries/find/QueryParser.pruning.cjs.map +1 -1
  166. package/build/queries/find/QueryParser.pruning.js.map +1 -1
  167. package/build/queries/find/QueryVisitor.cjs.map +1 -1
  168. package/build/queries/find/QueryVisitor.js.map +1 -1
  169. package/build/queries/find/buildFindQuery.cjs.map +1 -1
  170. package/build/queries/find/buildFindQuery.js.map +1 -1
  171. package/build/queries/find/scopes.cjs.map +1 -1
  172. package/build/queries/find/scopes.js.map +1 -1
  173. package/build/queries/parsedConditions.cjs.map +1 -1
  174. package/build/queries/parsedConditions.js.map +1 -1
  175. package/build/queries/renderConditions.cjs.map +1 -1
  176. package/build/queries/renderConditions.js.map +1 -1
  177. package/build/queries/skipCondition.cjs.map +1 -1
  178. package/build/queries/skipCondition.js.map +1 -1
  179. package/build/queries/sql/AliasAssigner.cjs.map +1 -1
  180. package/build/queries/sql/AliasAssigner.js.map +1 -1
  181. package/build/queries/sql/Expr.cjs.map +1 -1
  182. package/build/queries/sql/Expr.js.map +1 -1
  183. package/build/queries/sql/Tables.cjs.map +1 -1
  184. package/build/queries/sql/Tables.d.cts.map +1 -1
  185. package/build/queries/sql/Tables.d.mts.map +1 -1
  186. package/build/queries/sql/Tables.js.map +1 -1
  187. package/build/queries/sql/execute.cjs.map +1 -1
  188. package/build/queries/sql/execute.js.map +1 -1
  189. package/build/queries/sql/expressions/arrayAgg.cjs.map +1 -1
  190. package/build/queries/sql/expressions/arrayAgg.js.map +1 -1
  191. package/build/queries/sql/expressions/case.cjs.map +1 -1
  192. package/build/queries/sql/expressions/case.js.map +1 -1
  193. package/build/queries/sql/expressions/coalesce.cjs.map +1 -1
  194. package/build/queries/sql/expressions/coalesce.js.map +1 -1
  195. package/build/queries/sql/expressions/codecs.cjs.map +1 -1
  196. package/build/queries/sql/expressions/codecs.js.map +1 -1
  197. package/build/queries/sql/expressions/expression.cjs.map +1 -1
  198. package/build/queries/sql/expressions/expression.d.cts.map +1 -1
  199. package/build/queries/sql/expressions/expression.d.mts.map +1 -1
  200. package/build/queries/sql/expressions/expression.js.map +1 -1
  201. package/build/queries/sql/expressions/greatest.cjs.map +1 -1
  202. package/build/queries/sql/expressions/greatest.js.map +1 -1
  203. package/build/queries/sql/expressions/least.cjs.map +1 -1
  204. package/build/queries/sql/expressions/least.js.map +1 -1
  205. package/build/queries/sql/expressions/nullIf.cjs.map +1 -1
  206. package/build/queries/sql/expressions/nullIf.js.map +1 -1
  207. package/build/queries/sql/expressions/parseExpression.cjs.map +1 -1
  208. package/build/queries/sql/expressions/parseExpression.js.map +1 -1
  209. package/build/queries/sql/expressions/types.d.cts.map +1 -1
  210. package/build/queries/sql/expressions/types.d.mts.map +1 -1
  211. package/build/queries/sql/query.cjs +1 -1
  212. package/build/queries/sql/query.cjs.map +1 -1
  213. package/build/queries/sql/query.js +1 -1
  214. package/build/queries/sql/query.js.map +1 -1
  215. package/build/queries/unnest.cjs.map +1 -1
  216. package/build/queries/unnest.js.map +1 -1
  217. package/build/queries/valueFilters.cjs.map +1 -1
  218. package/build/queries/valueFilters.d.cts.map +1 -1
  219. package/build/queries/valueFilters.d.mts.map +1 -1
  220. package/build/queries/valueFilters.js.map +1 -1
  221. package/build/reactivity/ReactionsManager.cjs.map +1 -1
  222. package/build/reactivity/ReactionsManager.js.map +1 -1
  223. package/build/reactivity/reactiveHints.cjs.map +1 -1
  224. package/build/reactivity/reactiveHints.js.map +1 -1
  225. package/build/relations/AbstractRelationImpl.cjs.map +1 -1
  226. package/build/relations/AbstractRelationImpl.js.map +1 -1
  227. package/build/relations/AsyncProperty.cjs.map +1 -1
  228. package/build/relations/AsyncProperty.js.map +1 -1
  229. package/build/relations/AsyncReactiveField.cjs.map +1 -1
  230. package/build/relations/AsyncReactiveField.js.map +1 -1
  231. package/build/relations/Collection.cjs.map +1 -1
  232. package/build/relations/Collection.js.map +1 -1
  233. package/build/relations/CustomCollection.cjs.map +1 -1
  234. package/build/relations/CustomCollection.js.map +1 -1
  235. package/build/relations/CustomReference.cjs.map +1 -1
  236. package/build/relations/CustomReference.js.map +1 -1
  237. package/build/relations/EnumCollection.cjs.map +1 -1
  238. package/build/relations/EnumCollection.js.map +1 -1
  239. package/build/relations/LazyField.cjs.map +1 -1
  240. package/build/relations/LazyField.js.map +1 -1
  241. package/build/relations/ManyToManyCollection.cjs.map +1 -1
  242. package/build/relations/ManyToManyCollection.js.map +1 -1
  243. package/build/relations/ManyToManyLargeCollection.cjs.map +1 -1
  244. package/build/relations/ManyToManyLargeCollection.js.map +1 -1
  245. package/build/relations/ManyToOneReference.cjs.map +1 -1
  246. package/build/relations/ManyToOneReference.js.map +1 -1
  247. package/build/relations/OneToManyCollection.cjs.map +1 -1
  248. package/build/relations/OneToManyCollection.js.map +1 -1
  249. package/build/relations/OneToManyLargeCollection.cjs.map +1 -1
  250. package/build/relations/OneToManyLargeCollection.js.map +1 -1
  251. package/build/relations/OneToOneReference.cjs.map +1 -1
  252. package/build/relations/OneToOneReference.js.map +1 -1
  253. package/build/relations/PolymorphicReference.cjs.map +1 -1
  254. package/build/relations/PolymorphicReference.js.map +1 -1
  255. package/build/relations/ReactiveField.cjs.map +1 -1
  256. package/build/relations/ReactiveField.js.map +1 -1
  257. package/build/relations/ReactiveGetter.cjs.map +1 -1
  258. package/build/relations/ReactiveGetter.js.map +1 -1
  259. package/build/relations/ReactiveManyToMany.cjs.map +1 -1
  260. package/build/relations/ReactiveManyToMany.js.map +1 -1
  261. package/build/relations/ReactiveManyToManyOtherSide.cjs.map +1 -1
  262. package/build/relations/ReactiveManyToManyOtherSide.js.map +1 -1
  263. package/build/relations/ReactiveReference.cjs.map +1 -1
  264. package/build/relations/ReactiveReference.js.map +1 -1
  265. package/build/relations/ReadOnlyCollection.cjs.map +1 -1
  266. package/build/relations/ReadOnlyCollection.js.map +1 -1
  267. package/build/relations/RecursiveCollection.cjs.map +1 -1
  268. package/build/relations/RecursiveCollection.js.map +1 -1
  269. package/build/relations/RecursiveCycleError.cjs.map +1 -1
  270. package/build/relations/RecursiveCycleError.js.map +1 -1
  271. package/build/relations/Reference.cjs.map +1 -1
  272. package/build/relations/Reference.js.map +1 -1
  273. package/build/relations/Relation.cjs.map +1 -1
  274. package/build/relations/Relation.js.map +1 -1
  275. package/build/relations/hasAsyncMethod.cjs.map +1 -1
  276. package/build/relations/hasAsyncMethod.js.map +1 -1
  277. package/build/relations/hasManyDerived.cjs.map +1 -1
  278. package/build/relations/hasManyDerived.js.map +1 -1
  279. package/build/relations/hasManyThrough.cjs.map +1 -1
  280. package/build/relations/hasManyThrough.js.map +1 -1
  281. package/build/relations/hasOneDerived.cjs.map +1 -1
  282. package/build/relations/hasOneDerived.js.map +1 -1
  283. package/build/relations/hasOneThrough.cjs.map +1 -1
  284. package/build/relations/hasOneThrough.js.map +1 -1
  285. package/build/relations/hasProperty.cjs.map +1 -1
  286. package/build/relations/hasProperty.js.map +1 -1
  287. package/build/relations/isCascadeDelete.cjs.map +1 -1
  288. package/build/relations/isCascadeDelete.js.map +1 -1
  289. package/build/resurrection.cjs.map +1 -1
  290. package/build/resurrection.js.map +1 -1
  291. package/build/rules.cjs.map +1 -1
  292. package/build/rules.js.map +1 -1
  293. package/build/runtimeConfig.cjs.map +1 -1
  294. package/build/runtimeConfig.js.map +1 -1
  295. package/build/serde/TypeInfo.cjs.map +1 -1
  296. package/build/serde/TypeInfo.js.map +1 -1
  297. package/build/serde/columns.cjs.map +1 -1
  298. package/build/serde/columns.js.map +1 -1
  299. package/build/serde/fieldSerde.cjs.map +1 -1
  300. package/build/serde/fieldSerde.js.map +1 -1
  301. package/build/serde/serde.cjs.map +1 -1
  302. package/build/serde/serde.d.cts.map +1 -1
  303. package/build/serde/serde.d.mts.map +1 -1
  304. package/build/serde/serde.js.map +1 -1
  305. package/build/serde/temporalMappers.cjs.map +1 -1
  306. package/build/serde/temporalMappers.js.map +1 -1
  307. package/build/upsert.cjs.map +1 -1
  308. package/build/upsert.d.cts.map +1 -1
  309. package/build/upsert.d.mts.map +1 -1
  310. package/build/upsert.js.map +1 -1
  311. package/build/utils.cjs.map +1 -1
  312. package/build/utils.js.map +1 -1
  313. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"ReactiveManyToMany.js","names":["#field","#isCached","#loadPromise","#loadedMode","#isLoaded","#doGet","#loadFromJoinTable","#loaded","#filterDeleted","#syncJoinTableRows","#isCascadeDelete"],"sources":["../../src/relations/ReactiveManyToMany.ts"],"sourcesContent":["import { manyToManyBatchLoader } from \"../batchloaders/manyToManyBatchLoader.ts\";\nimport {\n type Entity,\n type EntityMetadata,\n type ManyToManyField,\n type ReadOnlyCollection,\n ensureNotDeleted,\n fail,\n getEmInternalApi,\n getInstanceData,\n getMetadata,\n getMetadataForField,\n isLoaded,\n} from \"../index.ts\";\nimport type { IsLoadedCachable } from \"../loading/IsLoadedCache.ts\";\nimport { lazyField } from \"../newEntity.ts\";\nimport {\n type MaybeReactedEntity,\n type Reacted,\n type ReactiveHint,\n convertToLoadHint,\n} from \"../reactivity/reactiveHints.ts\";\nimport { AbstractRelationImpl } from \"./AbstractRelationImpl.ts\";\nimport { isCascadeDelete } from \"./isCascadeDelete.ts\";\nimport { RelationT, RelationU } from \"./RelationSymbols.ts\";\n\n/**\n * A reactive, derived many-to-many collection.\n *\n * Similar to `ReactiveReference` but for collections--membership is calculated from a reactive\n * function and persisted to the underlying join table.\n */\nexport interface ReactiveManyToMany<T extends Entity, U extends Entity> extends ReadOnlyCollection<T, U> {\n load(opts?: { withDeleted?: boolean; forceReload?: boolean }): Promise<readonly U[]>;\n}\n\n/** Creates a `ReactiveManyToMany`. */\nexport function hasReactiveManyToMany<T extends Entity, U extends Entity, const H extends ReactiveHint<T>>(\n hint: H,\n fn: (entity: Reacted<T, H>) => readonly MaybeReactedEntity<U>[],\n): ReactiveManyToMany<T, U> {\n return lazyField((entity: T, fieldName) => {\n const m2m = getMetadata(entity).allFields[fieldName] as ManyToManyField;\n return new ReactiveManyToManyImpl<T, U, H>(entity, m2m, hint, fn);\n });\n}\n\n/** Implements ReactiveManyToMany, initially a copy/paste of ReactiveReference but for m2m. */\nexport class ReactiveManyToManyImpl<T extends Entity, U extends Entity, H extends ReactiveHint<T>>\n extends AbstractRelationImpl<T, U[]>\n implements ReactiveManyToMany<T, U>, IsLoadedCachable\n{\n readonly #field: ManyToManyField;\n\n // Could be either the ref-loaded stored array, or full-loaded calculated array\n #loaded: U[] | undefined;\n // In ref-mode, we use our materialized FK value (which might be undefined)\n // If full-mode, we calculate the FK value from the subgraph\n #loadedMode: \"ref\" | \"full\" | undefined = undefined;\n // #isLoaded doesn't necessary care if we're ref-mode or full-mode, it's just\n // whether a caller can call `.get` and not have it blow up.\n #isLoaded: boolean | undefined = undefined;\n #isCached: boolean = false;\n #loadPromise: Promise<ReadonlyArray<U>> | undefined;\n\n constructor(\n entity: T,\n field: ManyToManyField,\n public reactiveHint: H,\n private fn: (entity: Reacted<T, H>) => readonly MaybeReactedEntity<U>[],\n ) {\n super(entity);\n this.#field = field;\n (getInstanceData(entity).relations ??= {})[this.fieldName] = this;\n }\n\n async load(opts?: { withDeleted?: boolean; forceReload?: boolean }): Promise<ReadonlyArray<U>> {\n ensureNotDeleted(this.entity, \"pending\");\n const { loadHint } = this;\n\n if (!this.isLoaded || opts?.forceReload) {\n const { em } = this.entity;\n const maybeDirty = opts?.forceReload || getEmInternalApi(em).rm.isMaybePendingRecalc(this.entity, this.fieldName);\n\n if (maybeDirty) {\n this.#isCached = false;\n return (this.#loadPromise ??= em.populate(this.entity, { hint: loadHint, ...opts }).then(() => {\n this.#loadPromise = undefined;\n this.#loadedMode = \"full\";\n this.#isLoaded = true;\n getEmInternalApi(this.entity.em).isLoadedCache.add(this);\n return this.#doGet(opts);\n }));\n } else {\n // Load from join table (existing materialized state)\n return (this.#loadPromise ??= this.#loadFromJoinTable().then((loaded) => {\n this.#loadPromise = undefined;\n this.#loadedMode = \"ref\";\n this.#isLoaded = true;\n this.#loaded = loaded;\n getEmInternalApi(this.entity.em).isLoadedCache.add(this);\n return this.#filterDeleted(loaded, opts);\n }));\n }\n }\n return this.#doGet(opts);\n }\n\n get isLoaded(): boolean {\n if (this.#isLoaded !== undefined) return this.#isLoaded;\n getEmInternalApi(this.entity.em).isLoadedCache.add(this);\n // If a WIP mutation has marked our field as potentially dirty, we need to be \"full\" loaded\n const maybeDirty = getEmInternalApi(this.entity.em).rm.isMaybePendingRecalc(this.entity, this.fieldName);\n if (maybeDirty) {\n // If we're dirty, only being \"full\" loaded is good enough to recalc\n this.#loadedMode = \"full\";\n this.#isLoaded = isLoaded(this.entity, this.loadHint);\n this.#isCached = false;\n } else {\n // If we've had `.load` called before, assume we're still loaded\n this.#isLoaded = !!this.#loadedMode;\n }\n return this.#isLoaded;\n }\n\n resetIsLoaded(): void {\n this.#isLoaded = undefined;\n this.#isCached = false;\n }\n\n #doGet(opts?: { withDeleted?: boolean }): U[] {\n const { fn } = this;\n ensureNotDeleted(this.entity, \"pending\");\n // Fast pass if we've already calculated this (cache invalidation will happen on\n // any mutation via #resetIsLoaded)..\n if (this.#isCached) {\n return this.#filterDeleted(this.#loaded ?? [], opts) as U[];\n }\n // Call isLoaded to probe the load hint, and get `#isLoaded` set, but still have\n // our `if` check the raw `#isLoaded` to know if we should eval-latest or return `loaded`.\n if (this.isLoaded && this.#loadedMode === \"full\") {\n const newValue = fn(this.entity as any) as unknown as U[];\n // Preserve deleted entities that were in the previous loaded value\n const previousDeleted = (this.#loaded ?? []).filter((e) => e.isDeletedEntity);\n for (const entity of previousDeleted) {\n if (!newValue.includes(entity)) newValue.push(entity);\n }\n if (!getEmInternalApi(this.entity.em).isValidating) {\n this.#syncJoinTableRows(newValue);\n }\n this.#loaded = newValue;\n this.#isCached = true;\n getEmInternalApi(this.entity.em).isLoadedCache.add(this);\n } else if (!!this.#loadedMode) {\n // We're either loadedMode=ref, or loadedMode=full (but not actually fully loaded),\n // but in theory loadedMode only gets set in `.load()`, so we should have #loaded\n // set to some value, even if it's not fully up-to-date.\n this.#isCached = true;\n getEmInternalApi(this.entity.em).isLoadedCache.add(this);\n } else {\n const noun = this.entity.isNewEntity ? \"derived\" : \"loaded\";\n throw new Error(`${this.entity}.${this.fieldName} has not been ${noun} yet`);\n }\n return this.#filterDeleted(this.#loaded ?? [], opts) as U[];\n }\n\n get getWithDeleted(): U[] {\n return this.#doGet({ withDeleted: true });\n }\n\n get get(): U[] {\n return this.#doGet({ withDeleted: false });\n }\n\n set(): void {\n fail(`Cannot set ${this.entity}.${this.fieldName} ReactiveManyToMany directly.`);\n }\n\n get loadHint(): any {\n const meta = getMetadata(this.entity);\n return (meta.config.__data.cachedReactiveLoadHints[this.fieldName] ??= convertToLoadHint(meta, this.reactiveHint));\n }\n\n setFromOpts(_others: U[]): void {\n throw new Error(`ReactiveManyToMany ${this.entity}.${this.fieldName} cannot be set via opts`);\n }\n\n maybeCascadeDelete(): void {\n if (this.#isCascadeDelete) {\n const jr = getEmInternalApi(this.entity.em).joinRows(this);\n (jr.getOthers(this.columnName, this.entity) as U[]).forEach((e) => this.entity.em.delete(e));\n }\n }\n\n async cleanupOnEntityDeleted(): Promise<void> {\n const existing = await this.load({ withDeleted: true });\n const jr = getEmInternalApi(this.entity.em).joinRows(this);\n for (const entity of existing) jr.addRemove(this, this.entity, entity);\n this.#loaded = [];\n this.#isLoaded = false;\n }\n\n // Properties needed for JoinRows/ManyToManyCollection compatibility\n get meta(): EntityMetadata {\n return getMetadataForField(getMetadata(this.entity), this.fieldName);\n }\n\n get fieldName(): string {\n return this.#field.fieldName;\n }\n\n get otherMeta(): EntityMetadata {\n return this.#field.otherMetadata();\n }\n\n get joinTableName(): string {\n return this.#field.joinTableName;\n }\n\n get columnName(): string {\n return this.#field.columnNames[0];\n }\n\n get otherColumnName(): string {\n return this.#field.columnNames[1];\n }\n\n get hasJoinTableId(): boolean {\n return this.#field.hasJoinTableId;\n }\n\n get otherFieldName(): string {\n return this.#field.otherFieldName;\n }\n\n get hasBeenSet(): boolean {\n return true;\n }\n\n get isPreloaded(): boolean {\n return false;\n }\n\n preload(): void {\n // No-op for now - could implement if needed\n }\n\n import(\n other: ReactiveManyToManyImpl<T, U, H>,\n _: unknown,\n mapEntities: (e: U[] | undefined) => U[] | undefined,\n ): void {\n this.#loaded = mapEntities(other.#loaded);\n this.#loadedMode = other.#loadedMode;\n this.#isLoaded = other.#isLoaded ?? false;\n // Don't set `#isCached b/c we're not registering ourselves with `isLoadedCache` yet; let\n // the first caller to `#doGet` do that if needed.\n }\n\n public toString(): string {\n return `ReactiveManyToMany(entity: ${this.entity}, fieldName: ${this.fieldName}, otherMeta: ${this.otherMeta.type})`;\n }\n\n get #isCascadeDelete(): boolean {\n return isCascadeDelete(this, this.#field.fieldName);\n }\n\n async #loadFromJoinTable(): Promise<U[]> {\n const { em } = this.entity;\n const key = `${this.columnName}=${this.entity.id}`;\n // Use the existing m2m loader by creating a compatible collection-like object\n await manyToManyBatchLoader(em, this).load(key);\n return getEmInternalApi(em).joinRows(this).getOthers(this.columnName, this.entity) as U[];\n }\n\n // This is like calling `setField` but for a m2m relation...\n #syncJoinTableRows(newValue: U[]): void {\n const jr = getEmInternalApi(this.entity.em).joinRows(this);\n const newSet = new Set(newValue);\n const oldSet = new Set(jr.getOthers(this.columnName, this.entity) as U[]);\n // Find entities to add (in new but not in old)\n for (const entity of newValue) {\n if (!oldSet.has(entity)) {\n jr.addNew(this, this.entity, entity);\n }\n }\n // Find entities to remove (in old but not in new)\n for (const entity of oldSet) {\n if (!newSet.has(entity)) {\n jr.addRemove(this, this.entity, entity);\n }\n }\n }\n\n #filterDeleted(entities: U[], opts?: { withDeleted?: boolean }): U[] {\n return opts?.withDeleted === true\n ? [...entities]\n : entities.filter((e) => !e.isDeletedEntity && !(e as any).isSoftDeletedEntity);\n }\n\n [RelationT]: T = null!;\n [RelationU]: U = null!;\n}\n\n/** Type guard utility for determining if an entity field is a ReactiveManyToMany. */\nexport function isReactiveManyToMany(\n maybeReactiveManyToMany: any,\n): maybeReactiveManyToMany is ReactiveManyToMany<any, any> {\n return maybeReactiveManyToMany instanceof ReactiveManyToManyImpl;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ReactiveManyToMany.js","names":["#field","#isCached","#loadPromise","#loadedMode","#isLoaded","#doGet","#loadFromJoinTable","#loaded","#filterDeleted","#syncJoinTableRows","#isCascadeDelete"],"sources":["../../src/relations/ReactiveManyToMany.ts"],"sourcesContent":["import { manyToManyBatchLoader } from \"src/batchloaders/manyToManyBatchLoader.ts\";\nimport {\n type Entity,\n type EntityMetadata,\n type ManyToManyField,\n type ReadOnlyCollection,\n ensureNotDeleted,\n fail,\n getEmInternalApi,\n getInstanceData,\n getMetadata,\n getMetadataForField,\n isLoaded,\n} from \"src/index.ts\";\nimport type { IsLoadedCachable } from \"src/loading/IsLoadedCache.ts\";\nimport { lazyField } from \"src/newEntity.ts\";\nimport {\n type MaybeReactedEntity,\n type Reacted,\n type ReactiveHint,\n convertToLoadHint,\n} from \"src/reactivity/reactiveHints.ts\";\nimport { AbstractRelationImpl } from \"src/relations/AbstractRelationImpl.ts\";\nimport { isCascadeDelete } from \"src/relations/isCascadeDelete.ts\";\nimport { RelationT, RelationU } from \"src/relations/RelationSymbols.ts\";\n\n/**\n * A reactive, derived many-to-many collection.\n *\n * Similar to `ReactiveReference` but for collections--membership is calculated from a reactive\n * function and persisted to the underlying join table.\n */\nexport interface ReactiveManyToMany<T extends Entity, U extends Entity> extends ReadOnlyCollection<T, U> {\n load(opts?: { withDeleted?: boolean; forceReload?: boolean }): Promise<readonly U[]>;\n}\n\n/** Creates a `ReactiveManyToMany`. */\nexport function hasReactiveManyToMany<T extends Entity, U extends Entity, const H extends ReactiveHint<T>>(\n hint: H,\n fn: (entity: Reacted<T, H>) => readonly MaybeReactedEntity<U>[],\n): ReactiveManyToMany<T, U> {\n return lazyField((entity: T, fieldName) => {\n const m2m = getMetadata(entity).allFields[fieldName] as ManyToManyField;\n return new ReactiveManyToManyImpl<T, U, H>(entity, m2m, hint, fn);\n });\n}\n\n/** Implements ReactiveManyToMany, initially a copy/paste of ReactiveReference but for m2m. */\nexport class ReactiveManyToManyImpl<T extends Entity, U extends Entity, H extends ReactiveHint<T>>\n extends AbstractRelationImpl<T, U[]>\n implements ReactiveManyToMany<T, U>, IsLoadedCachable\n{\n readonly #field: ManyToManyField;\n\n // Could be either the ref-loaded stored array, or full-loaded calculated array\n #loaded: U[] | undefined;\n // In ref-mode, we use our materialized FK value (which might be undefined)\n // If full-mode, we calculate the FK value from the subgraph\n #loadedMode: \"ref\" | \"full\" | undefined = undefined;\n // #isLoaded doesn't necessary care if we're ref-mode or full-mode, it's just\n // whether a caller can call `.get` and not have it blow up.\n #isLoaded: boolean | undefined = undefined;\n #isCached: boolean = false;\n #loadPromise: Promise<ReadonlyArray<U>> | undefined;\n\n constructor(\n entity: T,\n field: ManyToManyField,\n public reactiveHint: H,\n private fn: (entity: Reacted<T, H>) => readonly MaybeReactedEntity<U>[],\n ) {\n super(entity);\n this.#field = field;\n (getInstanceData(entity).relations ??= {})[this.fieldName] = this;\n }\n\n async load(opts?: { withDeleted?: boolean; forceReload?: boolean }): Promise<ReadonlyArray<U>> {\n ensureNotDeleted(this.entity, \"pending\");\n const { loadHint } = this;\n\n if (!this.isLoaded || opts?.forceReload) {\n const { em } = this.entity;\n const maybeDirty = opts?.forceReload || getEmInternalApi(em).rm.isMaybePendingRecalc(this.entity, this.fieldName);\n\n if (maybeDirty) {\n this.#isCached = false;\n return (this.#loadPromise ??= em.populate(this.entity, { hint: loadHint, ...opts }).then(() => {\n this.#loadPromise = undefined;\n this.#loadedMode = \"full\";\n this.#isLoaded = true;\n getEmInternalApi(this.entity.em).isLoadedCache.add(this);\n return this.#doGet(opts);\n }));\n } else {\n // Load from join table (existing materialized state)\n return (this.#loadPromise ??= this.#loadFromJoinTable().then((loaded) => {\n this.#loadPromise = undefined;\n this.#loadedMode = \"ref\";\n this.#isLoaded = true;\n this.#loaded = loaded;\n getEmInternalApi(this.entity.em).isLoadedCache.add(this);\n return this.#filterDeleted(loaded, opts);\n }));\n }\n }\n return this.#doGet(opts);\n }\n\n get isLoaded(): boolean {\n if (this.#isLoaded !== undefined) return this.#isLoaded;\n getEmInternalApi(this.entity.em).isLoadedCache.add(this);\n // If a WIP mutation has marked our field as potentially dirty, we need to be \"full\" loaded\n const maybeDirty = getEmInternalApi(this.entity.em).rm.isMaybePendingRecalc(this.entity, this.fieldName);\n if (maybeDirty) {\n // If we're dirty, only being \"full\" loaded is good enough to recalc\n this.#loadedMode = \"full\";\n this.#isLoaded = isLoaded(this.entity, this.loadHint);\n this.#isCached = false;\n } else {\n // If we've had `.load` called before, assume we're still loaded\n this.#isLoaded = !!this.#loadedMode;\n }\n return this.#isLoaded;\n }\n\n resetIsLoaded(): void {\n this.#isLoaded = undefined;\n this.#isCached = false;\n }\n\n #doGet(opts?: { withDeleted?: boolean }): U[] {\n const { fn } = this;\n ensureNotDeleted(this.entity, \"pending\");\n // Fast pass if we've already calculated this (cache invalidation will happen on\n // any mutation via #resetIsLoaded)..\n if (this.#isCached) {\n return this.#filterDeleted(this.#loaded ?? [], opts) as U[];\n }\n // Call isLoaded to probe the load hint, and get `#isLoaded` set, but still have\n // our `if` check the raw `#isLoaded` to know if we should eval-latest or return `loaded`.\n if (this.isLoaded && this.#loadedMode === \"full\") {\n const newValue = fn(this.entity as any) as unknown as U[];\n // Preserve deleted entities that were in the previous loaded value\n const previousDeleted = (this.#loaded ?? []).filter((e) => e.isDeletedEntity);\n for (const entity of previousDeleted) {\n if (!newValue.includes(entity)) newValue.push(entity);\n }\n if (!getEmInternalApi(this.entity.em).isValidating) {\n this.#syncJoinTableRows(newValue);\n }\n this.#loaded = newValue;\n this.#isCached = true;\n getEmInternalApi(this.entity.em).isLoadedCache.add(this);\n } else if (!!this.#loadedMode) {\n // We're either loadedMode=ref, or loadedMode=full (but not actually fully loaded),\n // but in theory loadedMode only gets set in `.load()`, so we should have #loaded\n // set to some value, even if it's not fully up-to-date.\n this.#isCached = true;\n getEmInternalApi(this.entity.em).isLoadedCache.add(this);\n } else {\n const noun = this.entity.isNewEntity ? \"derived\" : \"loaded\";\n throw new Error(`${this.entity}.${this.fieldName} has not been ${noun} yet`);\n }\n return this.#filterDeleted(this.#loaded ?? [], opts) as U[];\n }\n\n get getWithDeleted(): U[] {\n return this.#doGet({ withDeleted: true });\n }\n\n get get(): U[] {\n return this.#doGet({ withDeleted: false });\n }\n\n set(): void {\n fail(`Cannot set ${this.entity}.${this.fieldName} ReactiveManyToMany directly.`);\n }\n\n get loadHint(): any {\n const meta = getMetadata(this.entity);\n return (meta.config.__data.cachedReactiveLoadHints[this.fieldName] ??= convertToLoadHint(meta, this.reactiveHint));\n }\n\n setFromOpts(_others: U[]): void {\n throw new Error(`ReactiveManyToMany ${this.entity}.${this.fieldName} cannot be set via opts`);\n }\n\n maybeCascadeDelete(): void {\n if (this.#isCascadeDelete) {\n const jr = getEmInternalApi(this.entity.em).joinRows(this);\n (jr.getOthers(this.columnName, this.entity) as U[]).forEach((e) => this.entity.em.delete(e));\n }\n }\n\n async cleanupOnEntityDeleted(): Promise<void> {\n const existing = await this.load({ withDeleted: true });\n const jr = getEmInternalApi(this.entity.em).joinRows(this);\n for (const entity of existing) jr.addRemove(this, this.entity, entity);\n this.#loaded = [];\n this.#isLoaded = false;\n }\n\n // Properties needed for JoinRows/ManyToManyCollection compatibility\n get meta(): EntityMetadata {\n return getMetadataForField(getMetadata(this.entity), this.fieldName);\n }\n\n get fieldName(): string {\n return this.#field.fieldName;\n }\n\n get otherMeta(): EntityMetadata {\n return this.#field.otherMetadata();\n }\n\n get joinTableName(): string {\n return this.#field.joinTableName;\n }\n\n get columnName(): string {\n return this.#field.columnNames[0];\n }\n\n get otherColumnName(): string {\n return this.#field.columnNames[1];\n }\n\n get hasJoinTableId(): boolean {\n return this.#field.hasJoinTableId;\n }\n\n get otherFieldName(): string {\n return this.#field.otherFieldName;\n }\n\n get hasBeenSet(): boolean {\n return true;\n }\n\n get isPreloaded(): boolean {\n return false;\n }\n\n preload(): void {\n // No-op for now - could implement if needed\n }\n\n import(\n other: ReactiveManyToManyImpl<T, U, H>,\n _: unknown,\n mapEntities: (e: U[] | undefined) => U[] | undefined,\n ): void {\n this.#loaded = mapEntities(other.#loaded);\n this.#loadedMode = other.#loadedMode;\n this.#isLoaded = other.#isLoaded ?? false;\n // Don't set `#isCached b/c we're not registering ourselves with `isLoadedCache` yet; let\n // the first caller to `#doGet` do that if needed.\n }\n\n public toString(): string {\n return `ReactiveManyToMany(entity: ${this.entity}, fieldName: ${this.fieldName}, otherMeta: ${this.otherMeta.type})`;\n }\n\n get #isCascadeDelete(): boolean {\n return isCascadeDelete(this, this.#field.fieldName);\n }\n\n async #loadFromJoinTable(): Promise<U[]> {\n const { em } = this.entity;\n const key = `${this.columnName}=${this.entity.id}`;\n // Use the existing m2m loader by creating a compatible collection-like object\n await manyToManyBatchLoader(em, this).load(key);\n return getEmInternalApi(em).joinRows(this).getOthers(this.columnName, this.entity) as U[];\n }\n\n // This is like calling `setField` but for a m2m relation...\n #syncJoinTableRows(newValue: U[]): void {\n const jr = getEmInternalApi(this.entity.em).joinRows(this);\n const newSet = new Set(newValue);\n const oldSet = new Set(jr.getOthers(this.columnName, this.entity) as U[]);\n // Find entities to add (in new but not in old)\n for (const entity of newValue) {\n if (!oldSet.has(entity)) {\n jr.addNew(this, this.entity, entity);\n }\n }\n // Find entities to remove (in old but not in new)\n for (const entity of oldSet) {\n if (!newSet.has(entity)) {\n jr.addRemove(this, this.entity, entity);\n }\n }\n }\n\n #filterDeleted(entities: U[], opts?: { withDeleted?: boolean }): U[] {\n return opts?.withDeleted === true\n ? [...entities]\n : entities.filter((e) => !e.isDeletedEntity && !(e as any).isSoftDeletedEntity);\n }\n\n [RelationT]: T = null!;\n [RelationU]: U = null!;\n}\n\n/** Type guard utility for determining if an entity field is a ReactiveManyToMany. */\nexport function isReactiveManyToMany(\n maybeReactiveManyToMany: any,\n): maybeReactiveManyToMany is ReactiveManyToMany<any, any> {\n return maybeReactiveManyToMany instanceof ReactiveManyToManyImpl;\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ReactiveManyToManyOtherSide.cjs","names":["lazyField","getMetadata","AbstractRelationImpl","#field","#isLoaded","#isCached","#loadPromise","#loadFromJoinTable","#loaded","#doGet","#cached","getMetadataForField","#applyPendingChanges","#filterDeleted","manyToManyBatchLoader","getEmInternalApi","RelationT","RelationU"],"sources":["../../src/relations/ReactiveManyToManyOtherSide.ts"],"sourcesContent":["import { manyToManyBatchLoader } from \"../batchloaders/manyToManyBatchLoader.ts\";\nimport type { ManyToManyLike } from \"../flush/JoinRows.ts\";\nimport {\n type Entity,\n type EntityMetadata,\n type ManyToManyField,\n type ReadOnlyCollection,\n ensureNotDeleted,\n getEmInternalApi,\n getInstanceData,\n getMetadata,\n getMetadataForField,\n} from \"../index.ts\";\nimport type { IsLoadedCachable } from \"../loading/IsLoadedCache.ts\";\nimport { lazyField } from \"../newEntity.ts\";\nimport { AbstractRelationImpl } from \"./AbstractRelationImpl.ts\";\nimport { RelationT, RelationU } from \"./RelationSymbols.ts\";\n\n/**\n * A read-only collection representing the \"other side\" of a ReactiveManyToMany.\n *\n * I.e. when `Author.bestReviews` is a `ReactiveManyToMany` (controlling side), `BookReview.bestReviewAuthors`\n * the `ReactiveManyToManyOtherSide` (read-only view).\n */\nexport interface ReactiveManyToManyOtherSide<T extends Entity, U extends Entity> extends ReadOnlyCollection<T, U> {}\n\n/** Creates a ReactiveManyToManyOtherSide, the read-only other side of a ReactiveManyToMany. */\nexport function hasReactiveManyToManyOtherSide<T extends Entity, U extends Entity>(): ReactiveManyToManyOtherSide<\n T,\n U\n> {\n return lazyField((entity: T, fieldName) => {\n const m2m = getMetadata(entity).allFields[fieldName] as ManyToManyField;\n return new ReactiveManyToManyOtherSideImpl<T, U>(entity, m2m);\n });\n}\n\nexport class ReactiveManyToManyOtherSideImpl<T extends Entity, U extends Entity>\n extends AbstractRelationImpl<T, U[]>\n implements ReactiveManyToManyOtherSide<T, U>, ManyToManyLike, IsLoadedCachable\n{\n #field: ManyToManyField;\n // Loading state\n #loaded: U[] | undefined;\n #isLoaded: boolean = false;\n #loadPromise: Promise<readonly U[]> | undefined;\n // Cached result of applyPendingChanges (without deleted filtering)\n #cached: U[] | undefined;\n #isCached: boolean = false;\n\n constructor(entity: T, field: ManyToManyField) {\n super(entity);\n this.#field = field;\n (getInstanceData(entity).relations ??= {})[field.fieldName] = this;\n }\n\n async load(opts?: { withDeleted?: boolean; forceReload?: boolean }): Promise<readonly U[]> {\n ensureNotDeleted(this.entity, \"pending\");\n if (!this.#isLoaded || opts?.forceReload) {\n this.#isCached = false;\n return (this.#loadPromise ??= this.#loadFromJoinTable().then((loaded) => {\n this.#loadPromise = undefined;\n this.#loaded = loaded;\n this.#isLoaded = true;\n getEmInternalApi(this.entity.em).isLoadedCache.addNaive(this);\n return this.#doGet(opts);\n }));\n }\n return this.#doGet(opts);\n }\n\n get isLoaded(): boolean {\n return this.#isLoaded;\n }\n\n resetIsLoaded(): void {\n this.#isCached = false;\n }\n\n get get(): U[] {\n return this.#doGet({ withDeleted: false });\n }\n\n get getWithDeleted(): U[] {\n return this.#doGet({ withDeleted: true });\n }\n\n set(): void {\n throw new Error(`ReactiveManyToManyOtherSide ${this.entity}.${this.fieldName} cannot be set`);\n }\n\n setFromOpts(_others: U[]): void {\n throw new Error(`ReactiveManyToManyOtherSide ${this.entity}.${this.fieldName} cannot be set via opts`);\n }\n\n maybeCascadeDelete(): void {\n // Don't cascade - the controlling side handles this\n }\n\n async cleanupOnEntityDeleted(): Promise<void> {\n this.#loaded = [];\n this.#cached = undefined;\n this.#isCached = false;\n this.#isLoaded = false;\n }\n\n get fieldName(): string {\n return this.#field.fieldName;\n }\n\n // Properties for JoinRows/ManyToManyCollection compatibility\n get meta(): EntityMetadata {\n return getMetadataForField(getMetadata(this.entity), this.fieldName);\n }\n\n get otherMeta(): EntityMetadata {\n return this.#field.otherMetadata();\n }\n\n get joinTableName(): string {\n return this.#field.joinTableName;\n }\n\n get columnName(): string {\n return this.#field.columnNames[0];\n }\n\n get otherColumnName(): string {\n return this.#field.columnNames[1];\n }\n\n get hasJoinTableId(): boolean {\n return this.#field.hasJoinTableId;\n }\n\n get otherFieldName(): string {\n return this.#field.otherFieldName;\n }\n\n get hasBeenSet(): boolean {\n return false;\n }\n\n get isPreloaded(): boolean {\n return false;\n }\n\n preload(): void {\n // No-op for reactive m2m other side\n }\n\n import(\n other: ReactiveManyToManyOtherSideImpl<T, U>,\n _: unknown,\n mapEntities: (e: U[] | undefined) => U[] | undefined,\n ): void {\n this.#loaded = mapEntities(other.#loaded);\n this.#isLoaded = other.#isLoaded;\n // Don't copy #cached/#isCached since it assumes we're registered in `isLoaded`, let #doGet do that\n }\n\n public toString(): string {\n return `ReactiveManyToManyOtherSide(entity: ${this.entity}, fieldName: ${this.fieldName}, otherMeta: ${this.otherMeta.type})`;\n }\n\n #doGet(opts?: { withDeleted?: boolean }): U[] {\n ensureNotDeleted(this.entity, \"pending\");\n if (!this.#isLoaded) {\n throw new Error(`${this.entity}.${this.fieldName} has not been loaded yet`);\n }\n if (!this.#isCached) {\n this.#cached = this.#applyPendingChanges(this.#loaded ?? []);\n this.#isCached = true;\n getEmInternalApi(this.entity.em).isLoadedCache.addNaive(this);\n }\n return this.#filterDeleted(this.#cached!, opts);\n }\n\n async #loadFromJoinTable(): Promise<U[]> {\n const { em } = this.entity;\n const key = `${this.columnName}=${this.entity.id}`;\n await manyToManyBatchLoader(em, this).load(key);\n return getEmInternalApi(em).joinRows(this).getOthers(this.columnName, this.entity) as U[];\n }\n\n /** Apply pending changes from the controlling side. */\n #applyPendingChanges(baseEntities: U[]): U[] {\n const result = new Set(baseEntities);\n const jr = getEmInternalApi(this.entity.em).joinRows(this);\n const added = jr.addedFor(this, this.entity);\n for (const other of added) result.add(other as U);\n const removed = jr.removedFor(this, this.entity);\n for (const other of removed) result.delete(other as U);\n return [...result];\n }\n\n #filterDeleted(entities: U[], opts?: { withDeleted?: boolean }): U[] {\n if (opts?.withDeleted === true) {\n return entities;\n }\n return entities.filter((e) => !e.isDeletedEntity && !(e as any).isSoftDeletedEntity);\n }\n\n [RelationT]: T = null!;\n [RelationU]: U = null!;\n}\n\nexport function isReactiveManyToManyOtherSide(\n maybeCollection: any,\n): maybeCollection is ReactiveManyToManyOtherSide<any, any> {\n return maybeCollection instanceof ReactiveManyToManyOtherSideImpl;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ReactiveManyToManyOtherSide.cjs","names":["lazyField","getMetadata","AbstractRelationImpl","#field","#isLoaded","#isCached","#loadPromise","#loadFromJoinTable","#loaded","#doGet","#cached","getMetadataForField","#applyPendingChanges","#filterDeleted","manyToManyBatchLoader","getEmInternalApi","RelationT","RelationU"],"sources":["../../src/relations/ReactiveManyToManyOtherSide.ts"],"sourcesContent":["import { manyToManyBatchLoader } from \"src/batchloaders/manyToManyBatchLoader.ts\";\nimport type { ManyToManyLike } from \"src/flush/JoinRows.ts\";\nimport {\n type Entity,\n type EntityMetadata,\n type ManyToManyField,\n type ReadOnlyCollection,\n ensureNotDeleted,\n getEmInternalApi,\n getInstanceData,\n getMetadata,\n getMetadataForField,\n} from \"src/index.ts\";\nimport type { IsLoadedCachable } from \"src/loading/IsLoadedCache.ts\";\nimport { lazyField } from \"src/newEntity.ts\";\nimport { AbstractRelationImpl } from \"src/relations/AbstractRelationImpl.ts\";\nimport { RelationT, RelationU } from \"src/relations/RelationSymbols.ts\";\n\n/**\n * A read-only collection representing the \"other side\" of a ReactiveManyToMany.\n *\n * I.e. when `Author.bestReviews` is a `ReactiveManyToMany` (controlling side), `BookReview.bestReviewAuthors`\n * the `ReactiveManyToManyOtherSide` (read-only view).\n */\nexport interface ReactiveManyToManyOtherSide<T extends Entity, U extends Entity> extends ReadOnlyCollection<T, U> {}\n\n/** Creates a ReactiveManyToManyOtherSide, the read-only other side of a ReactiveManyToMany. */\nexport function hasReactiveManyToManyOtherSide<T extends Entity, U extends Entity>(): ReactiveManyToManyOtherSide<\n T,\n U\n> {\n return lazyField((entity: T, fieldName) => {\n const m2m = getMetadata(entity).allFields[fieldName] as ManyToManyField;\n return new ReactiveManyToManyOtherSideImpl<T, U>(entity, m2m);\n });\n}\n\nexport class ReactiveManyToManyOtherSideImpl<T extends Entity, U extends Entity>\n extends AbstractRelationImpl<T, U[]>\n implements ReactiveManyToManyOtherSide<T, U>, ManyToManyLike, IsLoadedCachable\n{\n #field: ManyToManyField;\n // Loading state\n #loaded: U[] | undefined;\n #isLoaded: boolean = false;\n #loadPromise: Promise<readonly U[]> | undefined;\n // Cached result of applyPendingChanges (without deleted filtering)\n #cached: U[] | undefined;\n #isCached: boolean = false;\n\n constructor(entity: T, field: ManyToManyField) {\n super(entity);\n this.#field = field;\n (getInstanceData(entity).relations ??= {})[field.fieldName] = this;\n }\n\n async load(opts?: { withDeleted?: boolean; forceReload?: boolean }): Promise<readonly U[]> {\n ensureNotDeleted(this.entity, \"pending\");\n if (!this.#isLoaded || opts?.forceReload) {\n this.#isCached = false;\n return (this.#loadPromise ??= this.#loadFromJoinTable().then((loaded) => {\n this.#loadPromise = undefined;\n this.#loaded = loaded;\n this.#isLoaded = true;\n getEmInternalApi(this.entity.em).isLoadedCache.addNaive(this);\n return this.#doGet(opts);\n }));\n }\n return this.#doGet(opts);\n }\n\n get isLoaded(): boolean {\n return this.#isLoaded;\n }\n\n resetIsLoaded(): void {\n this.#isCached = false;\n }\n\n get get(): U[] {\n return this.#doGet({ withDeleted: false });\n }\n\n get getWithDeleted(): U[] {\n return this.#doGet({ withDeleted: true });\n }\n\n set(): void {\n throw new Error(`ReactiveManyToManyOtherSide ${this.entity}.${this.fieldName} cannot be set`);\n }\n\n setFromOpts(_others: U[]): void {\n throw new Error(`ReactiveManyToManyOtherSide ${this.entity}.${this.fieldName} cannot be set via opts`);\n }\n\n maybeCascadeDelete(): void {\n // Don't cascade - the controlling side handles this\n }\n\n async cleanupOnEntityDeleted(): Promise<void> {\n this.#loaded = [];\n this.#cached = undefined;\n this.#isCached = false;\n this.#isLoaded = false;\n }\n\n get fieldName(): string {\n return this.#field.fieldName;\n }\n\n // Properties for JoinRows/ManyToManyCollection compatibility\n get meta(): EntityMetadata {\n return getMetadataForField(getMetadata(this.entity), this.fieldName);\n }\n\n get otherMeta(): EntityMetadata {\n return this.#field.otherMetadata();\n }\n\n get joinTableName(): string {\n return this.#field.joinTableName;\n }\n\n get columnName(): string {\n return this.#field.columnNames[0];\n }\n\n get otherColumnName(): string {\n return this.#field.columnNames[1];\n }\n\n get hasJoinTableId(): boolean {\n return this.#field.hasJoinTableId;\n }\n\n get otherFieldName(): string {\n return this.#field.otherFieldName;\n }\n\n get hasBeenSet(): boolean {\n return false;\n }\n\n get isPreloaded(): boolean {\n return false;\n }\n\n preload(): void {\n // No-op for reactive m2m other side\n }\n\n import(\n other: ReactiveManyToManyOtherSideImpl<T, U>,\n _: unknown,\n mapEntities: (e: U[] | undefined) => U[] | undefined,\n ): void {\n this.#loaded = mapEntities(other.#loaded);\n this.#isLoaded = other.#isLoaded;\n // Don't copy #cached/#isCached since it assumes we're registered in `isLoaded`, let #doGet do that\n }\n\n public toString(): string {\n return `ReactiveManyToManyOtherSide(entity: ${this.entity}, fieldName: ${this.fieldName}, otherMeta: ${this.otherMeta.type})`;\n }\n\n #doGet(opts?: { withDeleted?: boolean }): U[] {\n ensureNotDeleted(this.entity, \"pending\");\n if (!this.#isLoaded) {\n throw new Error(`${this.entity}.${this.fieldName} has not been loaded yet`);\n }\n if (!this.#isCached) {\n this.#cached = this.#applyPendingChanges(this.#loaded ?? []);\n this.#isCached = true;\n getEmInternalApi(this.entity.em).isLoadedCache.addNaive(this);\n }\n return this.#filterDeleted(this.#cached!, opts);\n }\n\n async #loadFromJoinTable(): Promise<U[]> {\n const { em } = this.entity;\n const key = `${this.columnName}=${this.entity.id}`;\n await manyToManyBatchLoader(em, this).load(key);\n return getEmInternalApi(em).joinRows(this).getOthers(this.columnName, this.entity) as U[];\n }\n\n /** Apply pending changes from the controlling side. */\n #applyPendingChanges(baseEntities: U[]): U[] {\n const result = new Set(baseEntities);\n const jr = getEmInternalApi(this.entity.em).joinRows(this);\n const added = jr.addedFor(this, this.entity);\n for (const other of added) result.add(other as U);\n const removed = jr.removedFor(this, this.entity);\n for (const other of removed) result.delete(other as U);\n return [...result];\n }\n\n #filterDeleted(entities: U[], opts?: { withDeleted?: boolean }): U[] {\n if (opts?.withDeleted === true) {\n return entities;\n }\n return entities.filter((e) => !e.isDeletedEntity && !(e as any).isSoftDeletedEntity);\n }\n\n [RelationT]: T = null!;\n [RelationU]: U = null!;\n}\n\nexport function isReactiveManyToManyOtherSide(\n maybeCollection: any,\n): maybeCollection is ReactiveManyToManyOtherSide<any, any> {\n return maybeCollection instanceof ReactiveManyToManyOtherSideImpl;\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ReactiveManyToManyOtherSide.js","names":["#field","#isLoaded","#isCached","#loadPromise","#loadFromJoinTable","#loaded","#doGet","#cached","#applyPendingChanges","#filterDeleted"],"sources":["../../src/relations/ReactiveManyToManyOtherSide.ts"],"sourcesContent":["import { manyToManyBatchLoader } from \"../batchloaders/manyToManyBatchLoader.ts\";\nimport type { ManyToManyLike } from \"../flush/JoinRows.ts\";\nimport {\n type Entity,\n type EntityMetadata,\n type ManyToManyField,\n type ReadOnlyCollection,\n ensureNotDeleted,\n getEmInternalApi,\n getInstanceData,\n getMetadata,\n getMetadataForField,\n} from \"../index.ts\";\nimport type { IsLoadedCachable } from \"../loading/IsLoadedCache.ts\";\nimport { lazyField } from \"../newEntity.ts\";\nimport { AbstractRelationImpl } from \"./AbstractRelationImpl.ts\";\nimport { RelationT, RelationU } from \"./RelationSymbols.ts\";\n\n/**\n * A read-only collection representing the \"other side\" of a ReactiveManyToMany.\n *\n * I.e. when `Author.bestReviews` is a `ReactiveManyToMany` (controlling side), `BookReview.bestReviewAuthors`\n * the `ReactiveManyToManyOtherSide` (read-only view).\n */\nexport interface ReactiveManyToManyOtherSide<T extends Entity, U extends Entity> extends ReadOnlyCollection<T, U> {}\n\n/** Creates a ReactiveManyToManyOtherSide, the read-only other side of a ReactiveManyToMany. */\nexport function hasReactiveManyToManyOtherSide<T extends Entity, U extends Entity>(): ReactiveManyToManyOtherSide<\n T,\n U\n> {\n return lazyField((entity: T, fieldName) => {\n const m2m = getMetadata(entity).allFields[fieldName] as ManyToManyField;\n return new ReactiveManyToManyOtherSideImpl<T, U>(entity, m2m);\n });\n}\n\nexport class ReactiveManyToManyOtherSideImpl<T extends Entity, U extends Entity>\n extends AbstractRelationImpl<T, U[]>\n implements ReactiveManyToManyOtherSide<T, U>, ManyToManyLike, IsLoadedCachable\n{\n #field: ManyToManyField;\n // Loading state\n #loaded: U[] | undefined;\n #isLoaded: boolean = false;\n #loadPromise: Promise<readonly U[]> | undefined;\n // Cached result of applyPendingChanges (without deleted filtering)\n #cached: U[] | undefined;\n #isCached: boolean = false;\n\n constructor(entity: T, field: ManyToManyField) {\n super(entity);\n this.#field = field;\n (getInstanceData(entity).relations ??= {})[field.fieldName] = this;\n }\n\n async load(opts?: { withDeleted?: boolean; forceReload?: boolean }): Promise<readonly U[]> {\n ensureNotDeleted(this.entity, \"pending\");\n if (!this.#isLoaded || opts?.forceReload) {\n this.#isCached = false;\n return (this.#loadPromise ??= this.#loadFromJoinTable().then((loaded) => {\n this.#loadPromise = undefined;\n this.#loaded = loaded;\n this.#isLoaded = true;\n getEmInternalApi(this.entity.em).isLoadedCache.addNaive(this);\n return this.#doGet(opts);\n }));\n }\n return this.#doGet(opts);\n }\n\n get isLoaded(): boolean {\n return this.#isLoaded;\n }\n\n resetIsLoaded(): void {\n this.#isCached = false;\n }\n\n get get(): U[] {\n return this.#doGet({ withDeleted: false });\n }\n\n get getWithDeleted(): U[] {\n return this.#doGet({ withDeleted: true });\n }\n\n set(): void {\n throw new Error(`ReactiveManyToManyOtherSide ${this.entity}.${this.fieldName} cannot be set`);\n }\n\n setFromOpts(_others: U[]): void {\n throw new Error(`ReactiveManyToManyOtherSide ${this.entity}.${this.fieldName} cannot be set via opts`);\n }\n\n maybeCascadeDelete(): void {\n // Don't cascade - the controlling side handles this\n }\n\n async cleanupOnEntityDeleted(): Promise<void> {\n this.#loaded = [];\n this.#cached = undefined;\n this.#isCached = false;\n this.#isLoaded = false;\n }\n\n get fieldName(): string {\n return this.#field.fieldName;\n }\n\n // Properties for JoinRows/ManyToManyCollection compatibility\n get meta(): EntityMetadata {\n return getMetadataForField(getMetadata(this.entity), this.fieldName);\n }\n\n get otherMeta(): EntityMetadata {\n return this.#field.otherMetadata();\n }\n\n get joinTableName(): string {\n return this.#field.joinTableName;\n }\n\n get columnName(): string {\n return this.#field.columnNames[0];\n }\n\n get otherColumnName(): string {\n return this.#field.columnNames[1];\n }\n\n get hasJoinTableId(): boolean {\n return this.#field.hasJoinTableId;\n }\n\n get otherFieldName(): string {\n return this.#field.otherFieldName;\n }\n\n get hasBeenSet(): boolean {\n return false;\n }\n\n get isPreloaded(): boolean {\n return false;\n }\n\n preload(): void {\n // No-op for reactive m2m other side\n }\n\n import(\n other: ReactiveManyToManyOtherSideImpl<T, U>,\n _: unknown,\n mapEntities: (e: U[] | undefined) => U[] | undefined,\n ): void {\n this.#loaded = mapEntities(other.#loaded);\n this.#isLoaded = other.#isLoaded;\n // Don't copy #cached/#isCached since it assumes we're registered in `isLoaded`, let #doGet do that\n }\n\n public toString(): string {\n return `ReactiveManyToManyOtherSide(entity: ${this.entity}, fieldName: ${this.fieldName}, otherMeta: ${this.otherMeta.type})`;\n }\n\n #doGet(opts?: { withDeleted?: boolean }): U[] {\n ensureNotDeleted(this.entity, \"pending\");\n if (!this.#isLoaded) {\n throw new Error(`${this.entity}.${this.fieldName} has not been loaded yet`);\n }\n if (!this.#isCached) {\n this.#cached = this.#applyPendingChanges(this.#loaded ?? []);\n this.#isCached = true;\n getEmInternalApi(this.entity.em).isLoadedCache.addNaive(this);\n }\n return this.#filterDeleted(this.#cached!, opts);\n }\n\n async #loadFromJoinTable(): Promise<U[]> {\n const { em } = this.entity;\n const key = `${this.columnName}=${this.entity.id}`;\n await manyToManyBatchLoader(em, this).load(key);\n return getEmInternalApi(em).joinRows(this).getOthers(this.columnName, this.entity) as U[];\n }\n\n /** Apply pending changes from the controlling side. */\n #applyPendingChanges(baseEntities: U[]): U[] {\n const result = new Set(baseEntities);\n const jr = getEmInternalApi(this.entity.em).joinRows(this);\n const added = jr.addedFor(this, this.entity);\n for (const other of added) result.add(other as U);\n const removed = jr.removedFor(this, this.entity);\n for (const other of removed) result.delete(other as U);\n return [...result];\n }\n\n #filterDeleted(entities: U[], opts?: { withDeleted?: boolean }): U[] {\n if (opts?.withDeleted === true) {\n return entities;\n }\n return entities.filter((e) => !e.isDeletedEntity && !(e as any).isSoftDeletedEntity);\n }\n\n [RelationT]: T = null!;\n [RelationU]: U = null!;\n}\n\nexport function isReactiveManyToManyOtherSide(\n maybeCollection: any,\n): maybeCollection is ReactiveManyToManyOtherSide<any, any> {\n return maybeCollection instanceof ReactiveManyToManyOtherSideImpl;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ReactiveManyToManyOtherSide.js","names":["#field","#isLoaded","#isCached","#loadPromise","#loadFromJoinTable","#loaded","#doGet","#cached","#applyPendingChanges","#filterDeleted"],"sources":["../../src/relations/ReactiveManyToManyOtherSide.ts"],"sourcesContent":["import { manyToManyBatchLoader } from \"src/batchloaders/manyToManyBatchLoader.ts\";\nimport type { ManyToManyLike } from \"src/flush/JoinRows.ts\";\nimport {\n type Entity,\n type EntityMetadata,\n type ManyToManyField,\n type ReadOnlyCollection,\n ensureNotDeleted,\n getEmInternalApi,\n getInstanceData,\n getMetadata,\n getMetadataForField,\n} from \"src/index.ts\";\nimport type { IsLoadedCachable } from \"src/loading/IsLoadedCache.ts\";\nimport { lazyField } from \"src/newEntity.ts\";\nimport { AbstractRelationImpl } from \"src/relations/AbstractRelationImpl.ts\";\nimport { RelationT, RelationU } from \"src/relations/RelationSymbols.ts\";\n\n/**\n * A read-only collection representing the \"other side\" of a ReactiveManyToMany.\n *\n * I.e. when `Author.bestReviews` is a `ReactiveManyToMany` (controlling side), `BookReview.bestReviewAuthors`\n * the `ReactiveManyToManyOtherSide` (read-only view).\n */\nexport interface ReactiveManyToManyOtherSide<T extends Entity, U extends Entity> extends ReadOnlyCollection<T, U> {}\n\n/** Creates a ReactiveManyToManyOtherSide, the read-only other side of a ReactiveManyToMany. */\nexport function hasReactiveManyToManyOtherSide<T extends Entity, U extends Entity>(): ReactiveManyToManyOtherSide<\n T,\n U\n> {\n return lazyField((entity: T, fieldName) => {\n const m2m = getMetadata(entity).allFields[fieldName] as ManyToManyField;\n return new ReactiveManyToManyOtherSideImpl<T, U>(entity, m2m);\n });\n}\n\nexport class ReactiveManyToManyOtherSideImpl<T extends Entity, U extends Entity>\n extends AbstractRelationImpl<T, U[]>\n implements ReactiveManyToManyOtherSide<T, U>, ManyToManyLike, IsLoadedCachable\n{\n #field: ManyToManyField;\n // Loading state\n #loaded: U[] | undefined;\n #isLoaded: boolean = false;\n #loadPromise: Promise<readonly U[]> | undefined;\n // Cached result of applyPendingChanges (without deleted filtering)\n #cached: U[] | undefined;\n #isCached: boolean = false;\n\n constructor(entity: T, field: ManyToManyField) {\n super(entity);\n this.#field = field;\n (getInstanceData(entity).relations ??= {})[field.fieldName] = this;\n }\n\n async load(opts?: { withDeleted?: boolean; forceReload?: boolean }): Promise<readonly U[]> {\n ensureNotDeleted(this.entity, \"pending\");\n if (!this.#isLoaded || opts?.forceReload) {\n this.#isCached = false;\n return (this.#loadPromise ??= this.#loadFromJoinTable().then((loaded) => {\n this.#loadPromise = undefined;\n this.#loaded = loaded;\n this.#isLoaded = true;\n getEmInternalApi(this.entity.em).isLoadedCache.addNaive(this);\n return this.#doGet(opts);\n }));\n }\n return this.#doGet(opts);\n }\n\n get isLoaded(): boolean {\n return this.#isLoaded;\n }\n\n resetIsLoaded(): void {\n this.#isCached = false;\n }\n\n get get(): U[] {\n return this.#doGet({ withDeleted: false });\n }\n\n get getWithDeleted(): U[] {\n return this.#doGet({ withDeleted: true });\n }\n\n set(): void {\n throw new Error(`ReactiveManyToManyOtherSide ${this.entity}.${this.fieldName} cannot be set`);\n }\n\n setFromOpts(_others: U[]): void {\n throw new Error(`ReactiveManyToManyOtherSide ${this.entity}.${this.fieldName} cannot be set via opts`);\n }\n\n maybeCascadeDelete(): void {\n // Don't cascade - the controlling side handles this\n }\n\n async cleanupOnEntityDeleted(): Promise<void> {\n this.#loaded = [];\n this.#cached = undefined;\n this.#isCached = false;\n this.#isLoaded = false;\n }\n\n get fieldName(): string {\n return this.#field.fieldName;\n }\n\n // Properties for JoinRows/ManyToManyCollection compatibility\n get meta(): EntityMetadata {\n return getMetadataForField(getMetadata(this.entity), this.fieldName);\n }\n\n get otherMeta(): EntityMetadata {\n return this.#field.otherMetadata();\n }\n\n get joinTableName(): string {\n return this.#field.joinTableName;\n }\n\n get columnName(): string {\n return this.#field.columnNames[0];\n }\n\n get otherColumnName(): string {\n return this.#field.columnNames[1];\n }\n\n get hasJoinTableId(): boolean {\n return this.#field.hasJoinTableId;\n }\n\n get otherFieldName(): string {\n return this.#field.otherFieldName;\n }\n\n get hasBeenSet(): boolean {\n return false;\n }\n\n get isPreloaded(): boolean {\n return false;\n }\n\n preload(): void {\n // No-op for reactive m2m other side\n }\n\n import(\n other: ReactiveManyToManyOtherSideImpl<T, U>,\n _: unknown,\n mapEntities: (e: U[] | undefined) => U[] | undefined,\n ): void {\n this.#loaded = mapEntities(other.#loaded);\n this.#isLoaded = other.#isLoaded;\n // Don't copy #cached/#isCached since it assumes we're registered in `isLoaded`, let #doGet do that\n }\n\n public toString(): string {\n return `ReactiveManyToManyOtherSide(entity: ${this.entity}, fieldName: ${this.fieldName}, otherMeta: ${this.otherMeta.type})`;\n }\n\n #doGet(opts?: { withDeleted?: boolean }): U[] {\n ensureNotDeleted(this.entity, \"pending\");\n if (!this.#isLoaded) {\n throw new Error(`${this.entity}.${this.fieldName} has not been loaded yet`);\n }\n if (!this.#isCached) {\n this.#cached = this.#applyPendingChanges(this.#loaded ?? []);\n this.#isCached = true;\n getEmInternalApi(this.entity.em).isLoadedCache.addNaive(this);\n }\n return this.#filterDeleted(this.#cached!, opts);\n }\n\n async #loadFromJoinTable(): Promise<U[]> {\n const { em } = this.entity;\n const key = `${this.columnName}=${this.entity.id}`;\n await manyToManyBatchLoader(em, this).load(key);\n return getEmInternalApi(em).joinRows(this).getOthers(this.columnName, this.entity) as U[];\n }\n\n /** Apply pending changes from the controlling side. */\n #applyPendingChanges(baseEntities: U[]): U[] {\n const result = new Set(baseEntities);\n const jr = getEmInternalApi(this.entity.em).joinRows(this);\n const added = jr.addedFor(this, this.entity);\n for (const other of added) result.add(other as U);\n const removed = jr.removedFor(this, this.entity);\n for (const other of removed) result.delete(other as U);\n return [...result];\n }\n\n #filterDeleted(entities: U[], opts?: { withDeleted?: boolean }): U[] {\n if (opts?.withDeleted === true) {\n return entities;\n }\n return entities.filter((e) => !e.isDeletedEntity && !(e as any).isSoftDeletedEntity);\n }\n\n [RelationT]: T = null!;\n [RelationU]: U = null!;\n}\n\nexport function isReactiveManyToManyOtherSide(\n maybeCollection: any,\n): maybeCollection is ReactiveManyToManyOtherSide<any, any> {\n return maybeCollection instanceof ReactiveManyToManyOtherSideImpl;\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ReactiveReference.cjs","names":["lazyField","getMetadata","AbstractRelationImpl","#fieldName","#otherMeta","#state","getEmInternalApi","#loadPromise","#isLoaded","#isCached","isEntity","isLoaded","getField","sameEntity","maybeResolveReferenceToId","failNoId","failIfNewEntity","convertToLoadHint","toIdOf","deTagId","isCascadeDelete","RelationT","RelationU","ReferenceN","#rr","#loaded"],"sources":["../../src/relations/ReactiveReference.ts"],"sourcesContent":["import { type Entity } from \"../Entity.ts\";\nimport { type IdOf } from \"../EntityManager.ts\";\nimport { getField, setField } from \"../fields.ts\";\nimport {\n type EntityMetadata,\n type ManyToOneField,\n type TaggedId,\n deTagId,\n ensureNotDeleted,\n fail,\n getEmInternalApi,\n getInstanceData,\n getMetadata,\n isEntity,\n isLoaded,\n maybeResolveReferenceToId,\n sameEntity,\n toIdOf,\n} from \"../index.ts\";\nimport type { IsLoadedCachable } from \"../loading/IsLoadedCache.ts\";\nimport { lazyField } from \"../newEntity.ts\";\nimport {\n type MaybeReactedEntity,\n type Reacted,\n type ReactiveHint,\n convertToLoadHint,\n} from \"../reactivity/reactiveHints.ts\";\nimport { AbstractRelationImpl } from \"./AbstractRelationImpl.ts\";\nimport { isCascadeDelete } from \"./isCascadeDelete.ts\";\nimport { failIfNewEntity, failNoId } from \"./ManyToOneReference.ts\";\nimport { type Reference } from \"./Reference.ts\";\nimport { ReferenceN } from \"./ReferenceSymbols.ts\";\nimport { RelationT, RelationU } from \"./RelationSymbols.ts\";\n\nexport interface ReactiveReference<T extends Entity, U extends Entity, N extends never | undefined> extends Reference<\n T,\n U,\n N\n> {\n isLoaded: boolean;\n isSet: boolean;\n\n load(opts?: { withDeleted?: boolean; forceReload?: true }): Promise<U | N>;\n\n /**\n * Returns the as-of-last-flush previously-calculated entity.\n *\n * This is useful if you have to purposefully avoid using the lambda to calc the latest entity,\n * i.e. if you're in a test and want to watch a calculated entity change from some dummy entity\n * to the new derived entity.\n * */\n fieldValue: U | N;\n\n /** Returns the id of the current assigned entity, or a runtime error if either 1) unset or 2) set to a new entity that doesn't have an `id` yet. */\n id: IdOf<U>;\n\n /** Returns the id of the current assigned entity, undefined if unset, or a runtime error if set to a new entity. */\n idIfSet: IdOf<U> | undefined;\n\n idUntagged: string;\n\n idUntaggedIfSet: string | undefined;\n}\n\nexport function hasReactiveReference<T extends Entity, U extends Entity, const H extends ReactiveHint<T>>(\n hint: H,\n fn: (entity: Reacted<T, H>) => MaybeReactedEntity<U>,\n): ReactiveReference<T, U, never>;\nexport function hasReactiveReference<T extends Entity, U extends Entity, const H extends ReactiveHint<T>>(\n hint: H,\n fn: (entity: Reacted<T, H>) => MaybeReactedEntity<U> | undefined,\n): ReactiveReference<T, U, undefined>;\n/** Creates a `ReactiveReference`. */\nexport function hasReactiveReference<\n T extends Entity,\n U extends Entity,\n const H extends ReactiveHint<T>,\n N extends never | undefined,\n>(hint: H, fn: (entity: Reacted<T, H>) => MaybeReactedEntity<U> | N): ReactiveReference<T, U, N> {\n return lazyField((entity: T, fieldName) => {\n const m2o = getMetadata(entity).allFields[fieldName] as ManyToOneField;\n return new ReactiveReferenceImpl<T, U, H, N>(entity, m2o, hint, fn);\n });\n}\n\n/**\n * Implements ReactiveReferences.\n *\n * This is a bit trickier than ReactiveFields, because we have to differentiate our loaded\n * between:\n *\n * - our load hint is loaded, and we can directly calculate the referenced entity, or\n * - we've fetched the calculated/stored entity from the database, but not purposefully\n * not re-calculating it (which would require fetching the load hint, which might be\n * expensive, and defeat the point of having our field materialized in the database)\n *\n * Further, want to support all of:\n *\n * - When loaded & `.get` has been called, we cache the value\n *\n * - If a WIP mutation happens, we invalidate `.get` *but* if we check `.isLoaded`,\n * we can return the live/updated value, without an `await`.\n *\n * - If a WIP mutation happens, we invalidate `.isLoaded`, and if we're *no longer loaded*,\n * we want to keep returning the stale value (as asserted by the tests), but the next\n * `load` / `populate` should load the graph & recalculate the value.\n */\nexport class ReactiveReferenceImpl<\n T extends Entity,\n U extends Entity,\n H extends ReactiveHint<T>,\n N extends never | undefined,\n>\n extends AbstractRelationImpl<T, U>\n implements ReactiveReference<T, U, N>, IsLoadedCachable\n{\n readonly #fieldName: keyof T & string;\n readonly #otherMeta: EntityMetadata;\n #state: RRState<U, N>;\n // #isLoaded doesn't necessary care if we're ref-mode or full-mode, it's just\n // whether a caller can call `.get` and not have it blow up.\n #isLoaded: boolean | undefined = undefined;\n #isCached: boolean = false;\n #loadPromise: Promise<U | N> | undefined;\n\n constructor(\n entity: T,\n field: ManyToOneField,\n public reactiveHint: H,\n private fn: (entity: Reacted<T, H>) => MaybeReactedEntity<U> | N,\n ) {\n super(entity);\n // Minimizing changes until the other PR lands...\n this.#fieldName = field.fieldName as keyof T & string;\n this.#otherMeta = field.otherMetadata();\n this.#state = new RRUnloadedState<U, N>(this);\n // We can be initialized with [entity | id | undefined], and if it's entity or id, then setImpl\n // will set loaded appropriately; but if we're initialized undefined, then mark loaded here\n if (entity.isNewEntity) {\n // const isLoaded = isEntity(this.current()) ? true : undefined;\n }\n // If the property transformer is not enabled, then the RR will not be in the relations map because they are\n // defined in the implementation file and not the codegen file. So we should insert ourselves regardless.\n (getInstanceData(entity).relations ??= {})[field.fieldName] = this;\n }\n\n async load(opts?: { withDeleted?: boolean; forceReload?: true }): Promise<U | N> {\n ensureNotDeleted(this.entity, \"pending\");\n const { loadHint } = this;\n if (!this.isLoaded || opts?.forceReload) {\n const { em } = this.entity;\n // Just because we're not loaded, doesn't mean we necessarily need to load our full hint.\n // We prefer to load only our previously-calculated/materialized value, and only load the\n // full load hint if we need recalculated.\n const maybeDirty = opts?.forceReload || getEmInternalApi(em).rm.isMaybePendingRecalc(this.entity, this.fieldName);\n if (maybeDirty) {\n return (this.#loadPromise ??= em\n .populate(this.entity, { hint: loadHint, ...opts })\n .then(() => {\n this.#state = this.#state.enterRecalcMode(this);\n this.#isLoaded = true;\n getEmInternalApi(this.entity.em).isLoadedCache.add(this);\n // Go through `this.get` so that `setField` is called to set our latest value\n this.#isCached = false;\n return this.doGet(opts);\n })\n .finally(() => {\n this.#loadPromise = undefined;\n }));\n } else {\n // If we don't need a full recalc, just make sure we have the entity in memory\n // ...ideally we would check `isSet` and whether the key was in the `instanceData`\n const current = this.current();\n if (isEntity(current) || current === undefined) {\n this.#state = new RRRefState<U, N>(this, current as U | N | undefined);\n this.#isLoaded = true;\n getEmInternalApi(this.entity.em).isLoadedCache.add(this);\n return current as U | N;\n } else {\n return (this.#loadPromise ??= em\n .load(this.#otherMeta.cstr, current)\n .then((loaded) => {\n this.#state = new RRRefState<U, N>(this, loaded);\n this.#isLoaded = true;\n getEmInternalApi(this.entity.em).isLoadedCache.add(this);\n return loaded;\n })\n .finally(() => {\n this.#loadPromise = undefined;\n }));\n }\n }\n }\n // The ReactiveField.load() always sets `#isCached = false`, but our assertion is that our\n // IsLoadedCache/resetIsLoaded infra is good enough to \"see all invalidating mutations\",\n // so we don't need this explicit reset.\n // this.#isCached = false;\n return this.doGet(opts);\n }\n\n get isLoaded(): boolean {\n // If we've cached an isLoaded value, just use that, see https://github.com/joist-orm/joist-orm/issues/1166\n if (this.#isLoaded !== undefined) return this.#isLoaded;\n getEmInternalApi(this.entity.em).isLoadedCache.add(this);\n // If a WIP mutation has marked our field as potentially dirty, we need to be \"full\" loaded\n const maybeDirty = getEmInternalApi(this.entity.em).rm.isMaybePendingRecalc(this.entity, this.fieldName);\n if (maybeDirty) {\n // If we're dirty, only being \"full\" loaded is good enough to recalc\n this.#state = this.#state.enterRecalcMode(this);\n this.#isLoaded = isLoaded(this.entity, this.loadHint);\n this.#isCached = false;\n } else {\n // If we've had `.load` called before, assume we're still loaded\n this.#isLoaded = this.#state.canReturnValue;\n }\n return this.#isLoaded;\n }\n\n resetIsLoaded(): void {\n this.#isLoaded = undefined;\n this.#isCached = false;\n }\n\n private doGet(opts?: { withDeleted?: boolean }): U | N {\n const { fn } = this;\n ensureNotDeleted(this.entity, \"pending\");\n // Fast pass if we've already calculated this (cache invalidation will happen on\n // any mutation via #resetIsLoaded)..\n if (this.#isCached) {\n return this.filterDeleted(this.#state.doGet() as U | N, opts);\n }\n // Call isLoaded to probe the load hint, and get `#isLoaded` set, but still have\n // our `if` check the raw `#isLoaded` to know if we should eval-latest or return `loaded`.\n if (this.isLoaded && this.#state.kind === \"full\") {\n const newValue = this.filterDeleted(fn(this.entity as any) as any, opts);\n // It's cheap to set this every time we're called, i.e. even if it's not the\n // official \"being called during em.flush\" update (...unless we're accessing it\n // during the validate phase of `em.flush`, then skip it to avoid tripping up\n // the \"cannot change entities during flush\" logic.)\n if (!getEmInternalApi(this.entity.em).isValidating) {\n this.setImpl(newValue);\n }\n this.#state = new RRFullState<U, N>(this, newValue);\n this.#isCached = true;\n getEmInternalApi(this.entity.em).isLoadedCache.add(this);\n } else if (this.#state.canReturnValue) {\n // We're either in ref mode, or in full mode but not actually fully loaded,\n // but in theory those states only get set after `.load()` / `.preload()` / import,\n // so we should have some stored value, even if it's not fully up-to-date.\n this.#isCached = true;\n getEmInternalApi(this.entity.em).isLoadedCache.add(this);\n } else {\n const noun = this.entity.isNewEntity ? \"derived\" : \"loaded\";\n throw new Error(`${this.entity}.${this.fieldName} has not been ${noun} yet`);\n }\n return this.filterDeleted(this.#state.doGet() as U | N, opts);\n }\n\n get fieldValue(): U | N {\n return getField(this.entity, this.fieldName);\n }\n\n get getWithDeleted(): U | N {\n return this.doGet({ withDeleted: true });\n }\n\n get get(): U | N {\n return this.doGet({ withDeleted: false });\n }\n\n set(_: U | N): void {\n fail(`Cannot set ${this.entity}.${this.fieldName} ReactiveReference directly.`);\n }\n\n get isSet(): boolean {\n return this.current() !== undefined;\n }\n\n // Internal method used by OneToManyCollection\n setImpl(entity: U | N): void {\n ensureNotDeleted(this.entity, \"pending\");\n if (sameEntity(entity, this.current({ withDeleted: true }))) {\n return;\n }\n // Prefer to keep the id in our data hash, but if this is a new entity w/o an id, use the entity itself\n setField(this.entity, this.fieldName, isEntity(entity) ? (entity.idTaggedMaybe ?? entity) : entity);\n }\n\n get isPreloaded(): boolean {\n return !!this.maybeFindEntity();\n }\n\n preload(): void {\n this.#state = new RRRefState<U, N>(this, this.maybeFindEntity());\n this.#isLoaded = true;\n this.#isCached = true;\n }\n\n private unload(): void {\n this.#state = new RRUnloadedState<U, N>(this);\n this.#isLoaded = false;\n this.#isCached = false;\n this.#loadPromise = undefined;\n }\n\n import(other: ReactiveReferenceImpl<T, U, H, N>, findEntity: (e: U) => U): void {\n this.#state = other.#state.import(this, findEntity);\n this.#isLoaded = true;\n this.#isCached = true;\n }\n\n /** Returns the tagged id of the current value. */\n get idTaggedMaybe(): TaggedId | N {\n ensureNotDeleted(this.entity, \"pending\");\n return maybeResolveReferenceToId(this.current()) as TaggedId | N;\n }\n\n get id(): IdOf<U> {\n return this.idMaybe || failNoId(this.entity, this.fieldName, this.current());\n }\n\n get idUntagged(): string {\n return this.idUntaggedMaybe || failNoId(this.entity, this.fieldName, this.current());\n }\n\n get idIfSet(): IdOf<U> | N | undefined {\n return this.idMaybe || failIfNewEntity(this.entity, this.fieldName, this.current());\n }\n\n get idUntaggedIfSet(): string | undefined {\n return this.idUntaggedMaybe || failIfNewEntity(this.entity, this.fieldName, this.current());\n }\n\n get loadHint(): any {\n const meta = getMetadata(this.entity);\n return (meta.config.__data.cachedReactiveLoadHints[this.fieldName] ??= convertToLoadHint(meta, this.reactiveHint));\n }\n\n // private impl\n\n /** Returns the id of the current value. */\n private get idMaybe(): IdOf<U> | undefined {\n ensureNotDeleted(this.entity, \"pending\");\n return toIdOf(this.otherMeta, this.idTaggedMaybe);\n }\n\n private get idUntaggedMaybe(): string | undefined {\n return deTagId(this.otherMeta, this.idMaybe);\n }\n\n setFromOpts(_: U | IdOf<U> | N): void {\n throw new Error(`ReactiveReference ${this.entity}.${this.fieldName} cannot be set via opts`);\n }\n\n maybeCascadeDelete(): void {\n if (this.isCascadeDelete) {\n const current = this.current({ withDeleted: true });\n if (current !== undefined && typeof current !== \"string\") {\n this.entity.em.delete(current as U);\n }\n }\n }\n\n async cleanupOnEntityDeleted(): Promise<void> {\n // if we are going to delete this relation as well, then we don't need to clean it up\n if (this.isCascadeDelete) return;\n setField(this.entity, this.fieldName, undefined);\n this.#state = new RRRefState<U, N>(this, undefined as N);\n this.#isLoaded = false;\n this.#isCached = false;\n this.#loadPromise = undefined;\n }\n\n // We need to keep U in data[fieldName] to handle entities without an id assigned yet.\n current(opts?: { withDeleted?: boolean }): U | string | N {\n const current = getField(this.entity, this.fieldName);\n if (current !== undefined && isEntity(current)) {\n return this.filterDeleted(current as U, opts);\n }\n return current;\n }\n\n get fieldName(): string {\n return this.#fieldName;\n }\n\n public get otherMeta(): EntityMetadata<U> {\n return (getMetadata(this.entity).allFields[this.#fieldName] as ManyToOneField).otherMetadata();\n }\n\n public get hasBeenSet(): boolean {\n return true;\n }\n\n public toString(): string {\n return `ReactiveReference(entity: ${this.entity}, hint: ${this.loadHint}, fieldName: ${this.fieldName}, otherMeta: {\n this.otherMeta.type\n }, id: ${this.id})`;\n }\n\n /**\n * Removes pending-hard-delete (but not soft-deleted), unless explicitly asked for.\n *\n * Note that we leave soft-deleted entities b/c the call signature of a `book.author.get`\n * very likely does not expect a soft-deleted entity to result in `undefined`.\n *\n * (Contrasted with `author.books.get` which is more intuitive to have soft-deleted\n * entities filtered out, i.e. it doesn't fundamentally change the return type.)\n */\n private filterDeleted(entity: U | N, opts?: { withDeleted?: boolean }): U | N {\n if (entity && (!opts || !opts.withDeleted) && entity.isDeletedEntity) {\n return undefined!;\n }\n return entity;\n }\n\n private get isCascadeDelete(): boolean {\n return isCascadeDelete(this, this.#fieldName);\n }\n\n /**\n * Looks for an entity in `EntityManager`, b/c we may have it in memory even if\n * our reference is not specifically loaded.\n */\n maybeFindEntity(): U | N {\n // Check this.loaded first b/c a new entity won't have an id yet\n if (this.#state.canReturnValue) {\n const loaded = this.#state.doGet();\n if (loaded !== undefined) {\n return loaded;\n }\n }\n const { idTaggedMaybe } = this;\n return idTaggedMaybe !== undefined ? (this.entity.em.getEntity(idTaggedMaybe) as U | N) : (undefined as N);\n }\n\n [RelationT]: T = null!;\n [RelationU]: U = null!;\n [ReferenceN]: N = null!;\n}\n\n/** Type guard utility for determining if an entity field is a ReactiveReference. */\nexport function isReactiveReference(maybeReactiveRef: any): maybeReactiveRef is ReactiveReference<any, any, any> {\n return maybeReactiveRef instanceof ReactiveReferenceImpl;\n}\n\ninterface RRState<U extends Entity, N extends never | undefined> {\n readonly kind: \"unloaded\" | \"ref\" | \"full\";\n readonly canReturnValue: boolean;\n doGet(): U | N | undefined;\n /**\n * Switches from \"materialized ref value\" semantics to \"recalc from reactive graph when available\" semantics.\n *\n * This is synchronous because it does not mean the load hint is fully loaded yet. It only means this\n * reference should now behave as recalc-capable: if `isLoaded(loadHint)` later reports true, `doGet`\n * will recalculate from `fn(...)`; otherwise we keep returning the last known/stale value until `load()`\n * populates the graph.\n */\n enterRecalcMode(target: ReactiveReferenceImpl<any, any, any, any>): RRFullState<U, N>;\n import(target: ReactiveReferenceImpl<any, any, any, any>, findEntity: (e: U) => U): RRState<U, N>;\n}\n\nclass RRUnloadedState<U extends Entity, N extends never | undefined> implements RRState<U, N> {\n readonly kind = \"unloaded\";\n readonly canReturnValue = false;\n #rr: ReactiveReferenceImpl<any, any, any, any>;\n\n constructor(rr: ReactiveReferenceImpl<any, any, any, any>) {\n this.#rr = rr;\n }\n\n doGet(): U | N | undefined {\n throw new Error(`${this.#rr.entity}.${this.#rr.fieldName} was not loaded`);\n }\n\n enterRecalcMode(target: ReactiveReferenceImpl<any, any, any, any>): RRFullState<U, N> {\n return new RRFullState<U, N>(target, undefined);\n }\n\n import(target: ReactiveReferenceImpl<any, any, any, any>, _findEntity: (e: U) => U): RRState<U, N> {\n return new RRUnloadedState<U, N>(target);\n }\n}\n\nclass RRRefState<U extends Entity, N extends never | undefined> implements RRState<U, N> {\n readonly kind = \"ref\";\n readonly canReturnValue = true;\n #loaded: U | N | undefined;\n\n constructor(_rr: ReactiveReferenceImpl<any, any, any, any>, loaded: U | N | undefined) {\n this.#loaded = loaded;\n }\n\n doGet(): U | N | undefined {\n return this.#loaded;\n }\n\n enterRecalcMode(target: ReactiveReferenceImpl<any, any, any, any>): RRFullState<U, N> {\n return new RRFullState<U, N>(target, this.#loaded);\n }\n\n import(target: ReactiveReferenceImpl<any, any, any, any>, findEntity: (e: U) => U): RRState<U, N> {\n return new RRRefState<U, N>(target, mapReactiveReferenceEntity(this.#loaded, findEntity));\n }\n}\n\nclass RRFullState<U extends Entity, N extends never | undefined> implements RRState<U, N> {\n readonly kind = \"full\";\n readonly canReturnValue = true;\n #loaded: U | N | undefined;\n\n constructor(_rr: ReactiveReferenceImpl<any, any, any, any>, loaded: U | N | undefined) {\n this.#loaded = loaded;\n }\n\n doGet(): U | N | undefined {\n return this.#loaded;\n }\n\n enterRecalcMode(_target: ReactiveReferenceImpl<any, any, any, any>): RRFullState<U, N> {\n return this;\n }\n\n import(target: ReactiveReferenceImpl<any, any, any, any>, findEntity: (e: U) => U): RRState<U, N> {\n return new RRRefState<U, N>(target, mapReactiveReferenceEntity(this.#loaded, findEntity));\n }\n}\n\nfunction mapReactiveReferenceEntity<U extends Entity, N extends never | undefined>(\n entity: U | N | undefined,\n findEntity: (e: U) => U,\n): U | N | undefined {\n return isEntity(entity) ? findEntity(entity) : entity;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ReactiveReference.cjs","names":["lazyField","getMetadata","AbstractRelationImpl","#fieldName","#otherMeta","#state","getEmInternalApi","#loadPromise","#isLoaded","#isCached","isEntity","isLoaded","getField","sameEntity","maybeResolveReferenceToId","failNoId","failIfNewEntity","convertToLoadHint","toIdOf","deTagId","isCascadeDelete","RelationT","RelationU","ReferenceN","#rr","#loaded"],"sources":["../../src/relations/ReactiveReference.ts"],"sourcesContent":["import { type Entity } from \"src/Entity.ts\";\nimport { type IdOf } from \"src/EntityManager.ts\";\nimport { getField, setField } from \"src/fields.ts\";\nimport {\n type EntityMetadata,\n type ManyToOneField,\n type TaggedId,\n deTagId,\n ensureNotDeleted,\n fail,\n getEmInternalApi,\n getInstanceData,\n getMetadata,\n isEntity,\n isLoaded,\n maybeResolveReferenceToId,\n sameEntity,\n toIdOf,\n} from \"src/index.ts\";\nimport type { IsLoadedCachable } from \"src/loading/IsLoadedCache.ts\";\nimport { lazyField } from \"src/newEntity.ts\";\nimport {\n type MaybeReactedEntity,\n type Reacted,\n type ReactiveHint,\n convertToLoadHint,\n} from \"src/reactivity/reactiveHints.ts\";\nimport { AbstractRelationImpl } from \"src/relations/AbstractRelationImpl.ts\";\nimport { isCascadeDelete } from \"src/relations/isCascadeDelete.ts\";\nimport { failIfNewEntity, failNoId } from \"src/relations/ManyToOneReference.ts\";\nimport { type Reference } from \"src/relations/Reference.ts\";\nimport { ReferenceN } from \"src/relations/ReferenceSymbols.ts\";\nimport { RelationT, RelationU } from \"src/relations/RelationSymbols.ts\";\n\nexport interface ReactiveReference<T extends Entity, U extends Entity, N extends never | undefined> extends Reference<\n T,\n U,\n N\n> {\n isLoaded: boolean;\n isSet: boolean;\n\n load(opts?: { withDeleted?: boolean; forceReload?: true }): Promise<U | N>;\n\n /**\n * Returns the as-of-last-flush previously-calculated entity.\n *\n * This is useful if you have to purposefully avoid using the lambda to calc the latest entity,\n * i.e. if you're in a test and want to watch a calculated entity change from some dummy entity\n * to the new derived entity.\n * */\n fieldValue: U | N;\n\n /** Returns the id of the current assigned entity, or a runtime error if either 1) unset or 2) set to a new entity that doesn't have an `id` yet. */\n id: IdOf<U>;\n\n /** Returns the id of the current assigned entity, undefined if unset, or a runtime error if set to a new entity. */\n idIfSet: IdOf<U> | undefined;\n\n idUntagged: string;\n\n idUntaggedIfSet: string | undefined;\n}\n\nexport function hasReactiveReference<T extends Entity, U extends Entity, const H extends ReactiveHint<T>>(\n hint: H,\n fn: (entity: Reacted<T, H>) => MaybeReactedEntity<U>,\n): ReactiveReference<T, U, never>;\nexport function hasReactiveReference<T extends Entity, U extends Entity, const H extends ReactiveHint<T>>(\n hint: H,\n fn: (entity: Reacted<T, H>) => MaybeReactedEntity<U> | undefined,\n): ReactiveReference<T, U, undefined>;\n/** Creates a `ReactiveReference`. */\nexport function hasReactiveReference<\n T extends Entity,\n U extends Entity,\n const H extends ReactiveHint<T>,\n N extends never | undefined,\n>(hint: H, fn: (entity: Reacted<T, H>) => MaybeReactedEntity<U> | N): ReactiveReference<T, U, N> {\n return lazyField((entity: T, fieldName) => {\n const m2o = getMetadata(entity).allFields[fieldName] as ManyToOneField;\n return new ReactiveReferenceImpl<T, U, H, N>(entity, m2o, hint, fn);\n });\n}\n\n/**\n * Implements ReactiveReferences.\n *\n * This is a bit trickier than ReactiveFields, because we have to differentiate our loaded\n * between:\n *\n * - our load hint is loaded, and we can directly calculate the referenced entity, or\n * - we've fetched the calculated/stored entity from the database, but not purposefully\n * not re-calculating it (which would require fetching the load hint, which might be\n * expensive, and defeat the point of having our field materialized in the database)\n *\n * Further, want to support all of:\n *\n * - When loaded & `.get` has been called, we cache the value\n *\n * - If a WIP mutation happens, we invalidate `.get` *but* if we check `.isLoaded`,\n * we can return the live/updated value, without an `await`.\n *\n * - If a WIP mutation happens, we invalidate `.isLoaded`, and if we're *no longer loaded*,\n * we want to keep returning the stale value (as asserted by the tests), but the next\n * `load` / `populate` should load the graph & recalculate the value.\n */\nexport class ReactiveReferenceImpl<\n T extends Entity,\n U extends Entity,\n H extends ReactiveHint<T>,\n N extends never | undefined,\n>\n extends AbstractRelationImpl<T, U>\n implements ReactiveReference<T, U, N>, IsLoadedCachable\n{\n readonly #fieldName: keyof T & string;\n readonly #otherMeta: EntityMetadata;\n #state: RRState<U, N>;\n // #isLoaded doesn't necessary care if we're ref-mode or full-mode, it's just\n // whether a caller can call `.get` and not have it blow up.\n #isLoaded: boolean | undefined = undefined;\n #isCached: boolean = false;\n #loadPromise: Promise<U | N> | undefined;\n\n constructor(\n entity: T,\n field: ManyToOneField,\n public reactiveHint: H,\n private fn: (entity: Reacted<T, H>) => MaybeReactedEntity<U> | N,\n ) {\n super(entity);\n // Minimizing changes until the other PR lands...\n this.#fieldName = field.fieldName as keyof T & string;\n this.#otherMeta = field.otherMetadata();\n this.#state = new RRUnloadedState<U, N>(this);\n // We can be initialized with [entity | id | undefined], and if it's entity or id, then setImpl\n // will set loaded appropriately; but if we're initialized undefined, then mark loaded here\n if (entity.isNewEntity) {\n // const isLoaded = isEntity(this.current()) ? true : undefined;\n }\n // If the property transformer is not enabled, then the RR will not be in the relations map because they are\n // defined in the implementation file and not the codegen file. So we should insert ourselves regardless.\n (getInstanceData(entity).relations ??= {})[field.fieldName] = this;\n }\n\n async load(opts?: { withDeleted?: boolean; forceReload?: true }): Promise<U | N> {\n ensureNotDeleted(this.entity, \"pending\");\n const { loadHint } = this;\n if (!this.isLoaded || opts?.forceReload) {\n const { em } = this.entity;\n // Just because we're not loaded, doesn't mean we necessarily need to load our full hint.\n // We prefer to load only our previously-calculated/materialized value, and only load the\n // full load hint if we need recalculated.\n const maybeDirty = opts?.forceReload || getEmInternalApi(em).rm.isMaybePendingRecalc(this.entity, this.fieldName);\n if (maybeDirty) {\n return (this.#loadPromise ??= em\n .populate(this.entity, { hint: loadHint, ...opts })\n .then(() => {\n this.#state = this.#state.enterRecalcMode(this);\n this.#isLoaded = true;\n getEmInternalApi(this.entity.em).isLoadedCache.add(this);\n // Go through `this.get` so that `setField` is called to set our latest value\n this.#isCached = false;\n return this.doGet(opts);\n })\n .finally(() => {\n this.#loadPromise = undefined;\n }));\n } else {\n // If we don't need a full recalc, just make sure we have the entity in memory\n // ...ideally we would check `isSet` and whether the key was in the `instanceData`\n const current = this.current();\n if (isEntity(current) || current === undefined) {\n this.#state = new RRRefState<U, N>(this, current as U | N | undefined);\n this.#isLoaded = true;\n getEmInternalApi(this.entity.em).isLoadedCache.add(this);\n return current as U | N;\n } else {\n return (this.#loadPromise ??= em\n .load(this.#otherMeta.cstr, current)\n .then((loaded) => {\n this.#state = new RRRefState<U, N>(this, loaded);\n this.#isLoaded = true;\n getEmInternalApi(this.entity.em).isLoadedCache.add(this);\n return loaded;\n })\n .finally(() => {\n this.#loadPromise = undefined;\n }));\n }\n }\n }\n // The ReactiveField.load() always sets `#isCached = false`, but our assertion is that our\n // IsLoadedCache/resetIsLoaded infra is good enough to \"see all invalidating mutations\",\n // so we don't need this explicit reset.\n // this.#isCached = false;\n return this.doGet(opts);\n }\n\n get isLoaded(): boolean {\n // If we've cached an isLoaded value, just use that, see https://github.com/joist-orm/joist-orm/issues/1166\n if (this.#isLoaded !== undefined) return this.#isLoaded;\n getEmInternalApi(this.entity.em).isLoadedCache.add(this);\n // If a WIP mutation has marked our field as potentially dirty, we need to be \"full\" loaded\n const maybeDirty = getEmInternalApi(this.entity.em).rm.isMaybePendingRecalc(this.entity, this.fieldName);\n if (maybeDirty) {\n // If we're dirty, only being \"full\" loaded is good enough to recalc\n this.#state = this.#state.enterRecalcMode(this);\n this.#isLoaded = isLoaded(this.entity, this.loadHint);\n this.#isCached = false;\n } else {\n // If we've had `.load` called before, assume we're still loaded\n this.#isLoaded = this.#state.canReturnValue;\n }\n return this.#isLoaded;\n }\n\n resetIsLoaded(): void {\n this.#isLoaded = undefined;\n this.#isCached = false;\n }\n\n private doGet(opts?: { withDeleted?: boolean }): U | N {\n const { fn } = this;\n ensureNotDeleted(this.entity, \"pending\");\n // Fast pass if we've already calculated this (cache invalidation will happen on\n // any mutation via #resetIsLoaded)..\n if (this.#isCached) {\n return this.filterDeleted(this.#state.doGet() as U | N, opts);\n }\n // Call isLoaded to probe the load hint, and get `#isLoaded` set, but still have\n // our `if` check the raw `#isLoaded` to know if we should eval-latest or return `loaded`.\n if (this.isLoaded && this.#state.kind === \"full\") {\n const newValue = this.filterDeleted(fn(this.entity as any) as any, opts);\n // It's cheap to set this every time we're called, i.e. even if it's not the\n // official \"being called during em.flush\" update (...unless we're accessing it\n // during the validate phase of `em.flush`, then skip it to avoid tripping up\n // the \"cannot change entities during flush\" logic.)\n if (!getEmInternalApi(this.entity.em).isValidating) {\n this.setImpl(newValue);\n }\n this.#state = new RRFullState<U, N>(this, newValue);\n this.#isCached = true;\n getEmInternalApi(this.entity.em).isLoadedCache.add(this);\n } else if (this.#state.canReturnValue) {\n // We're either in ref mode, or in full mode but not actually fully loaded,\n // but in theory those states only get set after `.load()` / `.preload()` / import,\n // so we should have some stored value, even if it's not fully up-to-date.\n this.#isCached = true;\n getEmInternalApi(this.entity.em).isLoadedCache.add(this);\n } else {\n const noun = this.entity.isNewEntity ? \"derived\" : \"loaded\";\n throw new Error(`${this.entity}.${this.fieldName} has not been ${noun} yet`);\n }\n return this.filterDeleted(this.#state.doGet() as U | N, opts);\n }\n\n get fieldValue(): U | N {\n return getField(this.entity, this.fieldName);\n }\n\n get getWithDeleted(): U | N {\n return this.doGet({ withDeleted: true });\n }\n\n get get(): U | N {\n return this.doGet({ withDeleted: false });\n }\n\n set(_: U | N): void {\n fail(`Cannot set ${this.entity}.${this.fieldName} ReactiveReference directly.`);\n }\n\n get isSet(): boolean {\n return this.current() !== undefined;\n }\n\n // Internal method used by OneToManyCollection\n setImpl(entity: U | N): void {\n ensureNotDeleted(this.entity, \"pending\");\n if (sameEntity(entity, this.current({ withDeleted: true }))) {\n return;\n }\n // Prefer to keep the id in our data hash, but if this is a new entity w/o an id, use the entity itself\n setField(this.entity, this.fieldName, isEntity(entity) ? (entity.idTaggedMaybe ?? entity) : entity);\n }\n\n get isPreloaded(): boolean {\n return !!this.maybeFindEntity();\n }\n\n preload(): void {\n this.#state = new RRRefState<U, N>(this, this.maybeFindEntity());\n this.#isLoaded = true;\n this.#isCached = true;\n }\n\n private unload(): void {\n this.#state = new RRUnloadedState<U, N>(this);\n this.#isLoaded = false;\n this.#isCached = false;\n this.#loadPromise = undefined;\n }\n\n import(other: ReactiveReferenceImpl<T, U, H, N>, findEntity: (e: U) => U): void {\n this.#state = other.#state.import(this, findEntity);\n this.#isLoaded = true;\n this.#isCached = true;\n }\n\n /** Returns the tagged id of the current value. */\n get idTaggedMaybe(): TaggedId | N {\n ensureNotDeleted(this.entity, \"pending\");\n return maybeResolveReferenceToId(this.current()) as TaggedId | N;\n }\n\n get id(): IdOf<U> {\n return this.idMaybe || failNoId(this.entity, this.fieldName, this.current());\n }\n\n get idUntagged(): string {\n return this.idUntaggedMaybe || failNoId(this.entity, this.fieldName, this.current());\n }\n\n get idIfSet(): IdOf<U> | N | undefined {\n return this.idMaybe || failIfNewEntity(this.entity, this.fieldName, this.current());\n }\n\n get idUntaggedIfSet(): string | undefined {\n return this.idUntaggedMaybe || failIfNewEntity(this.entity, this.fieldName, this.current());\n }\n\n get loadHint(): any {\n const meta = getMetadata(this.entity);\n return (meta.config.__data.cachedReactiveLoadHints[this.fieldName] ??= convertToLoadHint(meta, this.reactiveHint));\n }\n\n // private impl\n\n /** Returns the id of the current value. */\n private get idMaybe(): IdOf<U> | undefined {\n ensureNotDeleted(this.entity, \"pending\");\n return toIdOf(this.otherMeta, this.idTaggedMaybe);\n }\n\n private get idUntaggedMaybe(): string | undefined {\n return deTagId(this.otherMeta, this.idMaybe);\n }\n\n setFromOpts(_: U | IdOf<U> | N): void {\n throw new Error(`ReactiveReference ${this.entity}.${this.fieldName} cannot be set via opts`);\n }\n\n maybeCascadeDelete(): void {\n if (this.isCascadeDelete) {\n const current = this.current({ withDeleted: true });\n if (current !== undefined && typeof current !== \"string\") {\n this.entity.em.delete(current as U);\n }\n }\n }\n\n async cleanupOnEntityDeleted(): Promise<void> {\n // if we are going to delete this relation as well, then we don't need to clean it up\n if (this.isCascadeDelete) return;\n setField(this.entity, this.fieldName, undefined);\n this.#state = new RRRefState<U, N>(this, undefined as N);\n this.#isLoaded = false;\n this.#isCached = false;\n this.#loadPromise = undefined;\n }\n\n // We need to keep U in data[fieldName] to handle entities without an id assigned yet.\n current(opts?: { withDeleted?: boolean }): U | string | N {\n const current = getField(this.entity, this.fieldName);\n if (current !== undefined && isEntity(current)) {\n return this.filterDeleted(current as U, opts);\n }\n return current;\n }\n\n get fieldName(): string {\n return this.#fieldName;\n }\n\n public get otherMeta(): EntityMetadata<U> {\n return (getMetadata(this.entity).allFields[this.#fieldName] as ManyToOneField).otherMetadata();\n }\n\n public get hasBeenSet(): boolean {\n return true;\n }\n\n public toString(): string {\n return `ReactiveReference(entity: ${this.entity}, hint: ${this.loadHint}, fieldName: ${this.fieldName}, otherMeta: {\n this.otherMeta.type\n }, id: ${this.id})`;\n }\n\n /**\n * Removes pending-hard-delete (but not soft-deleted), unless explicitly asked for.\n *\n * Note that we leave soft-deleted entities b/c the call signature of a `book.author.get`\n * very likely does not expect a soft-deleted entity to result in `undefined`.\n *\n * (Contrasted with `author.books.get` which is more intuitive to have soft-deleted\n * entities filtered out, i.e. it doesn't fundamentally change the return type.)\n */\n private filterDeleted(entity: U | N, opts?: { withDeleted?: boolean }): U | N {\n if (entity && (!opts || !opts.withDeleted) && entity.isDeletedEntity) {\n return undefined!;\n }\n return entity;\n }\n\n private get isCascadeDelete(): boolean {\n return isCascadeDelete(this, this.#fieldName);\n }\n\n /**\n * Looks for an entity in `EntityManager`, b/c we may have it in memory even if\n * our reference is not specifically loaded.\n */\n maybeFindEntity(): U | N {\n // Check this.loaded first b/c a new entity won't have an id yet\n if (this.#state.canReturnValue) {\n const loaded = this.#state.doGet();\n if (loaded !== undefined) {\n return loaded;\n }\n }\n const { idTaggedMaybe } = this;\n return idTaggedMaybe !== undefined ? (this.entity.em.getEntity(idTaggedMaybe) as U | N) : (undefined as N);\n }\n\n [RelationT]: T = null!;\n [RelationU]: U = null!;\n [ReferenceN]: N = null!;\n}\n\n/** Type guard utility for determining if an entity field is a ReactiveReference. */\nexport function isReactiveReference(maybeReactiveRef: any): maybeReactiveRef is ReactiveReference<any, any, any> {\n return maybeReactiveRef instanceof ReactiveReferenceImpl;\n}\n\ninterface RRState<U extends Entity, N extends never | undefined> {\n readonly kind: \"unloaded\" | \"ref\" | \"full\";\n readonly canReturnValue: boolean;\n doGet(): U | N | undefined;\n /**\n * Switches from \"materialized ref value\" semantics to \"recalc from reactive graph when available\" semantics.\n *\n * This is synchronous because it does not mean the load hint is fully loaded yet. It only means this\n * reference should now behave as recalc-capable: if `isLoaded(loadHint)` later reports true, `doGet`\n * will recalculate from `fn(...)`; otherwise we keep returning the last known/stale value until `load()`\n * populates the graph.\n */\n enterRecalcMode(target: ReactiveReferenceImpl<any, any, any, any>): RRFullState<U, N>;\n import(target: ReactiveReferenceImpl<any, any, any, any>, findEntity: (e: U) => U): RRState<U, N>;\n}\n\nclass RRUnloadedState<U extends Entity, N extends never | undefined> implements RRState<U, N> {\n readonly kind = \"unloaded\";\n readonly canReturnValue = false;\n #rr: ReactiveReferenceImpl<any, any, any, any>;\n\n constructor(rr: ReactiveReferenceImpl<any, any, any, any>) {\n this.#rr = rr;\n }\n\n doGet(): U | N | undefined {\n throw new Error(`${this.#rr.entity}.${this.#rr.fieldName} was not loaded`);\n }\n\n enterRecalcMode(target: ReactiveReferenceImpl<any, any, any, any>): RRFullState<U, N> {\n return new RRFullState<U, N>(target, undefined);\n }\n\n import(target: ReactiveReferenceImpl<any, any, any, any>, _findEntity: (e: U) => U): RRState<U, N> {\n return new RRUnloadedState<U, N>(target);\n }\n}\n\nclass RRRefState<U extends Entity, N extends never | undefined> implements RRState<U, N> {\n readonly kind = \"ref\";\n readonly canReturnValue = true;\n #loaded: U | N | undefined;\n\n constructor(_rr: ReactiveReferenceImpl<any, any, any, any>, loaded: U | N | undefined) {\n this.#loaded = loaded;\n }\n\n doGet(): U | N | undefined {\n return this.#loaded;\n }\n\n enterRecalcMode(target: ReactiveReferenceImpl<any, any, any, any>): RRFullState<U, N> {\n return new RRFullState<U, N>(target, this.#loaded);\n }\n\n import(target: ReactiveReferenceImpl<any, any, any, any>, findEntity: (e: U) => U): RRState<U, N> {\n return new RRRefState<U, N>(target, mapReactiveReferenceEntity(this.#loaded, findEntity));\n }\n}\n\nclass RRFullState<U extends Entity, N extends never | undefined> implements RRState<U, N> {\n readonly kind = \"full\";\n readonly canReturnValue = true;\n #loaded: U | N | undefined;\n\n constructor(_rr: ReactiveReferenceImpl<any, any, any, any>, loaded: U | N | undefined) {\n this.#loaded = loaded;\n }\n\n doGet(): U | N | undefined {\n return this.#loaded;\n }\n\n enterRecalcMode(_target: ReactiveReferenceImpl<any, any, any, any>): RRFullState<U, N> {\n return this;\n }\n\n import(target: ReactiveReferenceImpl<any, any, any, any>, findEntity: (e: U) => U): RRState<U, N> {\n return new RRRefState<U, N>(target, mapReactiveReferenceEntity(this.#loaded, findEntity));\n }\n}\n\nfunction mapReactiveReferenceEntity<U extends Entity, N extends never | undefined>(\n entity: U | N | undefined,\n findEntity: (e: U) => U,\n): U | N | undefined {\n return isEntity(entity) ? findEntity(entity) : entity;\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ReactiveReference.js","names":["#fieldName","#otherMeta","#state","#loadPromise","#isLoaded","#isCached","#rr","#loaded"],"sources":["../../src/relations/ReactiveReference.ts"],"sourcesContent":["import { type Entity } from \"../Entity.ts\";\nimport { type IdOf } from \"../EntityManager.ts\";\nimport { getField, setField } from \"../fields.ts\";\nimport {\n type EntityMetadata,\n type ManyToOneField,\n type TaggedId,\n deTagId,\n ensureNotDeleted,\n fail,\n getEmInternalApi,\n getInstanceData,\n getMetadata,\n isEntity,\n isLoaded,\n maybeResolveReferenceToId,\n sameEntity,\n toIdOf,\n} from \"../index.ts\";\nimport type { IsLoadedCachable } from \"../loading/IsLoadedCache.ts\";\nimport { lazyField } from \"../newEntity.ts\";\nimport {\n type MaybeReactedEntity,\n type Reacted,\n type ReactiveHint,\n convertToLoadHint,\n} from \"../reactivity/reactiveHints.ts\";\nimport { AbstractRelationImpl } from \"./AbstractRelationImpl.ts\";\nimport { isCascadeDelete } from \"./isCascadeDelete.ts\";\nimport { failIfNewEntity, failNoId } from \"./ManyToOneReference.ts\";\nimport { type Reference } from \"./Reference.ts\";\nimport { ReferenceN } from \"./ReferenceSymbols.ts\";\nimport { RelationT, RelationU } from \"./RelationSymbols.ts\";\n\nexport interface ReactiveReference<T extends Entity, U extends Entity, N extends never | undefined> extends Reference<\n T,\n U,\n N\n> {\n isLoaded: boolean;\n isSet: boolean;\n\n load(opts?: { withDeleted?: boolean; forceReload?: true }): Promise<U | N>;\n\n /**\n * Returns the as-of-last-flush previously-calculated entity.\n *\n * This is useful if you have to purposefully avoid using the lambda to calc the latest entity,\n * i.e. if you're in a test and want to watch a calculated entity change from some dummy entity\n * to the new derived entity.\n * */\n fieldValue: U | N;\n\n /** Returns the id of the current assigned entity, or a runtime error if either 1) unset or 2) set to a new entity that doesn't have an `id` yet. */\n id: IdOf<U>;\n\n /** Returns the id of the current assigned entity, undefined if unset, or a runtime error if set to a new entity. */\n idIfSet: IdOf<U> | undefined;\n\n idUntagged: string;\n\n idUntaggedIfSet: string | undefined;\n}\n\nexport function hasReactiveReference<T extends Entity, U extends Entity, const H extends ReactiveHint<T>>(\n hint: H,\n fn: (entity: Reacted<T, H>) => MaybeReactedEntity<U>,\n): ReactiveReference<T, U, never>;\nexport function hasReactiveReference<T extends Entity, U extends Entity, const H extends ReactiveHint<T>>(\n hint: H,\n fn: (entity: Reacted<T, H>) => MaybeReactedEntity<U> | undefined,\n): ReactiveReference<T, U, undefined>;\n/** Creates a `ReactiveReference`. */\nexport function hasReactiveReference<\n T extends Entity,\n U extends Entity,\n const H extends ReactiveHint<T>,\n N extends never | undefined,\n>(hint: H, fn: (entity: Reacted<T, H>) => MaybeReactedEntity<U> | N): ReactiveReference<T, U, N> {\n return lazyField((entity: T, fieldName) => {\n const m2o = getMetadata(entity).allFields[fieldName] as ManyToOneField;\n return new ReactiveReferenceImpl<T, U, H, N>(entity, m2o, hint, fn);\n });\n}\n\n/**\n * Implements ReactiveReferences.\n *\n * This is a bit trickier than ReactiveFields, because we have to differentiate our loaded\n * between:\n *\n * - our load hint is loaded, and we can directly calculate the referenced entity, or\n * - we've fetched the calculated/stored entity from the database, but not purposefully\n * not re-calculating it (which would require fetching the load hint, which might be\n * expensive, and defeat the point of having our field materialized in the database)\n *\n * Further, want to support all of:\n *\n * - When loaded & `.get` has been called, we cache the value\n *\n * - If a WIP mutation happens, we invalidate `.get` *but* if we check `.isLoaded`,\n * we can return the live/updated value, without an `await`.\n *\n * - If a WIP mutation happens, we invalidate `.isLoaded`, and if we're *no longer loaded*,\n * we want to keep returning the stale value (as asserted by the tests), but the next\n * `load` / `populate` should load the graph & recalculate the value.\n */\nexport class ReactiveReferenceImpl<\n T extends Entity,\n U extends Entity,\n H extends ReactiveHint<T>,\n N extends never | undefined,\n>\n extends AbstractRelationImpl<T, U>\n implements ReactiveReference<T, U, N>, IsLoadedCachable\n{\n readonly #fieldName: keyof T & string;\n readonly #otherMeta: EntityMetadata;\n #state: RRState<U, N>;\n // #isLoaded doesn't necessary care if we're ref-mode or full-mode, it's just\n // whether a caller can call `.get` and not have it blow up.\n #isLoaded: boolean | undefined = undefined;\n #isCached: boolean = false;\n #loadPromise: Promise<U | N> | undefined;\n\n constructor(\n entity: T,\n field: ManyToOneField,\n public reactiveHint: H,\n private fn: (entity: Reacted<T, H>) => MaybeReactedEntity<U> | N,\n ) {\n super(entity);\n // Minimizing changes until the other PR lands...\n this.#fieldName = field.fieldName as keyof T & string;\n this.#otherMeta = field.otherMetadata();\n this.#state = new RRUnloadedState<U, N>(this);\n // We can be initialized with [entity | id | undefined], and if it's entity or id, then setImpl\n // will set loaded appropriately; but if we're initialized undefined, then mark loaded here\n if (entity.isNewEntity) {\n // const isLoaded = isEntity(this.current()) ? true : undefined;\n }\n // If the property transformer is not enabled, then the RR will not be in the relations map because they are\n // defined in the implementation file and not the codegen file. So we should insert ourselves regardless.\n (getInstanceData(entity).relations ??= {})[field.fieldName] = this;\n }\n\n async load(opts?: { withDeleted?: boolean; forceReload?: true }): Promise<U | N> {\n ensureNotDeleted(this.entity, \"pending\");\n const { loadHint } = this;\n if (!this.isLoaded || opts?.forceReload) {\n const { em } = this.entity;\n // Just because we're not loaded, doesn't mean we necessarily need to load our full hint.\n // We prefer to load only our previously-calculated/materialized value, and only load the\n // full load hint if we need recalculated.\n const maybeDirty = opts?.forceReload || getEmInternalApi(em).rm.isMaybePendingRecalc(this.entity, this.fieldName);\n if (maybeDirty) {\n return (this.#loadPromise ??= em\n .populate(this.entity, { hint: loadHint, ...opts })\n .then(() => {\n this.#state = this.#state.enterRecalcMode(this);\n this.#isLoaded = true;\n getEmInternalApi(this.entity.em).isLoadedCache.add(this);\n // Go through `this.get` so that `setField` is called to set our latest value\n this.#isCached = false;\n return this.doGet(opts);\n })\n .finally(() => {\n this.#loadPromise = undefined;\n }));\n } else {\n // If we don't need a full recalc, just make sure we have the entity in memory\n // ...ideally we would check `isSet` and whether the key was in the `instanceData`\n const current = this.current();\n if (isEntity(current) || current === undefined) {\n this.#state = new RRRefState<U, N>(this, current as U | N | undefined);\n this.#isLoaded = true;\n getEmInternalApi(this.entity.em).isLoadedCache.add(this);\n return current as U | N;\n } else {\n return (this.#loadPromise ??= em\n .load(this.#otherMeta.cstr, current)\n .then((loaded) => {\n this.#state = new RRRefState<U, N>(this, loaded);\n this.#isLoaded = true;\n getEmInternalApi(this.entity.em).isLoadedCache.add(this);\n return loaded;\n })\n .finally(() => {\n this.#loadPromise = undefined;\n }));\n }\n }\n }\n // The ReactiveField.load() always sets `#isCached = false`, but our assertion is that our\n // IsLoadedCache/resetIsLoaded infra is good enough to \"see all invalidating mutations\",\n // so we don't need this explicit reset.\n // this.#isCached = false;\n return this.doGet(opts);\n }\n\n get isLoaded(): boolean {\n // If we've cached an isLoaded value, just use that, see https://github.com/joist-orm/joist-orm/issues/1166\n if (this.#isLoaded !== undefined) return this.#isLoaded;\n getEmInternalApi(this.entity.em).isLoadedCache.add(this);\n // If a WIP mutation has marked our field as potentially dirty, we need to be \"full\" loaded\n const maybeDirty = getEmInternalApi(this.entity.em).rm.isMaybePendingRecalc(this.entity, this.fieldName);\n if (maybeDirty) {\n // If we're dirty, only being \"full\" loaded is good enough to recalc\n this.#state = this.#state.enterRecalcMode(this);\n this.#isLoaded = isLoaded(this.entity, this.loadHint);\n this.#isCached = false;\n } else {\n // If we've had `.load` called before, assume we're still loaded\n this.#isLoaded = this.#state.canReturnValue;\n }\n return this.#isLoaded;\n }\n\n resetIsLoaded(): void {\n this.#isLoaded = undefined;\n this.#isCached = false;\n }\n\n private doGet(opts?: { withDeleted?: boolean }): U | N {\n const { fn } = this;\n ensureNotDeleted(this.entity, \"pending\");\n // Fast pass if we've already calculated this (cache invalidation will happen on\n // any mutation via #resetIsLoaded)..\n if (this.#isCached) {\n return this.filterDeleted(this.#state.doGet() as U | N, opts);\n }\n // Call isLoaded to probe the load hint, and get `#isLoaded` set, but still have\n // our `if` check the raw `#isLoaded` to know if we should eval-latest or return `loaded`.\n if (this.isLoaded && this.#state.kind === \"full\") {\n const newValue = this.filterDeleted(fn(this.entity as any) as any, opts);\n // It's cheap to set this every time we're called, i.e. even if it's not the\n // official \"being called during em.flush\" update (...unless we're accessing it\n // during the validate phase of `em.flush`, then skip it to avoid tripping up\n // the \"cannot change entities during flush\" logic.)\n if (!getEmInternalApi(this.entity.em).isValidating) {\n this.setImpl(newValue);\n }\n this.#state = new RRFullState<U, N>(this, newValue);\n this.#isCached = true;\n getEmInternalApi(this.entity.em).isLoadedCache.add(this);\n } else if (this.#state.canReturnValue) {\n // We're either in ref mode, or in full mode but not actually fully loaded,\n // but in theory those states only get set after `.load()` / `.preload()` / import,\n // so we should have some stored value, even if it's not fully up-to-date.\n this.#isCached = true;\n getEmInternalApi(this.entity.em).isLoadedCache.add(this);\n } else {\n const noun = this.entity.isNewEntity ? \"derived\" : \"loaded\";\n throw new Error(`${this.entity}.${this.fieldName} has not been ${noun} yet`);\n }\n return this.filterDeleted(this.#state.doGet() as U | N, opts);\n }\n\n get fieldValue(): U | N {\n return getField(this.entity, this.fieldName);\n }\n\n get getWithDeleted(): U | N {\n return this.doGet({ withDeleted: true });\n }\n\n get get(): U | N {\n return this.doGet({ withDeleted: false });\n }\n\n set(_: U | N): void {\n fail(`Cannot set ${this.entity}.${this.fieldName} ReactiveReference directly.`);\n }\n\n get isSet(): boolean {\n return this.current() !== undefined;\n }\n\n // Internal method used by OneToManyCollection\n setImpl(entity: U | N): void {\n ensureNotDeleted(this.entity, \"pending\");\n if (sameEntity(entity, this.current({ withDeleted: true }))) {\n return;\n }\n // Prefer to keep the id in our data hash, but if this is a new entity w/o an id, use the entity itself\n setField(this.entity, this.fieldName, isEntity(entity) ? (entity.idTaggedMaybe ?? entity) : entity);\n }\n\n get isPreloaded(): boolean {\n return !!this.maybeFindEntity();\n }\n\n preload(): void {\n this.#state = new RRRefState<U, N>(this, this.maybeFindEntity());\n this.#isLoaded = true;\n this.#isCached = true;\n }\n\n private unload(): void {\n this.#state = new RRUnloadedState<U, N>(this);\n this.#isLoaded = false;\n this.#isCached = false;\n this.#loadPromise = undefined;\n }\n\n import(other: ReactiveReferenceImpl<T, U, H, N>, findEntity: (e: U) => U): void {\n this.#state = other.#state.import(this, findEntity);\n this.#isLoaded = true;\n this.#isCached = true;\n }\n\n /** Returns the tagged id of the current value. */\n get idTaggedMaybe(): TaggedId | N {\n ensureNotDeleted(this.entity, \"pending\");\n return maybeResolveReferenceToId(this.current()) as TaggedId | N;\n }\n\n get id(): IdOf<U> {\n return this.idMaybe || failNoId(this.entity, this.fieldName, this.current());\n }\n\n get idUntagged(): string {\n return this.idUntaggedMaybe || failNoId(this.entity, this.fieldName, this.current());\n }\n\n get idIfSet(): IdOf<U> | N | undefined {\n return this.idMaybe || failIfNewEntity(this.entity, this.fieldName, this.current());\n }\n\n get idUntaggedIfSet(): string | undefined {\n return this.idUntaggedMaybe || failIfNewEntity(this.entity, this.fieldName, this.current());\n }\n\n get loadHint(): any {\n const meta = getMetadata(this.entity);\n return (meta.config.__data.cachedReactiveLoadHints[this.fieldName] ??= convertToLoadHint(meta, this.reactiveHint));\n }\n\n // private impl\n\n /** Returns the id of the current value. */\n private get idMaybe(): IdOf<U> | undefined {\n ensureNotDeleted(this.entity, \"pending\");\n return toIdOf(this.otherMeta, this.idTaggedMaybe);\n }\n\n private get idUntaggedMaybe(): string | undefined {\n return deTagId(this.otherMeta, this.idMaybe);\n }\n\n setFromOpts(_: U | IdOf<U> | N): void {\n throw new Error(`ReactiveReference ${this.entity}.${this.fieldName} cannot be set via opts`);\n }\n\n maybeCascadeDelete(): void {\n if (this.isCascadeDelete) {\n const current = this.current({ withDeleted: true });\n if (current !== undefined && typeof current !== \"string\") {\n this.entity.em.delete(current as U);\n }\n }\n }\n\n async cleanupOnEntityDeleted(): Promise<void> {\n // if we are going to delete this relation as well, then we don't need to clean it up\n if (this.isCascadeDelete) return;\n setField(this.entity, this.fieldName, undefined);\n this.#state = new RRRefState<U, N>(this, undefined as N);\n this.#isLoaded = false;\n this.#isCached = false;\n this.#loadPromise = undefined;\n }\n\n // We need to keep U in data[fieldName] to handle entities without an id assigned yet.\n current(opts?: { withDeleted?: boolean }): U | string | N {\n const current = getField(this.entity, this.fieldName);\n if (current !== undefined && isEntity(current)) {\n return this.filterDeleted(current as U, opts);\n }\n return current;\n }\n\n get fieldName(): string {\n return this.#fieldName;\n }\n\n public get otherMeta(): EntityMetadata<U> {\n return (getMetadata(this.entity).allFields[this.#fieldName] as ManyToOneField).otherMetadata();\n }\n\n public get hasBeenSet(): boolean {\n return true;\n }\n\n public toString(): string {\n return `ReactiveReference(entity: ${this.entity}, hint: ${this.loadHint}, fieldName: ${this.fieldName}, otherMeta: {\n this.otherMeta.type\n }, id: ${this.id})`;\n }\n\n /**\n * Removes pending-hard-delete (but not soft-deleted), unless explicitly asked for.\n *\n * Note that we leave soft-deleted entities b/c the call signature of a `book.author.get`\n * very likely does not expect a soft-deleted entity to result in `undefined`.\n *\n * (Contrasted with `author.books.get` which is more intuitive to have soft-deleted\n * entities filtered out, i.e. it doesn't fundamentally change the return type.)\n */\n private filterDeleted(entity: U | N, opts?: { withDeleted?: boolean }): U | N {\n if (entity && (!opts || !opts.withDeleted) && entity.isDeletedEntity) {\n return undefined!;\n }\n return entity;\n }\n\n private get isCascadeDelete(): boolean {\n return isCascadeDelete(this, this.#fieldName);\n }\n\n /**\n * Looks for an entity in `EntityManager`, b/c we may have it in memory even if\n * our reference is not specifically loaded.\n */\n maybeFindEntity(): U | N {\n // Check this.loaded first b/c a new entity won't have an id yet\n if (this.#state.canReturnValue) {\n const loaded = this.#state.doGet();\n if (loaded !== undefined) {\n return loaded;\n }\n }\n const { idTaggedMaybe } = this;\n return idTaggedMaybe !== undefined ? (this.entity.em.getEntity(idTaggedMaybe) as U | N) : (undefined as N);\n }\n\n [RelationT]: T = null!;\n [RelationU]: U = null!;\n [ReferenceN]: N = null!;\n}\n\n/** Type guard utility for determining if an entity field is a ReactiveReference. */\nexport function isReactiveReference(maybeReactiveRef: any): maybeReactiveRef is ReactiveReference<any, any, any> {\n return maybeReactiveRef instanceof ReactiveReferenceImpl;\n}\n\ninterface RRState<U extends Entity, N extends never | undefined> {\n readonly kind: \"unloaded\" | \"ref\" | \"full\";\n readonly canReturnValue: boolean;\n doGet(): U | N | undefined;\n /**\n * Switches from \"materialized ref value\" semantics to \"recalc from reactive graph when available\" semantics.\n *\n * This is synchronous because it does not mean the load hint is fully loaded yet. It only means this\n * reference should now behave as recalc-capable: if `isLoaded(loadHint)` later reports true, `doGet`\n * will recalculate from `fn(...)`; otherwise we keep returning the last known/stale value until `load()`\n * populates the graph.\n */\n enterRecalcMode(target: ReactiveReferenceImpl<any, any, any, any>): RRFullState<U, N>;\n import(target: ReactiveReferenceImpl<any, any, any, any>, findEntity: (e: U) => U): RRState<U, N>;\n}\n\nclass RRUnloadedState<U extends Entity, N extends never | undefined> implements RRState<U, N> {\n readonly kind = \"unloaded\";\n readonly canReturnValue = false;\n #rr: ReactiveReferenceImpl<any, any, any, any>;\n\n constructor(rr: ReactiveReferenceImpl<any, any, any, any>) {\n this.#rr = rr;\n }\n\n doGet(): U | N | undefined {\n throw new Error(`${this.#rr.entity}.${this.#rr.fieldName} was not loaded`);\n }\n\n enterRecalcMode(target: ReactiveReferenceImpl<any, any, any, any>): RRFullState<U, N> {\n return new RRFullState<U, N>(target, undefined);\n }\n\n import(target: ReactiveReferenceImpl<any, any, any, any>, _findEntity: (e: U) => U): RRState<U, N> {\n return new RRUnloadedState<U, N>(target);\n }\n}\n\nclass RRRefState<U extends Entity, N extends never | undefined> implements RRState<U, N> {\n readonly kind = \"ref\";\n readonly canReturnValue = true;\n #loaded: U | N | undefined;\n\n constructor(_rr: ReactiveReferenceImpl<any, any, any, any>, loaded: U | N | undefined) {\n this.#loaded = loaded;\n }\n\n doGet(): U | N | undefined {\n return this.#loaded;\n }\n\n enterRecalcMode(target: ReactiveReferenceImpl<any, any, any, any>): RRFullState<U, N> {\n return new RRFullState<U, N>(target, this.#loaded);\n }\n\n import(target: ReactiveReferenceImpl<any, any, any, any>, findEntity: (e: U) => U): RRState<U, N> {\n return new RRRefState<U, N>(target, mapReactiveReferenceEntity(this.#loaded, findEntity));\n }\n}\n\nclass RRFullState<U extends Entity, N extends never | undefined> implements RRState<U, N> {\n readonly kind = \"full\";\n readonly canReturnValue = true;\n #loaded: U | N | undefined;\n\n constructor(_rr: ReactiveReferenceImpl<any, any, any, any>, loaded: U | N | undefined) {\n this.#loaded = loaded;\n }\n\n doGet(): U | N | undefined {\n return this.#loaded;\n }\n\n enterRecalcMode(_target: ReactiveReferenceImpl<any, any, any, any>): RRFullState<U, N> {\n return this;\n }\n\n import(target: ReactiveReferenceImpl<any, any, any, any>, findEntity: (e: U) => U): RRState<U, N> {\n return new RRRefState<U, N>(target, mapReactiveReferenceEntity(this.#loaded, findEntity));\n }\n}\n\nfunction mapReactiveReferenceEntity<U extends Entity, N extends never | undefined>(\n entity: U | N | undefined,\n findEntity: (e: U) => U,\n): U | N | undefined {\n return isEntity(entity) ? findEntity(entity) : entity;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ReactiveReference.js","names":["#fieldName","#otherMeta","#state","#loadPromise","#isLoaded","#isCached","#rr","#loaded"],"sources":["../../src/relations/ReactiveReference.ts"],"sourcesContent":["import { type Entity } from \"src/Entity.ts\";\nimport { type IdOf } from \"src/EntityManager.ts\";\nimport { getField, setField } from \"src/fields.ts\";\nimport {\n type EntityMetadata,\n type ManyToOneField,\n type TaggedId,\n deTagId,\n ensureNotDeleted,\n fail,\n getEmInternalApi,\n getInstanceData,\n getMetadata,\n isEntity,\n isLoaded,\n maybeResolveReferenceToId,\n sameEntity,\n toIdOf,\n} from \"src/index.ts\";\nimport type { IsLoadedCachable } from \"src/loading/IsLoadedCache.ts\";\nimport { lazyField } from \"src/newEntity.ts\";\nimport {\n type MaybeReactedEntity,\n type Reacted,\n type ReactiveHint,\n convertToLoadHint,\n} from \"src/reactivity/reactiveHints.ts\";\nimport { AbstractRelationImpl } from \"src/relations/AbstractRelationImpl.ts\";\nimport { isCascadeDelete } from \"src/relations/isCascadeDelete.ts\";\nimport { failIfNewEntity, failNoId } from \"src/relations/ManyToOneReference.ts\";\nimport { type Reference } from \"src/relations/Reference.ts\";\nimport { ReferenceN } from \"src/relations/ReferenceSymbols.ts\";\nimport { RelationT, RelationU } from \"src/relations/RelationSymbols.ts\";\n\nexport interface ReactiveReference<T extends Entity, U extends Entity, N extends never | undefined> extends Reference<\n T,\n U,\n N\n> {\n isLoaded: boolean;\n isSet: boolean;\n\n load(opts?: { withDeleted?: boolean; forceReload?: true }): Promise<U | N>;\n\n /**\n * Returns the as-of-last-flush previously-calculated entity.\n *\n * This is useful if you have to purposefully avoid using the lambda to calc the latest entity,\n * i.e. if you're in a test and want to watch a calculated entity change from some dummy entity\n * to the new derived entity.\n * */\n fieldValue: U | N;\n\n /** Returns the id of the current assigned entity, or a runtime error if either 1) unset or 2) set to a new entity that doesn't have an `id` yet. */\n id: IdOf<U>;\n\n /** Returns the id of the current assigned entity, undefined if unset, or a runtime error if set to a new entity. */\n idIfSet: IdOf<U> | undefined;\n\n idUntagged: string;\n\n idUntaggedIfSet: string | undefined;\n}\n\nexport function hasReactiveReference<T extends Entity, U extends Entity, const H extends ReactiveHint<T>>(\n hint: H,\n fn: (entity: Reacted<T, H>) => MaybeReactedEntity<U>,\n): ReactiveReference<T, U, never>;\nexport function hasReactiveReference<T extends Entity, U extends Entity, const H extends ReactiveHint<T>>(\n hint: H,\n fn: (entity: Reacted<T, H>) => MaybeReactedEntity<U> | undefined,\n): ReactiveReference<T, U, undefined>;\n/** Creates a `ReactiveReference`. */\nexport function hasReactiveReference<\n T extends Entity,\n U extends Entity,\n const H extends ReactiveHint<T>,\n N extends never | undefined,\n>(hint: H, fn: (entity: Reacted<T, H>) => MaybeReactedEntity<U> | N): ReactiveReference<T, U, N> {\n return lazyField((entity: T, fieldName) => {\n const m2o = getMetadata(entity).allFields[fieldName] as ManyToOneField;\n return new ReactiveReferenceImpl<T, U, H, N>(entity, m2o, hint, fn);\n });\n}\n\n/**\n * Implements ReactiveReferences.\n *\n * This is a bit trickier than ReactiveFields, because we have to differentiate our loaded\n * between:\n *\n * - our load hint is loaded, and we can directly calculate the referenced entity, or\n * - we've fetched the calculated/stored entity from the database, but not purposefully\n * not re-calculating it (which would require fetching the load hint, which might be\n * expensive, and defeat the point of having our field materialized in the database)\n *\n * Further, want to support all of:\n *\n * - When loaded & `.get` has been called, we cache the value\n *\n * - If a WIP mutation happens, we invalidate `.get` *but* if we check `.isLoaded`,\n * we can return the live/updated value, without an `await`.\n *\n * - If a WIP mutation happens, we invalidate `.isLoaded`, and if we're *no longer loaded*,\n * we want to keep returning the stale value (as asserted by the tests), but the next\n * `load` / `populate` should load the graph & recalculate the value.\n */\nexport class ReactiveReferenceImpl<\n T extends Entity,\n U extends Entity,\n H extends ReactiveHint<T>,\n N extends never | undefined,\n>\n extends AbstractRelationImpl<T, U>\n implements ReactiveReference<T, U, N>, IsLoadedCachable\n{\n readonly #fieldName: keyof T & string;\n readonly #otherMeta: EntityMetadata;\n #state: RRState<U, N>;\n // #isLoaded doesn't necessary care if we're ref-mode or full-mode, it's just\n // whether a caller can call `.get` and not have it blow up.\n #isLoaded: boolean | undefined = undefined;\n #isCached: boolean = false;\n #loadPromise: Promise<U | N> | undefined;\n\n constructor(\n entity: T,\n field: ManyToOneField,\n public reactiveHint: H,\n private fn: (entity: Reacted<T, H>) => MaybeReactedEntity<U> | N,\n ) {\n super(entity);\n // Minimizing changes until the other PR lands...\n this.#fieldName = field.fieldName as keyof T & string;\n this.#otherMeta = field.otherMetadata();\n this.#state = new RRUnloadedState<U, N>(this);\n // We can be initialized with [entity | id | undefined], and if it's entity or id, then setImpl\n // will set loaded appropriately; but if we're initialized undefined, then mark loaded here\n if (entity.isNewEntity) {\n // const isLoaded = isEntity(this.current()) ? true : undefined;\n }\n // If the property transformer is not enabled, then the RR will not be in the relations map because they are\n // defined in the implementation file and not the codegen file. So we should insert ourselves regardless.\n (getInstanceData(entity).relations ??= {})[field.fieldName] = this;\n }\n\n async load(opts?: { withDeleted?: boolean; forceReload?: true }): Promise<U | N> {\n ensureNotDeleted(this.entity, \"pending\");\n const { loadHint } = this;\n if (!this.isLoaded || opts?.forceReload) {\n const { em } = this.entity;\n // Just because we're not loaded, doesn't mean we necessarily need to load our full hint.\n // We prefer to load only our previously-calculated/materialized value, and only load the\n // full load hint if we need recalculated.\n const maybeDirty = opts?.forceReload || getEmInternalApi(em).rm.isMaybePendingRecalc(this.entity, this.fieldName);\n if (maybeDirty) {\n return (this.#loadPromise ??= em\n .populate(this.entity, { hint: loadHint, ...opts })\n .then(() => {\n this.#state = this.#state.enterRecalcMode(this);\n this.#isLoaded = true;\n getEmInternalApi(this.entity.em).isLoadedCache.add(this);\n // Go through `this.get` so that `setField` is called to set our latest value\n this.#isCached = false;\n return this.doGet(opts);\n })\n .finally(() => {\n this.#loadPromise = undefined;\n }));\n } else {\n // If we don't need a full recalc, just make sure we have the entity in memory\n // ...ideally we would check `isSet` and whether the key was in the `instanceData`\n const current = this.current();\n if (isEntity(current) || current === undefined) {\n this.#state = new RRRefState<U, N>(this, current as U | N | undefined);\n this.#isLoaded = true;\n getEmInternalApi(this.entity.em).isLoadedCache.add(this);\n return current as U | N;\n } else {\n return (this.#loadPromise ??= em\n .load(this.#otherMeta.cstr, current)\n .then((loaded) => {\n this.#state = new RRRefState<U, N>(this, loaded);\n this.#isLoaded = true;\n getEmInternalApi(this.entity.em).isLoadedCache.add(this);\n return loaded;\n })\n .finally(() => {\n this.#loadPromise = undefined;\n }));\n }\n }\n }\n // The ReactiveField.load() always sets `#isCached = false`, but our assertion is that our\n // IsLoadedCache/resetIsLoaded infra is good enough to \"see all invalidating mutations\",\n // so we don't need this explicit reset.\n // this.#isCached = false;\n return this.doGet(opts);\n }\n\n get isLoaded(): boolean {\n // If we've cached an isLoaded value, just use that, see https://github.com/joist-orm/joist-orm/issues/1166\n if (this.#isLoaded !== undefined) return this.#isLoaded;\n getEmInternalApi(this.entity.em).isLoadedCache.add(this);\n // If a WIP mutation has marked our field as potentially dirty, we need to be \"full\" loaded\n const maybeDirty = getEmInternalApi(this.entity.em).rm.isMaybePendingRecalc(this.entity, this.fieldName);\n if (maybeDirty) {\n // If we're dirty, only being \"full\" loaded is good enough to recalc\n this.#state = this.#state.enterRecalcMode(this);\n this.#isLoaded = isLoaded(this.entity, this.loadHint);\n this.#isCached = false;\n } else {\n // If we've had `.load` called before, assume we're still loaded\n this.#isLoaded = this.#state.canReturnValue;\n }\n return this.#isLoaded;\n }\n\n resetIsLoaded(): void {\n this.#isLoaded = undefined;\n this.#isCached = false;\n }\n\n private doGet(opts?: { withDeleted?: boolean }): U | N {\n const { fn } = this;\n ensureNotDeleted(this.entity, \"pending\");\n // Fast pass if we've already calculated this (cache invalidation will happen on\n // any mutation via #resetIsLoaded)..\n if (this.#isCached) {\n return this.filterDeleted(this.#state.doGet() as U | N, opts);\n }\n // Call isLoaded to probe the load hint, and get `#isLoaded` set, but still have\n // our `if` check the raw `#isLoaded` to know if we should eval-latest or return `loaded`.\n if (this.isLoaded && this.#state.kind === \"full\") {\n const newValue = this.filterDeleted(fn(this.entity as any) as any, opts);\n // It's cheap to set this every time we're called, i.e. even if it's not the\n // official \"being called during em.flush\" update (...unless we're accessing it\n // during the validate phase of `em.flush`, then skip it to avoid tripping up\n // the \"cannot change entities during flush\" logic.)\n if (!getEmInternalApi(this.entity.em).isValidating) {\n this.setImpl(newValue);\n }\n this.#state = new RRFullState<U, N>(this, newValue);\n this.#isCached = true;\n getEmInternalApi(this.entity.em).isLoadedCache.add(this);\n } else if (this.#state.canReturnValue) {\n // We're either in ref mode, or in full mode but not actually fully loaded,\n // but in theory those states only get set after `.load()` / `.preload()` / import,\n // so we should have some stored value, even if it's not fully up-to-date.\n this.#isCached = true;\n getEmInternalApi(this.entity.em).isLoadedCache.add(this);\n } else {\n const noun = this.entity.isNewEntity ? \"derived\" : \"loaded\";\n throw new Error(`${this.entity}.${this.fieldName} has not been ${noun} yet`);\n }\n return this.filterDeleted(this.#state.doGet() as U | N, opts);\n }\n\n get fieldValue(): U | N {\n return getField(this.entity, this.fieldName);\n }\n\n get getWithDeleted(): U | N {\n return this.doGet({ withDeleted: true });\n }\n\n get get(): U | N {\n return this.doGet({ withDeleted: false });\n }\n\n set(_: U | N): void {\n fail(`Cannot set ${this.entity}.${this.fieldName} ReactiveReference directly.`);\n }\n\n get isSet(): boolean {\n return this.current() !== undefined;\n }\n\n // Internal method used by OneToManyCollection\n setImpl(entity: U | N): void {\n ensureNotDeleted(this.entity, \"pending\");\n if (sameEntity(entity, this.current({ withDeleted: true }))) {\n return;\n }\n // Prefer to keep the id in our data hash, but if this is a new entity w/o an id, use the entity itself\n setField(this.entity, this.fieldName, isEntity(entity) ? (entity.idTaggedMaybe ?? entity) : entity);\n }\n\n get isPreloaded(): boolean {\n return !!this.maybeFindEntity();\n }\n\n preload(): void {\n this.#state = new RRRefState<U, N>(this, this.maybeFindEntity());\n this.#isLoaded = true;\n this.#isCached = true;\n }\n\n private unload(): void {\n this.#state = new RRUnloadedState<U, N>(this);\n this.#isLoaded = false;\n this.#isCached = false;\n this.#loadPromise = undefined;\n }\n\n import(other: ReactiveReferenceImpl<T, U, H, N>, findEntity: (e: U) => U): void {\n this.#state = other.#state.import(this, findEntity);\n this.#isLoaded = true;\n this.#isCached = true;\n }\n\n /** Returns the tagged id of the current value. */\n get idTaggedMaybe(): TaggedId | N {\n ensureNotDeleted(this.entity, \"pending\");\n return maybeResolveReferenceToId(this.current()) as TaggedId | N;\n }\n\n get id(): IdOf<U> {\n return this.idMaybe || failNoId(this.entity, this.fieldName, this.current());\n }\n\n get idUntagged(): string {\n return this.idUntaggedMaybe || failNoId(this.entity, this.fieldName, this.current());\n }\n\n get idIfSet(): IdOf<U> | N | undefined {\n return this.idMaybe || failIfNewEntity(this.entity, this.fieldName, this.current());\n }\n\n get idUntaggedIfSet(): string | undefined {\n return this.idUntaggedMaybe || failIfNewEntity(this.entity, this.fieldName, this.current());\n }\n\n get loadHint(): any {\n const meta = getMetadata(this.entity);\n return (meta.config.__data.cachedReactiveLoadHints[this.fieldName] ??= convertToLoadHint(meta, this.reactiveHint));\n }\n\n // private impl\n\n /** Returns the id of the current value. */\n private get idMaybe(): IdOf<U> | undefined {\n ensureNotDeleted(this.entity, \"pending\");\n return toIdOf(this.otherMeta, this.idTaggedMaybe);\n }\n\n private get idUntaggedMaybe(): string | undefined {\n return deTagId(this.otherMeta, this.idMaybe);\n }\n\n setFromOpts(_: U | IdOf<U> | N): void {\n throw new Error(`ReactiveReference ${this.entity}.${this.fieldName} cannot be set via opts`);\n }\n\n maybeCascadeDelete(): void {\n if (this.isCascadeDelete) {\n const current = this.current({ withDeleted: true });\n if (current !== undefined && typeof current !== \"string\") {\n this.entity.em.delete(current as U);\n }\n }\n }\n\n async cleanupOnEntityDeleted(): Promise<void> {\n // if we are going to delete this relation as well, then we don't need to clean it up\n if (this.isCascadeDelete) return;\n setField(this.entity, this.fieldName, undefined);\n this.#state = new RRRefState<U, N>(this, undefined as N);\n this.#isLoaded = false;\n this.#isCached = false;\n this.#loadPromise = undefined;\n }\n\n // We need to keep U in data[fieldName] to handle entities without an id assigned yet.\n current(opts?: { withDeleted?: boolean }): U | string | N {\n const current = getField(this.entity, this.fieldName);\n if (current !== undefined && isEntity(current)) {\n return this.filterDeleted(current as U, opts);\n }\n return current;\n }\n\n get fieldName(): string {\n return this.#fieldName;\n }\n\n public get otherMeta(): EntityMetadata<U> {\n return (getMetadata(this.entity).allFields[this.#fieldName] as ManyToOneField).otherMetadata();\n }\n\n public get hasBeenSet(): boolean {\n return true;\n }\n\n public toString(): string {\n return `ReactiveReference(entity: ${this.entity}, hint: ${this.loadHint}, fieldName: ${this.fieldName}, otherMeta: {\n this.otherMeta.type\n }, id: ${this.id})`;\n }\n\n /**\n * Removes pending-hard-delete (but not soft-deleted), unless explicitly asked for.\n *\n * Note that we leave soft-deleted entities b/c the call signature of a `book.author.get`\n * very likely does not expect a soft-deleted entity to result in `undefined`.\n *\n * (Contrasted with `author.books.get` which is more intuitive to have soft-deleted\n * entities filtered out, i.e. it doesn't fundamentally change the return type.)\n */\n private filterDeleted(entity: U | N, opts?: { withDeleted?: boolean }): U | N {\n if (entity && (!opts || !opts.withDeleted) && entity.isDeletedEntity) {\n return undefined!;\n }\n return entity;\n }\n\n private get isCascadeDelete(): boolean {\n return isCascadeDelete(this, this.#fieldName);\n }\n\n /**\n * Looks for an entity in `EntityManager`, b/c we may have it in memory even if\n * our reference is not specifically loaded.\n */\n maybeFindEntity(): U | N {\n // Check this.loaded first b/c a new entity won't have an id yet\n if (this.#state.canReturnValue) {\n const loaded = this.#state.doGet();\n if (loaded !== undefined) {\n return loaded;\n }\n }\n const { idTaggedMaybe } = this;\n return idTaggedMaybe !== undefined ? (this.entity.em.getEntity(idTaggedMaybe) as U | N) : (undefined as N);\n }\n\n [RelationT]: T = null!;\n [RelationU]: U = null!;\n [ReferenceN]: N = null!;\n}\n\n/** Type guard utility for determining if an entity field is a ReactiveReference. */\nexport function isReactiveReference(maybeReactiveRef: any): maybeReactiveRef is ReactiveReference<any, any, any> {\n return maybeReactiveRef instanceof ReactiveReferenceImpl;\n}\n\ninterface RRState<U extends Entity, N extends never | undefined> {\n readonly kind: \"unloaded\" | \"ref\" | \"full\";\n readonly canReturnValue: boolean;\n doGet(): U | N | undefined;\n /**\n * Switches from \"materialized ref value\" semantics to \"recalc from reactive graph when available\" semantics.\n *\n * This is synchronous because it does not mean the load hint is fully loaded yet. It only means this\n * reference should now behave as recalc-capable: if `isLoaded(loadHint)` later reports true, `doGet`\n * will recalculate from `fn(...)`; otherwise we keep returning the last known/stale value until `load()`\n * populates the graph.\n */\n enterRecalcMode(target: ReactiveReferenceImpl<any, any, any, any>): RRFullState<U, N>;\n import(target: ReactiveReferenceImpl<any, any, any, any>, findEntity: (e: U) => U): RRState<U, N>;\n}\n\nclass RRUnloadedState<U extends Entity, N extends never | undefined> implements RRState<U, N> {\n readonly kind = \"unloaded\";\n readonly canReturnValue = false;\n #rr: ReactiveReferenceImpl<any, any, any, any>;\n\n constructor(rr: ReactiveReferenceImpl<any, any, any, any>) {\n this.#rr = rr;\n }\n\n doGet(): U | N | undefined {\n throw new Error(`${this.#rr.entity}.${this.#rr.fieldName} was not loaded`);\n }\n\n enterRecalcMode(target: ReactiveReferenceImpl<any, any, any, any>): RRFullState<U, N> {\n return new RRFullState<U, N>(target, undefined);\n }\n\n import(target: ReactiveReferenceImpl<any, any, any, any>, _findEntity: (e: U) => U): RRState<U, N> {\n return new RRUnloadedState<U, N>(target);\n }\n}\n\nclass RRRefState<U extends Entity, N extends never | undefined> implements RRState<U, N> {\n readonly kind = \"ref\";\n readonly canReturnValue = true;\n #loaded: U | N | undefined;\n\n constructor(_rr: ReactiveReferenceImpl<any, any, any, any>, loaded: U | N | undefined) {\n this.#loaded = loaded;\n }\n\n doGet(): U | N | undefined {\n return this.#loaded;\n }\n\n enterRecalcMode(target: ReactiveReferenceImpl<any, any, any, any>): RRFullState<U, N> {\n return new RRFullState<U, N>(target, this.#loaded);\n }\n\n import(target: ReactiveReferenceImpl<any, any, any, any>, findEntity: (e: U) => U): RRState<U, N> {\n return new RRRefState<U, N>(target, mapReactiveReferenceEntity(this.#loaded, findEntity));\n }\n}\n\nclass RRFullState<U extends Entity, N extends never | undefined> implements RRState<U, N> {\n readonly kind = \"full\";\n readonly canReturnValue = true;\n #loaded: U | N | undefined;\n\n constructor(_rr: ReactiveReferenceImpl<any, any, any, any>, loaded: U | N | undefined) {\n this.#loaded = loaded;\n }\n\n doGet(): U | N | undefined {\n return this.#loaded;\n }\n\n enterRecalcMode(_target: ReactiveReferenceImpl<any, any, any, any>): RRFullState<U, N> {\n return this;\n }\n\n import(target: ReactiveReferenceImpl<any, any, any, any>, findEntity: (e: U) => U): RRState<U, N> {\n return new RRRefState<U, N>(target, mapReactiveReferenceEntity(this.#loaded, findEntity));\n }\n}\n\nfunction mapReactiveReferenceEntity<U extends Entity, N extends never | undefined>(\n entity: U | N | undefined,\n findEntity: (e: U) => U,\n): U | N | undefined {\n return isEntity(entity) ? findEntity(entity) : entity;\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ReadOnlyCollection.cjs","names":["RecursiveParentsCollectionImpl","RecursiveChildrenCollectionImpl","RecursiveM2mCollectionImpl"],"sources":["../../src/relations/ReadOnlyCollection.ts"],"sourcesContent":["import { type Entity } from \"../Entity.ts\";\nimport {\n RecursiveChildrenCollectionImpl,\n RecursiveM2mCollectionImpl,\n RecursiveParentsCollectionImpl,\n} from \"./RecursiveCollection.ts\";\nimport { type Relation } from \"./Relation.ts\";\n\n/** A collection of `U` within `T`, either one-to-many or many-to-many. */\nexport interface ReadOnlyCollection<T extends Entity, U extends Entity> extends Relation<T, U> {\n load(opts?: { withDeleted: boolean }): Promise<ReadonlyArray<U>>;\n\n readonly isLoaded: boolean;\n}\n\n/** Adds a known-safe `get` accessor. */\nexport interface LoadedReadOnlyCollection<T extends Entity, U extends Entity> extends ReadOnlyCollection<T, U> {\n get: ReadonlyArray<U>;\n\n getWithDeleted: ReadonlyArray<U>;\n}\n\n/** Type guard utility for determining if an entity field is a `ReadOnlyCollection`. */\nexport function isReadOnlyCollection(maybeCollection: any): maybeCollection is ReadOnlyCollection<any, any> {\n return (\n maybeCollection instanceof RecursiveParentsCollectionImpl ||\n maybeCollection instanceof RecursiveChildrenCollectionImpl ||\n maybeCollection instanceof RecursiveM2mCollectionImpl\n );\n}\n\n/** Type guard utility for determining if an entity field is a loaded `ReadOnlyCollection`. */\nexport function isLoadedReadOnlyCollection(\n maybeCollection: any,\n): maybeCollection is ReadOnlyCollection<any, any> & LoadedReadOnlyCollection<any, any> {\n return isReadOnlyCollection(maybeCollection) && maybeCollection.isLoaded;\n}\n"],"mappings":";;;;;;AAuBA,SAAgB,qBAAqB,iBAAuE;CAC1G,OACE,2BAA2BA,sCAAAA,kCAC3B,2BAA2BC,sCAAAA,mCAC3B,2BAA2BC,sCAAAA;AAE/B;;AAGA,SAAgB,2BACd,iBACsF;CACtF,OAAO,qBAAqB,eAAe,KAAK,gBAAgB;AAClE"}
1
+ {"version":3,"file":"ReadOnlyCollection.cjs","names":["RecursiveParentsCollectionImpl","RecursiveChildrenCollectionImpl","RecursiveM2mCollectionImpl"],"sources":["../../src/relations/ReadOnlyCollection.ts"],"sourcesContent":["import { type Entity } from \"src/Entity.ts\";\nimport {\n RecursiveChildrenCollectionImpl,\n RecursiveM2mCollectionImpl,\n RecursiveParentsCollectionImpl,\n} from \"src/relations/RecursiveCollection.ts\";\nimport { type Relation } from \"src/relations/Relation.ts\";\n\n/** A collection of `U` within `T`, either one-to-many or many-to-many. */\nexport interface ReadOnlyCollection<T extends Entity, U extends Entity> extends Relation<T, U> {\n load(opts?: { withDeleted: boolean }): Promise<ReadonlyArray<U>>;\n\n readonly isLoaded: boolean;\n}\n\n/** Adds a known-safe `get` accessor. */\nexport interface LoadedReadOnlyCollection<T extends Entity, U extends Entity> extends ReadOnlyCollection<T, U> {\n get: ReadonlyArray<U>;\n\n getWithDeleted: ReadonlyArray<U>;\n}\n\n/** Type guard utility for determining if an entity field is a `ReadOnlyCollection`. */\nexport function isReadOnlyCollection(maybeCollection: any): maybeCollection is ReadOnlyCollection<any, any> {\n return (\n maybeCollection instanceof RecursiveParentsCollectionImpl ||\n maybeCollection instanceof RecursiveChildrenCollectionImpl ||\n maybeCollection instanceof RecursiveM2mCollectionImpl\n );\n}\n\n/** Type guard utility for determining if an entity field is a loaded `ReadOnlyCollection`. */\nexport function isLoadedReadOnlyCollection(\n maybeCollection: any,\n): maybeCollection is ReadOnlyCollection<any, any> & LoadedReadOnlyCollection<any, any> {\n return isReadOnlyCollection(maybeCollection) && maybeCollection.isLoaded;\n}\n"],"mappings":";;;;;;AAuBA,SAAgB,qBAAqB,iBAAuE;CAC1G,OACE,2BAA2BA,sCAAAA,kCAC3B,2BAA2BC,sCAAAA,mCAC3B,2BAA2BC,sCAAAA;AAE/B;;AAGA,SAAgB,2BACd,iBACsF;CACtF,OAAO,qBAAqB,eAAe,KAAK,gBAAgB;AAClE"}
@@ -1 +1 @@
1
- {"version":3,"file":"ReadOnlyCollection.js","names":[],"sources":["../../src/relations/ReadOnlyCollection.ts"],"sourcesContent":["import { type Entity } from \"../Entity.ts\";\nimport {\n RecursiveChildrenCollectionImpl,\n RecursiveM2mCollectionImpl,\n RecursiveParentsCollectionImpl,\n} from \"./RecursiveCollection.ts\";\nimport { type Relation } from \"./Relation.ts\";\n\n/** A collection of `U` within `T`, either one-to-many or many-to-many. */\nexport interface ReadOnlyCollection<T extends Entity, U extends Entity> extends Relation<T, U> {\n load(opts?: { withDeleted: boolean }): Promise<ReadonlyArray<U>>;\n\n readonly isLoaded: boolean;\n}\n\n/** Adds a known-safe `get` accessor. */\nexport interface LoadedReadOnlyCollection<T extends Entity, U extends Entity> extends ReadOnlyCollection<T, U> {\n get: ReadonlyArray<U>;\n\n getWithDeleted: ReadonlyArray<U>;\n}\n\n/** Type guard utility for determining if an entity field is a `ReadOnlyCollection`. */\nexport function isReadOnlyCollection(maybeCollection: any): maybeCollection is ReadOnlyCollection<any, any> {\n return (\n maybeCollection instanceof RecursiveParentsCollectionImpl ||\n maybeCollection instanceof RecursiveChildrenCollectionImpl ||\n maybeCollection instanceof RecursiveM2mCollectionImpl\n );\n}\n\n/** Type guard utility for determining if an entity field is a loaded `ReadOnlyCollection`. */\nexport function isLoadedReadOnlyCollection(\n maybeCollection: any,\n): maybeCollection is ReadOnlyCollection<any, any> & LoadedReadOnlyCollection<any, any> {\n return isReadOnlyCollection(maybeCollection) && maybeCollection.isLoaded;\n}\n"],"mappings":";;;;;AAuBA,SAAgB,qBAAqB,iBAAuE;CAC1G,OACE,2BAA2B,kCAC3B,2BAA2B,mCAC3B,2BAA2B;AAE/B;;AAGA,SAAgB,2BACd,iBACsF;CACtF,OAAO,qBAAqB,eAAe,KAAK,gBAAgB;AAClE"}
1
+ {"version":3,"file":"ReadOnlyCollection.js","names":[],"sources":["../../src/relations/ReadOnlyCollection.ts"],"sourcesContent":["import { type Entity } from \"src/Entity.ts\";\nimport {\n RecursiveChildrenCollectionImpl,\n RecursiveM2mCollectionImpl,\n RecursiveParentsCollectionImpl,\n} from \"src/relations/RecursiveCollection.ts\";\nimport { type Relation } from \"src/relations/Relation.ts\";\n\n/** A collection of `U` within `T`, either one-to-many or many-to-many. */\nexport interface ReadOnlyCollection<T extends Entity, U extends Entity> extends Relation<T, U> {\n load(opts?: { withDeleted: boolean }): Promise<ReadonlyArray<U>>;\n\n readonly isLoaded: boolean;\n}\n\n/** Adds a known-safe `get` accessor. */\nexport interface LoadedReadOnlyCollection<T extends Entity, U extends Entity> extends ReadOnlyCollection<T, U> {\n get: ReadonlyArray<U>;\n\n getWithDeleted: ReadonlyArray<U>;\n}\n\n/** Type guard utility for determining if an entity field is a `ReadOnlyCollection`. */\nexport function isReadOnlyCollection(maybeCollection: any): maybeCollection is ReadOnlyCollection<any, any> {\n return (\n maybeCollection instanceof RecursiveParentsCollectionImpl ||\n maybeCollection instanceof RecursiveChildrenCollectionImpl ||\n maybeCollection instanceof RecursiveM2mCollectionImpl\n );\n}\n\n/** Type guard utility for determining if an entity field is a loaded `ReadOnlyCollection`. */\nexport function isLoadedReadOnlyCollection(\n maybeCollection: any,\n): maybeCollection is ReadOnlyCollection<any, any> & LoadedReadOnlyCollection<any, any> {\n return isReadOnlyCollection(maybeCollection) && maybeCollection.isLoaded;\n}\n"],"mappings":";;;;;AAuBA,SAAgB,qBAAqB,iBAAuE;CAC1G,OACE,2BAA2B,kCAC3B,2BAA2B,mCAC3B,2BAA2B;AAE/B;;AAGA,SAAgB,2BACd,iBACsF;CACtF,OAAO,qBAAqB,eAAe,KAAK,gBAAgB;AAClE"}