joist-core 2.4.0-next.2 → 2.4.0-next.21

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 (153) hide show
  1. package/build/EntityManager.cjs +25 -11
  2. package/build/EntityManager.cjs.map +1 -1
  3. package/build/EntityManager.d.cts +11 -4
  4. package/build/EntityManager.d.cts.map +1 -1
  5. package/build/EntityManager.d.mts +11 -4
  6. package/build/EntityManager.d.mts.map +1 -1
  7. package/build/EntityManager.js +25 -13
  8. package/build/EntityManager.js.map +1 -1
  9. package/build/EntityMetadata.cjs +0 -1
  10. package/build/EntityMetadata.cjs.map +1 -1
  11. package/build/EntityMetadata.d.cts +8 -4
  12. package/build/EntityMetadata.d.cts.map +1 -1
  13. package/build/EntityMetadata.d.mts +8 -4
  14. package/build/EntityMetadata.d.mts.map +1 -1
  15. package/build/EntityMetadata.js +0 -1
  16. package/build/EntityMetadata.js.map +1 -1
  17. package/build/IndexManager.cjs +23 -5
  18. package/build/IndexManager.cjs.map +1 -1
  19. package/build/IndexManager.d.cts.map +1 -1
  20. package/build/IndexManager.d.mts.map +1 -1
  21. package/build/IndexManager.js +23 -5
  22. package/build/IndexManager.js.map +1 -1
  23. package/build/batchloaders/populateBatchLoader.cjs +2 -2
  24. package/build/batchloaders/populateBatchLoader.js +2 -2
  25. package/build/batchloaders/recursiveChildrenBatchLoader.cjs +1 -1
  26. package/build/batchloaders/recursiveChildrenBatchLoader.js +1 -1
  27. package/build/batchloaders/recursiveM2mBatchLoader.cjs +1 -1
  28. package/build/batchloaders/recursiveM2mBatchLoader.js +1 -1
  29. package/build/batchloaders/recursiveParentsBatchLoader.cjs +1 -1
  30. package/build/batchloaders/recursiveParentsBatchLoader.js +1 -1
  31. package/build/config.cjs +74 -1
  32. package/build/config.cjs.map +1 -1
  33. package/build/config.d.cts +83 -2
  34. package/build/config.d.cts.map +1 -1
  35. package/build/config.d.mts +83 -2
  36. package/build/config.d.mts.map +1 -1
  37. package/build/config.js +74 -1
  38. package/build/config.js.map +1 -1
  39. package/build/configure.cjs +6 -2
  40. package/build/configure.cjs.map +1 -1
  41. package/build/configure.d.cts.map +1 -1
  42. package/build/configure.d.mts.map +1 -1
  43. package/build/configure.js +6 -2
  44. package/build/configure.js.map +1 -1
  45. package/build/dataloaders/findDataLoader.cjs +1 -1
  46. package/build/dataloaders/findDataLoader.js +1 -1
  47. package/build/dataloaders/findOrCreateDataLoader.cjs +6 -2
  48. package/build/dataloaders/findOrCreateDataLoader.cjs.map +1 -1
  49. package/build/dataloaders/findOrCreateDataLoader.d.cts.map +1 -1
  50. package/build/dataloaders/findOrCreateDataLoader.d.mts.map +1 -1
  51. package/build/dataloaders/findOrCreateDataLoader.js +7 -3
  52. package/build/dataloaders/findOrCreateDataLoader.js.map +1 -1
  53. package/build/dataloaders/findPaginatedDataLoader.cjs +1 -1
  54. package/build/dataloaders/findPaginatedDataLoader.js +1 -1
  55. package/build/defaults.cjs +2 -2
  56. package/build/defaults.js +2 -2
  57. package/build/fields.cjs +1 -0
  58. package/build/fields.cjs.map +1 -1
  59. package/build/fields.d.cts.map +1 -1
  60. package/build/fields.d.mts.map +1 -1
  61. package/build/fields.js +1 -0
  62. package/build/fields.js.map +1 -1
  63. package/build/index.cjs +13 -8
  64. package/build/index.cjs.map +1 -1
  65. package/build/index.d.cts +4 -3
  66. package/build/index.d.cts.map +1 -1
  67. package/build/index.d.mts +4 -3
  68. package/build/index.d.mts.map +1 -1
  69. package/build/index.js +13 -10
  70. package/build/index.js.map +1 -1
  71. package/build/json.cjs +3 -3
  72. package/build/json.js +3 -3
  73. package/build/loading/loadHints.cjs +1 -1
  74. package/build/loading/loadHints.js +1 -1
  75. package/build/loading/withLoaded.cjs +2 -2
  76. package/build/loading/withLoaded.js +2 -2
  77. package/build/newTestInstance.cjs +1 -0
  78. package/build/newTestInstance.cjs.map +1 -1
  79. package/build/newTestInstance.d.cts.map +1 -1
  80. package/build/newTestInstance.d.mts.map +1 -1
  81. package/build/newTestInstance.js +1 -0
  82. package/build/newTestInstance.js.map +1 -1
  83. package/build/preloading/partitionHint.cjs +1 -1
  84. package/build/preloading/partitionHint.js +1 -1
  85. package/build/queries/sql/execute.cjs +68 -89
  86. package/build/queries/sql/execute.cjs.map +1 -1
  87. package/build/queries/sql/execute.d.cts +35 -18
  88. package/build/queries/sql/execute.d.cts.map +1 -1
  89. package/build/queries/sql/execute.d.mts +35 -18
  90. package/build/queries/sql/execute.d.mts.map +1 -1
  91. package/build/queries/sql/execute.js +70 -91
  92. package/build/queries/sql/execute.js.map +1 -1
  93. package/build/queries/sql/query.cjs +77 -30
  94. package/build/queries/sql/query.cjs.map +1 -1
  95. package/build/queries/sql/query.d.cts +64 -21
  96. package/build/queries/sql/query.d.cts.map +1 -1
  97. package/build/queries/sql/query.d.mts +64 -21
  98. package/build/queries/sql/query.d.mts.map +1 -1
  99. package/build/queries/sql/query.js +75 -30
  100. package/build/queries/sql/query.js.map +1 -1
  101. package/build/reactivity/ReactionsManager.cjs +168 -2
  102. package/build/reactivity/ReactionsManager.cjs.map +1 -1
  103. package/build/reactivity/ReactionsManager.d.cts +54 -1
  104. package/build/reactivity/ReactionsManager.d.cts.map +1 -1
  105. package/build/reactivity/ReactionsManager.d.mts +54 -1
  106. package/build/reactivity/ReactionsManager.d.mts.map +1 -1
  107. package/build/reactivity/ReactionsManager.js +169 -3
  108. package/build/reactivity/ReactionsManager.js.map +1 -1
  109. package/build/reactivity/reactiveHints.cjs +3 -3
  110. package/build/reactivity/reactiveHints.cjs.map +1 -1
  111. package/build/reactivity/reactiveHints.d.cts.map +1 -1
  112. package/build/reactivity/reactiveHints.d.mts.map +1 -1
  113. package/build/reactivity/reactiveHints.js +3 -3
  114. package/build/reactivity/reactiveHints.js.map +1 -1
  115. package/build/relations/AsyncReactiveField.cjs +3 -3
  116. package/build/relations/AsyncReactiveField.js +3 -3
  117. package/build/relations/LazyField.cjs +14 -0
  118. package/build/relations/LazyField.cjs.map +1 -1
  119. package/build/relations/LazyField.d.cts +6 -0
  120. package/build/relations/LazyField.d.cts.map +1 -1
  121. package/build/relations/LazyField.d.mts +6 -0
  122. package/build/relations/LazyField.d.mts.map +1 -1
  123. package/build/relations/LazyField.js +14 -0
  124. package/build/relations/LazyField.js.map +1 -1
  125. package/build/relations/ReactiveField.cjs +2 -2
  126. package/build/relations/ReactiveField.js +2 -2
  127. package/build/relations/ReactiveManyToMany.cjs +1 -1
  128. package/build/relations/ReactiveManyToMany.js +1 -1
  129. package/build/relations/ReactiveReference.cjs +1 -1
  130. package/build/relations/ReactiveReference.js +1 -1
  131. package/build/relations/RecursiveCollection.cjs +3 -3
  132. package/build/relations/RecursiveCollection.js +3 -3
  133. package/build/relations/hasProperty.cjs +2 -2
  134. package/build/relations/hasProperty.js +2 -2
  135. package/build/relations/index.cjs +5 -5
  136. package/build/relations/index.js +5 -5
  137. package/build/resurrection.cjs +11 -1
  138. package/build/resurrection.cjs.map +1 -1
  139. package/build/resurrection.d.cts +4 -1
  140. package/build/resurrection.d.cts.map +1 -1
  141. package/build/resurrection.d.mts +4 -1
  142. package/build/resurrection.d.mts.map +1 -1
  143. package/build/resurrection.js +11 -2
  144. package/build/resurrection.js.map +1 -1
  145. package/build/transitions.cjs +89 -0
  146. package/build/transitions.cjs.map +1 -0
  147. package/build/transitions.d.cts +110 -0
  148. package/build/transitions.d.cts.map +1 -0
  149. package/build/transitions.d.mts +110 -0
  150. package/build/transitions.d.mts.map +1 -0
  151. package/build/transitions.js +84 -0
  152. package/build/transitions.js.map +1 -0
  153. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"reactiveHints.d.cts","names":[],"sources":["../../src/reactivity/reactiveHints.ts"],"mappings":";;;;;;;;;;;;;;;;;;KA8CY,UAAU,UAAU,UAE9B,SAAS,KAGP,SAAS,KACT,SAAS,KACT,iBAAiB,KACjB,iBAAiB,KACjB,iBAAiB;;KAGT,SAAS,UAAU,uBAClB,WAAW,KAAK,cAAc,EAAE,4BAA4B,IAAI,cAAc,EAAE;KAGxF,cAAc,KAAK,UAAU,0BAA0B,KAAK;;KAG5D,iBAAiB,UAAU,aAC7B,WAAW,SAAS,kBAAkB,IAAI,sBAAsB,SAAS,GAAG;KAG1E,iBAAiB,UAAU,aAC7B,WAAW,SAAS,kBAAkB,IAAI,sBAAsB,SAAS,GAAG;KAG1E,iBAAiB,UAAU,aAC7B,WAAW,SAAS,kBAAkB,IAAI,sBAAsB,SAAS,GAAG;;;;;;;;KAUnE,aAAa,UAAU,iBACxB,UAAU,eACjB,oBAAoB,UAAU,eAC9B,mBAAmB;KAEX,mBAAmB,UAAU,aACtC,WAAW,UAAU,MAAM,UAAU,GAAG,kBAAiB,UAAU,UAAS,aAAa;;KAIhF,QAAQ,UAAU,QAAQ,KAAK,YACxC,WAAW,cAAc,WAAW,IAAI,EAAE,WAAW,6BAA6B,KAC/E,wBAAwB,GAAG,SAAS,QAAQ,GAAG,cAAc,GAAG,OAGhE,EAAE,WAAW,gCAAgC,SAAS,KACpD,qBAAqB,GAAG,GAAG,KAAK,gBAAgB,GAAG,SAAS,QAAQ,GAAG,cAAc,GAAG,KAAK,KAC7F,EAAE,WAAW,qBAAqB,SAAS,KACzC,gBAAgB,GAAG,SAAS,QAAQ,GAAG,cAAc,GAAG,KAAK,KAC7D,EAAE,WAAW,0BAA0B,KACrC,qBAAqB,GAAG,KACxB,EAAE,WAAW,sBAAsB,KACjC,iBAAiB,GAAG,SAAS,QAAQ,GAAG,cAAc,GAAG,OACzD,EAAE,WAAW,8BAA8B,KACzC,yBAAyB,GAAG,SAAS,QAAQ,GAAG,cAAc,GAAG,OACjE,EAAE,WAAW,oBAAoB,KAC/B,oBAAoB,KACpB,EAAE;;;;;;;;EASlB,uBAAuB,OAAO,GAAG;;EAEjC,SAAS,QAAQ,UAAU,SAAS,KAAK,YAAY,WAAW,cAAc;IAC5E,qBAAqB;;KAGb,mBAAmB,KAAK,UAAU;EAAW,uBAAuB;IAAM;;;;;;;;KAS1E,2BAA2B,KAAK,iBAAiB,WAAW,sBACpE,mBAAmB,kBACnB,mBAAmB;;;;;;;KAQX,oBAAoB,UAAU,iBAC/B,SAAS,eAChB,oBAAoB,SAAS;;KAGrB,qBAAqB,qCAAqC;EAChE,iBAAiB;;;;;;;;;;;;iBAaP,oBAAoB,UAAU,QAC5C,UAAU,+BAA+B,IACzC,YAAY,+BAA+B,IAC3C,MAAM,aAAa,IACnB,4BACA,4EACA,oBACC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA+SmB,kBACpB,sBACA,UAAU,UACV,wBACC,QAAQ;;iBAuEK,kBAAkB,UAAU,QAC1C,MAAM,gBACN,MAAM,aAAa,IACnB,4BACC,SAAS;;UAyEK;EACf;;EAEA,QAAQ;;EAER;;EAEA;;;;;;;;iBA+Bc,gBAAgB,QAAQ,QAAQ;iBAKhC,WAAW;iBAIX,0BAA0B;;iBAM1B,4BAA4B,QAAQ,QAAQ"}
1
+ {"version":3,"file":"reactiveHints.d.cts","names":[],"sources":["../../src/reactivity/reactiveHints.ts"],"mappings":";;;;;;;;;;;;;;;;;;KA8CY,UAAU,UAAU,UAE9B,SAAS,KAGP,SAAS,KACT,SAAS,KACT,iBAAiB,KACjB,iBAAiB,KACjB,iBAAiB;;KAGT,SAAS,UAAU,uBAClB,WAAW,KAAK,cAAc,EAAE,4BAA4B,IAAI,cAAc,EAAE;KAGxF,cAAc,KAAK,UAAU,0BAA0B,KAAK;;KAG5D,iBAAiB,UAAU,aAC7B,WAAW,SAAS,kBAAkB,IAAI,sBAAsB,SAAS,GAAG;KAG1E,iBAAiB,UAAU,aAC7B,WAAW,SAAS,kBAAkB,IAAI,sBAAsB,SAAS,GAAG;KAG1E,iBAAiB,UAAU,aAC7B,WAAW,SAAS,kBAAkB,IAAI,sBAAsB,SAAS,GAAG;;;;;;;;KAUnE,aAAa,UAAU,iBACxB,UAAU,eACjB,oBAAoB,UAAU,eAC9B,mBAAmB;KAEX,mBAAmB,UAAU,aACtC,WAAW,UAAU,MAAM,UAAU,GAAG,kBAAiB,UAAU,UAAS,aAAa;;KAIhF,QAAQ,UAAU,QAAQ,KAAK,YACxC,WAAW,cAAc,WAAW,IAAI,EAAE,WAAW,6BAA6B,KAC/E,wBAAwB,GAAG,SAAS,QAAQ,GAAG,cAAc,GAAG,OAGhE,EAAE,WAAW,gCAAgC,SAAS,KACpD,qBAAqB,GAAG,GAAG,KAAK,gBAAgB,GAAG,SAAS,QAAQ,GAAG,cAAc,GAAG,KAAK,KAC7F,EAAE,WAAW,qBAAqB,SAAS,KACzC,gBAAgB,GAAG,SAAS,QAAQ,GAAG,cAAc,GAAG,KAAK,KAC7D,EAAE,WAAW,0BAA0B,KACrC,qBAAqB,GAAG,KACxB,EAAE,WAAW,sBAAsB,KACjC,iBAAiB,GAAG,SAAS,QAAQ,GAAG,cAAc,GAAG,OACzD,EAAE,WAAW,8BAA8B,KACzC,yBAAyB,GAAG,SAAS,QAAQ,GAAG,cAAc,GAAG,OACjE,EAAE,WAAW,oBAAoB,KAC/B,oBAAoB,KACpB,EAAE;;;;;;;;EASlB,uBAAuB,OAAO,GAAG;;EAEjC,SAAS,QAAQ,UAAU,SAAS,KAAK,YAAY,WAAW,cAAc;IAC5E,qBAAqB;;KAGb,mBAAmB,KAAK,UAAU;EAAW,uBAAuB;IAAM;;;;;;;;KAS1E,2BAA2B,KAAK,iBAAiB,WAAW,sBACpE,mBAAmB,kBACnB,mBAAmB;;;;;;;KAQX,oBAAoB,UAAU,iBAC/B,SAAS,eAChB,oBAAoB,SAAS;;KAGrB,qBAAqB,qCAAqC;EAChE,iBAAiB;;;;;;;;;;;;iBAaP,oBAAoB,UAAU,QAC5C,UAAU,+BAA+B,IACzC,YAAY,+BAA+B,IAC3C,MAAM,aAAa,IACnB,4BACA,4EACA,oBACC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA+SmB,kBACpB,sBACA,UAAU,UACV,wBACC,QAAQ;;iBAuEK,kBAAkB,UAAU,QAC1C,MAAM,gBACN,MAAM,aAAa,IACnB,4BACC,SAAS;;UA0EK;EACf;;EAEA,QAAQ;;EAER;;EAEA;;;;;;;;iBA+Bc,gBAAgB,QAAQ,QAAQ;iBAKhC,WAAW;iBAIX,0BAA0B;;iBAM1B,4BAA4B,QAAQ,QAAQ"}
@@ -1 +1 @@
1
- {"version":3,"file":"reactiveHints.d.mts","names":[],"sources":["../../src/reactivity/reactiveHints.ts"],"mappings":";;;;;;;;;;;;;;;;;;KA8CY,UAAU,UAAU,UAE9B,SAAS,KAGP,SAAS,KACT,SAAS,KACT,iBAAiB,KACjB,iBAAiB,KACjB,iBAAiB;;KAGT,SAAS,UAAU,uBAClB,WAAW,KAAK,cAAc,EAAE,4BAA4B,IAAI,cAAc,EAAE;KAGxF,cAAc,KAAK,UAAU,0BAA0B,KAAK;;KAG5D,iBAAiB,UAAU,aAC7B,WAAW,SAAS,kBAAkB,IAAI,sBAAsB,SAAS,GAAG;KAG1E,iBAAiB,UAAU,aAC7B,WAAW,SAAS,kBAAkB,IAAI,sBAAsB,SAAS,GAAG;KAG1E,iBAAiB,UAAU,aAC7B,WAAW,SAAS,kBAAkB,IAAI,sBAAsB,SAAS,GAAG;;;;;;;;KAUnE,aAAa,UAAU,iBACxB,UAAU,eACjB,oBAAoB,UAAU,eAC9B,mBAAmB;KAEX,mBAAmB,UAAU,aACtC,WAAW,UAAU,MAAM,UAAU,GAAG,kBAAiB,UAAU,UAAS,aAAa;;KAIhF,QAAQ,UAAU,QAAQ,KAAK,YACxC,WAAW,cAAc,WAAW,IAAI,EAAE,WAAW,6BAA6B,KAC/E,wBAAwB,GAAG,SAAS,QAAQ,GAAG,cAAc,GAAG,OAGhE,EAAE,WAAW,gCAAgC,SAAS,KACpD,qBAAqB,GAAG,GAAG,KAAK,gBAAgB,GAAG,SAAS,QAAQ,GAAG,cAAc,GAAG,KAAK,KAC7F,EAAE,WAAW,qBAAqB,SAAS,KACzC,gBAAgB,GAAG,SAAS,QAAQ,GAAG,cAAc,GAAG,KAAK,KAC7D,EAAE,WAAW,0BAA0B,KACrC,qBAAqB,GAAG,KACxB,EAAE,WAAW,sBAAsB,KACjC,iBAAiB,GAAG,SAAS,QAAQ,GAAG,cAAc,GAAG,OACzD,EAAE,WAAW,8BAA8B,KACzC,yBAAyB,GAAG,SAAS,QAAQ,GAAG,cAAc,GAAG,OACjE,EAAE,WAAW,oBAAoB,KAC/B,oBAAoB,KACpB,EAAE;;;;;;;;EASlB,uBAAuB,OAAO,GAAG;;EAEjC,SAAS,QAAQ,UAAU,SAAS,KAAK,YAAY,WAAW,cAAc;IAC5E,qBAAqB;;KAGb,mBAAmB,KAAK,UAAU;EAAW,uBAAuB;IAAM;;;;;;;;KAS1E,2BAA2B,KAAK,iBAAiB,WAAW,sBACpE,mBAAmB,kBACnB,mBAAmB;;;;;;;KAQX,oBAAoB,UAAU,iBAC/B,SAAS,eAChB,oBAAoB,SAAS;;KAGrB,qBAAqB,qCAAqC;EAChE,iBAAiB;;;;;;;;;;;;iBAaP,oBAAoB,UAAU,QAC5C,UAAU,+BAA+B,IACzC,YAAY,+BAA+B,IAC3C,MAAM,aAAa,IACnB,4BACA,4EACA,oBACC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA+SmB,kBACpB,sBACA,UAAU,UACV,wBACC,QAAQ;;iBAuEK,kBAAkB,UAAU,QAC1C,MAAM,gBACN,MAAM,aAAa,IACnB,4BACC,SAAS;;UAyEK;EACf;;EAEA,QAAQ;;EAER;;EAEA;;;;;;;;iBA+Bc,gBAAgB,QAAQ,QAAQ;iBAKhC,WAAW;iBAIX,0BAA0B;;iBAM1B,4BAA4B,QAAQ,QAAQ"}
1
+ {"version":3,"file":"reactiveHints.d.mts","names":[],"sources":["../../src/reactivity/reactiveHints.ts"],"mappings":";;;;;;;;;;;;;;;;;;KA8CY,UAAU,UAAU,UAE9B,SAAS,KAGP,SAAS,KACT,SAAS,KACT,iBAAiB,KACjB,iBAAiB,KACjB,iBAAiB;;KAGT,SAAS,UAAU,uBAClB,WAAW,KAAK,cAAc,EAAE,4BAA4B,IAAI,cAAc,EAAE;KAGxF,cAAc,KAAK,UAAU,0BAA0B,KAAK;;KAG5D,iBAAiB,UAAU,aAC7B,WAAW,SAAS,kBAAkB,IAAI,sBAAsB,SAAS,GAAG;KAG1E,iBAAiB,UAAU,aAC7B,WAAW,SAAS,kBAAkB,IAAI,sBAAsB,SAAS,GAAG;KAG1E,iBAAiB,UAAU,aAC7B,WAAW,SAAS,kBAAkB,IAAI,sBAAsB,SAAS,GAAG;;;;;;;;KAUnE,aAAa,UAAU,iBACxB,UAAU,eACjB,oBAAoB,UAAU,eAC9B,mBAAmB;KAEX,mBAAmB,UAAU,aACtC,WAAW,UAAU,MAAM,UAAU,GAAG,kBAAiB,UAAU,UAAS,aAAa;;KAIhF,QAAQ,UAAU,QAAQ,KAAK,YACxC,WAAW,cAAc,WAAW,IAAI,EAAE,WAAW,6BAA6B,KAC/E,wBAAwB,GAAG,SAAS,QAAQ,GAAG,cAAc,GAAG,OAGhE,EAAE,WAAW,gCAAgC,SAAS,KACpD,qBAAqB,GAAG,GAAG,KAAK,gBAAgB,GAAG,SAAS,QAAQ,GAAG,cAAc,GAAG,KAAK,KAC7F,EAAE,WAAW,qBAAqB,SAAS,KACzC,gBAAgB,GAAG,SAAS,QAAQ,GAAG,cAAc,GAAG,KAAK,KAC7D,EAAE,WAAW,0BAA0B,KACrC,qBAAqB,GAAG,KACxB,EAAE,WAAW,sBAAsB,KACjC,iBAAiB,GAAG,SAAS,QAAQ,GAAG,cAAc,GAAG,OACzD,EAAE,WAAW,8BAA8B,KACzC,yBAAyB,GAAG,SAAS,QAAQ,GAAG,cAAc,GAAG,OACjE,EAAE,WAAW,oBAAoB,KAC/B,oBAAoB,KACpB,EAAE;;;;;;;;EASlB,uBAAuB,OAAO,GAAG;;EAEjC,SAAS,QAAQ,UAAU,SAAS,KAAK,YAAY,WAAW,cAAc;IAC5E,qBAAqB;;KAGb,mBAAmB,KAAK,UAAU;EAAW,uBAAuB;IAAM;;;;;;;;KAS1E,2BAA2B,KAAK,iBAAiB,WAAW,sBACpE,mBAAmB,kBACnB,mBAAmB;;;;;;;KAQX,oBAAoB,UAAU,iBAC/B,SAAS,eAChB,oBAAoB,SAAS;;KAGrB,qBAAqB,qCAAqC;EAChE,iBAAiB;;;;;;;;;;;;iBAaP,oBAAoB,UAAU,QAC5C,UAAU,+BAA+B,IACzC,YAAY,+BAA+B,IAC3C,MAAM,aAAa,IACnB,4BACA,4EACA,oBACC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA+SmB,kBACpB,sBACA,UAAU,UACV,wBACC,QAAQ;;iBAuEK,kBAAkB,UAAU,QAC1C,MAAM,gBACN,MAAM,aAAa,IACnB,4BACC,SAAS;;UA0EK;EACf;;EAEA,QAAQ;;EAER;;EAEA;;;;;;;;iBA+Bc,gBAAgB,QAAQ,QAAQ;iBAKhC,WAAW;iBAIX,0BAA0B;;iBAM1B,4BAA4B,QAAQ,QAAQ"}
@@ -1,10 +1,10 @@
1
- import { normalizeHint, suffixRe } from "../normalizeHints.js";
1
+ import { PropertyImpl } from "../relations/hasProperty.js";
2
2
  import { ReactiveGetterImpl } from "../relations/ReactiveGetter.js";
3
3
  import { RecursiveChildrenCollectionImpl, RecursiveM2mCollectionImpl, RecursiveParentsCollectionImpl } from "../relations/RecursiveCollection.js";
4
- import { PropertyImpl } from "../relations/hasProperty.js";
5
4
  import "../relations/index.js";
6
5
  import { fail, flatAndUnique, mergeNormalizedHints } from "../utils.js";
7
6
  import { getProperties } from "../getProperties.js";
7
+ import { normalizeHint, suffixRe } from "../normalizeHints.js";
8
8
  import { getBaseAndSelfMetas, getBaseMeta, getMetadata } from "../EntityMetadata.js";
9
9
  import { getEmInternalApi, isId } from "../EntityManager.js";
10
10
  import { isEntity } from "../Entity.js";
@@ -237,7 +237,7 @@ function convertToLoadHint(meta, hint, allowCustomKeys = false) {
237
237
  break;
238
238
  case "primitive":
239
239
  case "enum":
240
- if (field.derived === "async") mergeNormalizedHints(loadHint, { [key]: {} });
240
+ if (field.derived === "async" || field.kind === "primitive" && field.lazy) mergeNormalizedHints(loadHint, { [key]: {} });
241
241
  continue;
242
242
  case "m2mEnum":
243
243
  mergeNormalizedHints(loadHint, { [key]: {} });
@@ -1 +1 @@
1
- {"version":3,"file":"reactiveHints.js","names":[],"sources":["../../src/reactivity/reactiveHints.ts"],"sourcesContent":["import { getInstanceData } from \"src/BaseEntity.ts\";\nimport type { Changes } from \"src/changes.ts\";\nimport { type Entity, isEntity } from \"src/Entity.ts\";\nimport { type MaybeAbstractEntityConstructor, getEmInternalApi, isId } from \"src/EntityManager.ts\";\nimport {\n type EntityMetadata,\n type ManyToManyField,\n type ManyToOneField,\n type OneToManyField,\n type OneToOneField,\n type PolymorphicFieldComponent,\n getBaseAndSelfMetas,\n getBaseMeta,\n getMetadata,\n} from \"src/EntityMetadata.ts\";\nimport { getProperties } from \"src/getProperties.ts\";\nimport type { LoadHint, Loadable, Loaded } from \"src/loading/loadHints.ts\";\nimport { type NormalizeHint, type SuffixSeperator, normalizeHint, suffixRe } from \"src/normalizeHints.ts\";\nimport {\n type Collection,\n type EnumCollection,\n type LoadedCollection,\n type LoadedEnumCollection,\n type LoadedProperty,\n type LoadedReadOnlyCollection,\n type LoadedReference,\n type ManyToManyCollection,\n type OneToOneReference,\n type PolymorphicReference,\n type Property,\n PropertyImpl,\n type ReactiveGetter,\n type ReadOnlyCollection,\n type Reference,\n} from \"src/relations/index.ts\";\nimport type { LoadedOneToOneReference } from \"src/relations/OneToOneReference.ts\";\nimport { ReactiveGetterImpl } from \"src/relations/ReactiveGetter.ts\";\nimport {\n RecursiveChildrenCollectionImpl,\n RecursiveM2mCollectionImpl,\n RecursiveParentsCollectionImpl,\n} from \"src/relations/RecursiveCollection.ts\";\nimport type { FieldsOf, RelationsOf } from \"src/typeMap.ts\";\nimport { fail, flatAndUnique, mergeNormalizedHints } from \"src/utils.ts\";\n\n/** The keys in `T` that rules & hooks can react to. */\nexport type Reactable<T extends Entity> =\n // This will be primitives + enums + m2os\n FieldsOf<T> &\n // We include `Loadable` so that we include hasReactiveProperties,\n // which are reversable but won't be in any of our codegen types.\n Loadable<T> &\n Gettable<T> &\n SuffixedFieldsOf<T> &\n SuffixedLoadable<T> &\n SuffixedGettable<T>;\n\n/** Finds `hasReactiveGetters` which are not \"loadable\" b/c they're always loaded. */\nexport type Gettable<T extends Entity> = {\n -readonly [K in keyof T as GettableValue<T[K]> extends never ? never : K]: GettableValue<T[K]>;\n};\n\ntype GettableValue<V> = V extends ReactiveGetter<any, infer P> ? P : never;\n\n/** The fields of `T` suffixed with `:ro` or `_ro`. */\ntype SuffixedFieldsOf<T extends Entity> = {\n [K in keyof FieldsOf<T> & string as `${K}${SuffixSeperator}ro`]: FieldsOf<T>[K];\n};\n\ntype SuffixedLoadable<T extends Entity> = {\n [K in keyof Loadable<T> & string as `${K}${SuffixSeperator}ro`]: Loadable<T>[K];\n};\n\ntype SuffixedGettable<T extends Entity> = {\n [K in keyof Gettable<T> & string as `${K}${SuffixSeperator}ro`]: Gettable<T>[K];\n};\n\n/**\n * A reactive hint of a single key, multiple keys, or nested keys and sub-hints.\n *\n * Reactive hints are different from load hints in that reactive hints include specific\n * fields to react against, while load hints contain only relations, collections, and async\n * properties to preload.\n */\nexport type ReactiveHint<T extends Entity> =\n | (keyof Reactable<T> & string)\n | ReadonlyArray<keyof Reactable<T> & string>\n | NestedReactiveHint<T>;\n\nexport type NestedReactiveHint<T extends Entity> = {\n [K in keyof Reactable<T>]?: Reactable<T>[K] extends infer U extends Entity ? ReactiveHint<U> : {};\n};\n\n/** Given an entity `T` that is being reacted with hint `H`, mark only the `H` attributes visible & populated. */\nexport type Reacted<T extends Entity, H> = Entity & {\n [K in keyof NormalizeHint<H> & keyof T]: T[K] extends OneToOneReference<any, infer U>\n ? LoadedOneToOneReference<T, Entity & Reacted<U, NormalizeHint<H>[K]>>\n : // Add an explicit check for PolymorphicReference because it can add `PolymorphicReference &` which gives access to `idIfSet`\n // ...although maybe LoadedReference should just have `idIfSet` in it as well? LoadedOneToOneReference does...\n T[K] extends PolymorphicReference<any, infer U, infer N>\n ? PolymorphicReference<T, U, N> & LoadedReference<T, Entity & Reacted<U, NormalizeHint<H>[K]>, N>\n : T[K] extends Reference<any, infer U, infer N>\n ? LoadedReference<T, Entity & Reacted<U, NormalizeHint<H>[K]>, N>\n : T[K] extends EnumCollection<any, infer E>\n ? LoadedEnumCollection<T, E>\n : T[K] extends Collection<any, infer U>\n ? LoadedCollection<T, Entity & Reacted<U, NormalizeHint<H>[K]>>\n : T[K] extends ReadOnlyCollection<any, infer U>\n ? LoadedReadOnlyCollection<T, Entity & Reacted<U, NormalizeHint<H>[K]>>\n : T[K] extends Property<any, infer V>\n ? LoadedProperty<any, V>\n : T[K];\n} & {\n /**\n * Gives reactive rules & fields a way to get the full entity if they really need it.\n *\n * This should be used carefully b/c it will side-step the reactive system, and instead\n * reactive rules & fields should explicitly declare access to the fields they need, which\n * will then allow accessing the field via the `Reacted<Author, ...>` type.\n */\n fullNonReactiveAccess: Loaded<T, H>;\n /** Allow detecting if a reactive change is due to nuances like `hasUpdated` or `hasChanged`. */\n changes: Changes<T, keyof (FieldsOf<T> & RelationsOf<T>), keyof NormalizeHint<H>>;\n} & MaybeTransientFields<T>;\n\n/** Allow returning reacted entities from `hasReactiveReference`. */\nexport type MaybeReactedEntity<V> = V extends Entity ? { fullNonReactiveAccess: V } : V;\n\n/**\n * Allow returning reacted entities from `hasReactiveProperty`.\n *\n * The `MaybeReactedEntity` is used by `hasReactiveReference` which already has `undefined | never`\n * broken out as a separate generic; `hasReactiveProperty` does not, and so this type conditionally\n * pulls `undefined` out first, and then defers to `MaybeReactedEntity`.\n */\nexport type MaybeReactedPropertyEntity<V> = V extends (infer V1 extends Entity) | undefined\n ? MaybeReactedEntity<V1> | undefined\n : MaybeReactedEntity<V>;\n\n/**\n * A reactive hint that only allows fields immediately on the entity, i.e. no nested hints.\n *\n * This is used by `ReactiveGetter`s to guarantee that their lambda functions will\n * always be `.get`-able and hence not need loading.\n */\nexport type ShallowReactiveHint<T extends Entity> =\n | (keyof FieldsOf<T> & string)\n | ReadonlyArray<keyof FieldsOf<T> & string>;\n\n/** If the domain model has transient fields, allow reactive behavior to see it, i.e. don't run validation rules for special operations. */\nexport type MaybeTransientFields<T> = \"transientFields\" extends keyof T\n ? { transientFields: T[\"transientFields\"] }\n : {};\n\n/**\n * Takes a reactive hint and returns the `ReactiveTarget`s that, should they change, need to\n * walk back to this watching reactable.\n *\n * @param rootType The original type that contained the reactive rule/field, used for helpful error messages\n * @param entityType The current entity of the step we're walking\n * @param hint The current hint we're walking\n * @param reactForOtherSide the name of our FK column, if the previous collection needs to react to our FK changing\n * @param isFirst\n */\nexport function reverseReactiveHint<T extends Entity>(\n rootType: MaybeAbstractEntityConstructor<T>,\n entityType: MaybeAbstractEntityConstructor<T>,\n hint: ReactiveHint<T>,\n reactForOtherSide?: string,\n reactForOtherSideIsReadOnly?: \"field-read-only\" | \"hint-read-only\" | false,\n isFirst: boolean = true,\n): ReactiveTarget[] {\n const meta = getMetadata(entityType);\n // This is the list of fields for this `entityType` that we will react to their changing values\n const fields: string[] = [];\n const readOnlyFields: string[] = [];\n const maybeRecursive: ReactiveTarget[] = [];\n\n // If the hint before us was a collection, i.e. { books: [\"title\"] }, besides just reacting\n // to our `title` changing, `reactForOtherSide` tells us to react to our `author` field as well.\n if (reactForOtherSide) {\n const _fields = reactForOtherSideIsReadOnly ? readOnlyFields : fields;\n _fields.push(reactForOtherSide);\n if (meta.timestampFields?.deletedAt) {\n _fields.push(meta.timestampFields.deletedAt);\n }\n }\n\n // Look through the hint for our own fields, i.e. `[\"firstName\", \"lastName]`, and nested hints like `{ books: \"title\" }`.\n const subHintTargets: ReactiveTarget[] = Object.entries(normalizeHint(hint)).flatMap(([keyMaybeSuffix, subHint]) => {\n return reverseSubHint(\n rootType,\n entityType,\n hint,\n meta,\n // Pass fields & readOnlyFields, so `reverseSubHint` can append any of our primitive fields it finds\n fields,\n readOnlyFields,\n maybeRecursive,\n keyMaybeSuffix, // maybeSuffix = might end with `_ro` like `name_ro`\n subHint,\n );\n });\n\n const myTargets: ReactiveTarget[] = [];\n // If any of our primitives (or m2o fields) change, establish a reactive path from \"here\" (entityType)\n // that is initially empty (path: []) but will have paths layered on by upstream callers\n if (fields.length > 0 || isFirst || reactForOtherSideIsReadOnly === \"field-read-only\") {\n myTargets.push({ kind: \"update\", entity: entityType, fields, path: [] });\n }\n // Normally read-only fields don't trigger true async reactivity, but IsLoadedCache\n // needs them for same-EM cache invalidation.\n if (readOnlyFields.length > 0) {\n myTargets.push({ kind: \"read-only\", entity: entityType, fields: readOnlyFields, path: [] });\n }\n\n return [...myTargets, ...maybeRecursive, ...subHintTargets];\n}\n\nfunction reverseSubHint(\n rootType: MaybeAbstractEntityConstructor<any>,\n entityType: MaybeAbstractEntityConstructor<any>,\n hint: any,\n meta: EntityMetadata,\n fields: string[],\n readOnlyFields: string[],\n maybeRecursive: ReactiveTarget[],\n keyMaybeSuffix: string,\n subHint: any,\n): ReactiveTarget[] {\n const key = keyMaybeSuffix.replace(suffixRe, \"\");\n const field = meta.allFields[key];\n const isReadOnly = !!keyMaybeSuffix.match(suffixRe) || (field && field.immutable);\n const _fields = isReadOnly ? readOnlyFields : fields;\n if (field) {\n switch (field.kind) {\n case \"m2o\": {\n // If this is a ReactiveReference, maybe we do something different?\n _fields.push(field.fieldName);\n const otherMeta = field.otherMetadata();\n // If this is a ReactiveReference, it won't have an \"other\" side, which is fine if:\n // - we're only reacting to the RR value itself (and have no sub-hints), or\n // - all of our sub-hints are read-only\n const otherField = otherMeta.allFields[field.otherFieldName];\n if (!otherField && !isAllReadOnly(subHint)) {\n throw new Error(\n `Invalid hint in ${rootType.name}.ts, we don't currently support reacting through ReactiveReferences, ${JSON.stringify(hint)}`,\n );\n }\n return reverseReactiveHint(rootType, otherMeta.cstr, subHint, undefined, false, false).map(\n appendPath(maybeAddTypeFilterSuffix(meta, field)),\n );\n }\n case \"poly\": {\n _fields.push(field.fieldName);\n // A poly is basically multiple m2os glued together, so copy/paste the `case m2o` code\n // above but do it for each component FK, and glue them together.\n return field.components.flatMap((comp) => {\n return reverseReactiveHint(rootType, comp.otherMetadata().cstr, subHint, undefined, false, false).map(\n appendPath(maybeAddTypeFilterSuffix(meta, comp)),\n );\n });\n }\n case \"m2m\": {\n // While o2m and o2o can watch for just FK changes by passing `reactForOtherSide` (the FK lives in the other\n // table), for m2m reactivity we push the collection name into the reactive hint, because it's effectively\n // \"the other/reverse side\", and JoinRows will trigger `queueDownstreamReactables` explicitly, instead of\n // `setField` which handles regular/non-m2m mutations.\n _fields.push(field.fieldName);\n const otherFieldName = maybeAddTypeFilterSuffix(meta, field);\n return reverseReactiveHint(\n rootType,\n field.otherMetadata().cstr,\n subHint,\n // For m2m, we can always pass undefined here, as otherwise having the opposite m2m collection\n // recalc all of its children will cause over-reactivity\n undefined,\n false,\n false,\n ).map(appendPath(otherFieldName));\n }\n case \"o2m\":\n case \"o2o\": {\n const isOtherReadOnly = field.otherMetadata().allFields[field.otherFieldName].immutable;\n const otherFieldName = maybeAddTypeFilterSuffix(meta, field);\n // This is not a field, but we want our reverse side to be reactive, so pass reactForOtherSide\n return reverseReactiveHint(\n rootType,\n field.otherMetadata().cstr,\n subHint,\n // IsLoadedCache invalidation always needs to know the otherField name, so we must\n // end up with either a kind=update or kind=read-only target with `otherFieldName` in it.\n field.otherFieldName,\n // We can have three outcomes:\n // - kind=update fields=[otherFieldName] (normal behavior)\n // - kind=update fields=[] + kind=read-only fields=[otherFieldName] (isOtherReadOnly)\n // - kind=read-only fields=[otherFieldName] (hint isReadOnly)\n isReadOnly ? \"hint-read-only\" : isOtherReadOnly ? \"field-read-only\" : false,\n false,\n ).map(appendPath(otherFieldName));\n }\n case \"primaryKey\":\n case \"primitive\":\n case \"enum\":\n _fields.push(key);\n return [];\n case \"m2mEnum\":\n // An enum m2m is a leaf of enum codes (no \"other side\" entity to recurse into); like a m2m,\n // `JoinRows` calls `queueDownstreamReactables` explicitly when its membership changes.\n _fields.push(key);\n return [];\n default:\n throw new Error(`Invalid hint in ${rootType.name}.ts hint ${JSON.stringify(hint)}`);\n }\n } else {\n // We only need to look for ReactiveProperties here, because ReactiveFields & ReactiveReferences\n // have underlying primitive fields that, when/if they change, will be handled in the ^ code.\n //\n // I.e. we specifically don't need to handle RFs & RRs ^, because the EntityManager.flush loop will\n // notice their primitive values changing, and kicking off any downstream reactive fields as necessary.\n const p = getProperties(meta)[key];\n if (p instanceof PropertyImpl) {\n // If the field is marked as readonly (i.e. using the `_ro` suffix), then we can assume that applies to its\n // entire hint as well and can simply omit it. This also allows us to use non-reactive async props as long as\n // they are readonly.\n if (isReadOnly) return [];\n if (!p.reactiveHint) {\n throw new Error(\n `Property ${key} cannot be used in reactive hints in ${rootType.name}.ts hint ${JSON.stringify(\n hint,\n )}, please use hasReactiveProperty instead`,\n );\n }\n return reverseReactiveHint(rootType, meta.cstr, p.reactiveHint, undefined, false, false);\n } else if (p instanceof ReactiveGetterImpl) {\n // If the field is marked as readonly, then we can assume that applies to its entire hint as well and can\n // simply omit it.\n if (isReadOnly) return [];\n return reverseReactiveHint(rootType, meta.cstr, p.reactiveHint, undefined, false, false);\n } else if (p instanceof RecursiveParentsCollectionImpl) {\n // I.e. if this is `Author.mentorsRecursive`:\n // - fieldName=mentorsRecursive (being watched by the caller)\n // - otherFieldName=menteesRecursive (our reversal for walking down)\n // - m2oFieldName = mentor\n //\n // Note that \"reversing parents\" is generally easier, b/c when the tree changes, any given node can\n // still \"walk down\" to find its children w/the \"current value\" childrenRecursive; and any just-now\n // orphaned parents will themselves trigger their own childrenRecursive changes (i.e. we do not need\n // any \"old reference value\" / \"new reference value\" handling).\n //\n // I.e. given a tree of `A is mentor of B is mentor of C`, when B changes mentors, and we need to\n // \"walk to all affected/children mentees, so they can observe their new mentorsRecursive\", merely\n // walking down from (b itself & it's mentees) will find all affected mentees.\n const { otherFieldName, m2oFieldName } = p;\n // When a mentor.firstName/etc field changes, walk to its transitive children (through otherFieldName)\n // via the `return reverseReactiveHint` line below.\n //\n // But if the `author.mentor` field itself changes, we ourselves are also \"a new mentee\" of the mentor, so\n // do `fields.push(m2oFieldName)` to notify our direct reactable (a new mentee), and then our transitive\n // children/mentees. This avoids going \"up to the mentor, and walking down all of his mentees\", which would go\n // through our siblings, which would not have changed/need reactivity.\n _fields.push(m2oFieldName);\n maybeRecursive.push({\n kind: isReadOnly ? \"read-only\" : \"update\",\n entity: entityType,\n fields: recursiveMembershipFields(meta, m2oFieldName),\n path: [otherFieldName],\n });\n // Any reactables who were watching `parentsRecursive`, we'll back down to by going through `childrenRecursive`\n return reverseReactiveHint(rootType, entityType, subHint, undefined, false).map(appendPath(otherFieldName));\n } else if (p instanceof RecursiveChildrenCollectionImpl) {\n const { otherFieldName, o2mFieldName } = p;\n // I.e. this is `Author.menteesRecursive`\n // - fieldName=menteesRecursive` (being watched by the caller)\n // - otherFieldName=mentorsRecursive` (our reversal for walking up)\n // - The o2m's otherFieldName gives us the FK field (e.g., \"mentor\") on the child entity.\n //\n // Note that \"reversing children\" is trickier than reversing parents, b/c when the tree changes,\n // we need to consider both \"old reference value\" and \"new reference value\" scenarios.\n const o2mField = meta.allFields[o2mFieldName] as OneToManyField | OneToOneField;\n const m2oFieldName = o2mField.otherFieldName;\n _fields.push(m2oFieldName);\n maybeRecursive.push({\n kind: isReadOnly ? \"read-only\" : \"update\",\n entity: entityType,\n fields: recursiveMembershipFields(meta, m2oFieldName), // i.e. parent\n path: [otherFieldName], // i.e. parentsRecursive to tell them \"observe your new childrenRecursive\"\n });\n // Any reactables who were watching `childrenRecursive`, we'll up to by going through `parentsRecursive`\n return reverseReactiveHint(rootType, entityType, subHint, undefined, false).map(appendPath(otherFieldName));\n } else if (p instanceof RecursiveM2mCollectionImpl) {\n // I.e. if this is `User.parentsRecursive`:\n // - fieldName=parentsRecursive (being watched by the caller)\n // - otherFieldName=childrenRecursive (our reversal for walking down)\n // - m2mFieldName=parents (the underlying m2m collection)\n //\n // For m2m recursive, the \"change signal\" is when join rows are added/removed, so we push\n // the m2m collection name into fields (like regular m2m handling). To find affected entities\n // that are watching `parentsRecursive`, we walk through `childrenRecursive` (the reverse direction).\n const { otherFieldName, m2mFieldName } = p;\n // When a join row changes (e.g. parents collection is mutated), notify both this entity\n // and its transitive children/parents via otherFieldName\n _fields.push(m2mFieldName);\n maybeRecursive.push({\n kind: isReadOnly ? \"read-only\" : \"update\",\n entity: entityType,\n fields: recursiveMembershipFields(meta, m2mFieldName),\n path: [otherFieldName],\n });\n // Any reactables who were watching this recursive collection, we'll reach via the other direction\n return reverseReactiveHint(rootType, entityType, subHint, undefined, false).map(appendPath(otherFieldName));\n } else {\n throw new Error(`Invalid hint in ${rootType.name}.ts ${JSON.stringify(hint)}`);\n }\n }\n}\n\nfunction maybeAddTypeFilterSuffix(\n meta: EntityMetadata,\n field: ManyToOneField | OneToManyField | ManyToManyField | OneToOneField | PolymorphicFieldComponent,\n): string {\n const otherField = field.otherMetadata().allFields[field.otherFieldName];\n if (!otherField) {\n // This is usually an error, unless if its ReactiveReference which we don't create o2m-s for\n const isReactiveReference = \"kind\" in field && field.kind === \"m2o\" && field.derived === \"async\";\n if (!isReactiveReference) fail(`No field ${field.otherMetadata().type}.${field.otherFieldName}`);\n return field.otherFieldName;\n }\n // If we're Foo, and the other field (which we'll traverse back from at runtime) is actually\n // a poly FK pointing back to multiple `owner=Foo | Bar | Zaz`, add a suffix of `@Foo` so\n // that any runtime traversal that hit `owner` and see a `Bar | Zaz` will stop.\n const nextFieldIsPoly = otherField.kind === \"poly\";\n // If we're a SubType, and the other field points to our base type, assume there might be\n // SubType-specific fields in the path so far, so tell the other side to only walk back through\n // us if its value is actually a SubType.\n const nextFieldPointsToBaseType =\n !!meta.baseType && otherField.kind !== \"poly\" && (otherField as any).otherMetadata().cstr !== meta.cstr;\n return nextFieldIsPoly || nextFieldPointsToBaseType ? `${field.otherFieldName}@${meta.type}` : field.otherFieldName;\n}\n\n/**\n * Walks `reverseHint` for every entity in `entities`.\n *\n * I.e. given `[book1, book2]` and `[\"author\", 'publisher\"]`, will return all the books' authors' publishers.\n *\n * Note that for references (and only references), we walk both through \"the current value\"\n * and prior values remembered in `referenceHistory`. This is fundamentally because references are the true \"owner\"\n * of the relation, while collections are derived.\n *\n * For example, with reversals walking through collections:\n *\n * - Given a hint `book: { author: firstName }`\n * - Which reverses to `author[firstName] -> books`\n * - When we walk through `a1.books`, we use only the current value, which might have only `[b2]` in it.\n * - We don't need to worry about \"the old a1.books\" value, which might have previously had `[b1]` in it,\n * because the `b1.author` changing authors (from `a1` to `a2`) will trigger its own reactivity.\n *\n * Contrast with reversals walking through references:\n *\n * - Given a hint `author: { books: \"title\" }`\n * - Which reverses to `book[title] -> author`\n * - When we traverse through `b1.author`, we use both the current value and remembered values, so that both\n * \"our prior authors\" and \"our new author\" see their latest `author.books` collection values.\n *\n * Delete cleanup runs before reverse hints are walked, so the current relation value may already be unset by the\n * time we get here. For persisted entities, `cleanupOnEntityDeleted` clears the owning FK via `setField`, which\n * remembers the prior FK in `referenceHistory`; this lets us still find the old owner. For collection reversals\n * this is enough because collections are derived from the owning FK: removing `b1` from `a1.books` is represented\n * by `b1.author` changing, not by an independent \"old books\" snapshot.\n *\n * References can also have transient values during a unit of work, i.e. a ReactiveField might observe `b1.author=a2`\n * before the final flush changes it to `a3`. `setField` remembers both original and transient reference values\n * separately from `originalData`, and we walk them in addition to the current value.\n */\nexport async function followReverseHint(\n reactionName: string,\n entities: Entity[],\n reverseHint: string[],\n): Promise<Entity[]> {\n // Start at the current (unique) entities\n let current = new Set(entities);\n const paths = [...reverseHint];\n // And \"walk backwards\" through the reverse hint\n while (paths.length) {\n const path = paths.shift()!;\n const [fieldName, viaType] = path.split(\"@\");\n const promises = new Array(current.size);\n // The path might touch either a reference or a collection\n for (const c of current as Set<any>) {\n const relation = c[fieldName];\n // If `!relation`, I'd really like to `fail`, but atm we're knowingly creating invalid reversals for when\n // a ReactiveField depends on a ReactiveReference; b/c RRs lack an \"other side\", we cannot walk them in\n // reverse, and so for awhile we \"supported\" the RF -> RR pattern by relying on a totally-read-only reactive\n // hint -> dropped during reversal.\n //\n // However, now IsLoadedCache means that even these \"totally read only\" RFs need to be reactive at least\n // during their initial EM creation, to avoid stale values being observed during first-flush mutations,\n // and so we're now stuck with these \"invalid RR reversals\" until we can figure out a better way to handle them.\n // (Now that IsLoadedCache doesn't technically walk paths, it just invalidates given entity+field pairs, so\n // it is not bothered by an invalid/missing RR reversal.)\n if (!relation) continue;\n // fail(`Attempting to react for ${reactionName} for ${c.toString()}, but there is no \"reverse walkable\" field ${c.constructor.name}.${fieldName}`,);\n const currentValuePromise = maybeApplyTypeFilter(relation.load(), viaType);\n // Always wait for the relation itself\n promises.push(currentValuePromise);\n // If we're going from Book.author back to Author to re-validate the Author.books collection,\n // see if Book.author has changed, so we can re-validate both the old author's books and the\n // new author's books.\n const fieldKind = getMetadata(c).allFields[fieldName]?.kind;\n const isReference = fieldKind === \"m2o\" || fieldKind === \"poly\";\n const isManyToMany = fieldKind === \"m2m\";\n // See jsdoc comment about why this is only necessary for references...\n if (isReference) {\n for (const value of getInstanceData(c).getReferenceHistory(fieldName)) {\n promises.push(maybeApplyTypeFilter(loadReferenceHistoryValue(c, value), viaType));\n }\n }\n if (isManyToMany) {\n const m2m = c[fieldName] as ManyToManyCollection<any, any>;\n const joinRows = getEmInternalApi(m2m.entity.em).joinRows(m2m);\n // Return a tuple of [currentRows, removedRows]\n promises.push(joinRows.removedFor(m2m, c));\n }\n // Walk up the old parents\n if (relation instanceof RecursiveParentsCollectionImpl) {\n const { m2oFieldName } = relation;\n for (const value of getInstanceData(c).getReferenceHistory(m2oFieldName)) {\n promises.push(\n // First load the old parent itself\n loadReferenceHistoryValue(c, value).then((oldParent) => {\n if (!oldParent) return undefined;\n // And then resolve its fieldName=parentsRecursive\n const oldParentRecursiveParents = (oldParent as any)[fieldName];\n return oldParentRecursiveParents.load().then((parents: any) => {\n return [oldParent, ...parents];\n });\n }),\n );\n }\n }\n }\n const nextLevel = await Promise.all(promises);\n // Make a new set so that we dedupe as we go\n current = flatAndUnique(nextLevel);\n }\n return [...current];\n}\n\n/** Converts a normalized reactive `hint` into a load hint. */\nexport function convertToLoadHint<T extends Entity>(\n meta: EntityMetadata,\n hint: ReactiveHint<T>,\n allowCustomKeys = false,\n): LoadHint<T> {\n const loadHint = {};\n // Process the hints individually instead of just calling Object.fromEntries so that\n // we can handle inlined reactive hints that overlap.\n for (const [keyMaybeSuffix, subHint] of Object.entries(normalizeHint(hint))) {\n let key = keyMaybeSuffix.replace(suffixRe, \"\");\n let field = meta.allFields[key];\n\n // If `allowCustomKeys` is enabled, we're probably doing `toJSON`,\n // so look for `companyId` & `companyIds` to turn into `company`\n if (!field && allowCustomKeys) {\n const realField = Object.values(meta.allFields).find((f) => f.fieldIdName === key);\n if (realField) {\n field = realField;\n key = realField.fieldName;\n }\n }\n\n if (field) {\n switch (field.kind) {\n case \"m2m\":\n case \"m2o\":\n case \"o2m\":\n case \"o2o\":\n mergeNormalizedHints(loadHint, { [key]: convertToLoadHint(field.otherMetadata(), subHint, allowCustomKeys) });\n break;\n case \"poly\":\n for (const comp of field.components) {\n // Write `{ parent: { child@Type: hint } }` into the load hint\n mergeNormalizedHints(loadHint, {\n [key]: Object.fromEntries(\n Object.entries(convertToLoadHint(comp.otherMetadata(), subHint, allowCustomKeys)).map(\n // Map the subHint keys to add in `@Type`\n ([subKey, subHint]) => [`${subKey}@${comp.otherMetadata().type}`, subHint],\n ),\n ),\n });\n }\n break;\n case \"primitive\":\n case \"enum\":\n if (field.derived === \"async\") {\n mergeNormalizedHints(loadHint, { [key]: {} });\n }\n continue;\n case \"m2mEnum\":\n // The enum codes are a leaf, but the collection itself must be loaded for the reaction to read it.\n mergeNormalizedHints(loadHint, { [key]: {} });\n break;\n case \"primaryKey\":\n continue;\n default:\n throw new Error(`Invalid reactive hint ${meta.tableName} ${JSON.stringify(hint)}`);\n }\n } else {\n const p = getProperties(meta)[key];\n if (p instanceof RecursiveParentsCollectionImpl) {\n mergeNormalizedHints(loadHint, { [p.fieldName]: convertToLoadHint(meta, subHint, allowCustomKeys) });\n } else if (p instanceof RecursiveChildrenCollectionImpl) {\n mergeNormalizedHints(loadHint, { [p.fieldName]: convertToLoadHint(meta, subHint, allowCustomKeys) });\n } else if (p instanceof RecursiveM2mCollectionImpl) {\n mergeNormalizedHints(loadHint, { [p.fieldName]: convertToLoadHint(meta, subHint, allowCustomKeys) });\n } else if (p && p.reactiveHint) {\n mergeNormalizedHints(loadHint, convertToLoadHint(meta, p.reactiveHint, allowCustomKeys));\n } else if (!allowCustomKeys) {\n fail(`Invalid reactive hint on ${meta.tableName} ${JSON.stringify(hint)}`);\n }\n }\n }\n return loadHint as any;\n}\n\n/** An entity that, when `fields` change, should trigger the reactive rule/field pointed to by `path`. */\nexport interface ReactiveTarget {\n kind: \"read-only\" | \"update\";\n /** The entity that contains a field our reactive rule/field accesses. */\n entity: MaybeAbstractEntityConstructor<any>;\n /** The field(s) that our reactive rule/field accesses, plus any implicit fields like FKs. */\n fields: string[];\n /** The path from this `entity` back to the source reactive rule/field. */\n path: string[];\n}\n\nfunction maybeApplyTypeFilter(loadPromise: Promise<Entity | Entity[] | undefined>, viaType: string | undefined) {\n if (viaType) {\n return loadPromise.then((loaded) => {\n if (Array.isArray(loaded)) {\n return loaded.filter((e) => isTypeOrSubType(e, viaType));\n } else if (loaded && isTypeOrSubType(loaded, viaType)) {\n return loaded;\n } else {\n return undefined;\n }\n });\n }\n return loadPromise;\n}\n\n/** Loads a remembered reference value so reverse-hint walking can revisit it. */\nfunction loadReferenceHistoryValue(entity: Entity, value: any): Promise<Entity | undefined> {\n if (isEntity(value)) return Promise.resolve(value);\n if (isId(value)) return entity.em.load(value) as Promise<Entity>;\n return Promise.resolve(undefined);\n}\n\n/** Returns true if `entity` is exactly `typeName` or a subtype of `typeName`.\n *\n * Used to honor poly/type filters like `@Publisher` during reverse-hint walks:\n * a `SmallPublisher` entity should pass an `@Publisher` filter because it is\n * a Publisher via inheritance.\n */\nexport function isTypeOrSubType(entity: Entity, typeName: string): boolean {\n const meta = getMetadata(entity);\n return meta.type === typeName || meta.baseTypes.some((b) => b.type === typeName);\n}\n\nexport function isPolyHint(key: string): boolean {\n return key.includes(\"@\");\n}\n\nexport function splitPolyHintToKeyAndType(key: string): [string, string] {\n const [k, type] = key.split(\"@\");\n return [k, type];\n}\n\n/** If this hint is a poly like `author@Book`, return the type, otherwise return undefined. */\nexport function getRelationFromMaybePolyKey(entity: Entity, key: string): any {\n if (isPolyHint(key)) {\n const [realKey, typeName] = splitPolyHintToKeyAndType(key);\n // Even though `entity[realKey]` might exist, if it's from a different poly type, we don't want it\n const isApplicable = getBaseAndSelfMetas(getMetadata(entity)).some((meta) => meta.type === typeName);\n if (!isApplicable) return undefined;\n return (entity as any)[realKey];\n } else {\n return (entity as any)[key];\n }\n}\n\n/**\n * For an `author -> books -> bookReviews` hint, appends `BookReview.book`, `Book.author`,\n * i.e. the \"other side\" relation, to the reversed path on the way \"out\" from a mutated BookReview\n * to the target `Author` RF/RR/rule.\n */\nfunction appendPath(reverseField: string): (rt: ReactiveTarget) => ReactiveTarget {\n return (rt) => {\n // We can mutate this array b/c it's only owned by a single/continually-updated `ReactiveTarget`\n rt.path = [...rt.path, reverseField];\n return rt;\n };\n}\n\n/** Recursive membership changes on soft deletion or resurrection, even when the underlying relation is unchanged. */\nfunction recursiveMembershipFields(meta: EntityMetadata, fieldName: string): string[] {\n const deletedAt = getBaseMeta(meta).timestampFields?.deletedAt;\n return deletedAt ? [fieldName, deletedAt] : [fieldName];\n}\n\nfunction isAllReadOnly(hint: any): boolean {\n for (const [key, subHint] of Object.entries(normalizeHint(hint))) {\n const okay = (key.endsWith(\"_ro\") || key.endsWith(\":ro\")) && isAllReadOnly(subHint);\n if (!okay) return false;\n }\n return true;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"reactiveHints.js","names":[],"sources":["../../src/reactivity/reactiveHints.ts"],"sourcesContent":["import { getInstanceData } from \"src/BaseEntity.ts\";\nimport type { Changes } from \"src/changes.ts\";\nimport { type Entity, isEntity } from \"src/Entity.ts\";\nimport { type MaybeAbstractEntityConstructor, getEmInternalApi, isId } from \"src/EntityManager.ts\";\nimport {\n type EntityMetadata,\n type ManyToManyField,\n type ManyToOneField,\n type OneToManyField,\n type OneToOneField,\n type PolymorphicFieldComponent,\n getBaseAndSelfMetas,\n getBaseMeta,\n getMetadata,\n} from \"src/EntityMetadata.ts\";\nimport { getProperties } from \"src/getProperties.ts\";\nimport type { LoadHint, Loadable, Loaded } from \"src/loading/loadHints.ts\";\nimport { type NormalizeHint, type SuffixSeperator, normalizeHint, suffixRe } from \"src/normalizeHints.ts\";\nimport {\n type Collection,\n type EnumCollection,\n type LoadedCollection,\n type LoadedEnumCollection,\n type LoadedProperty,\n type LoadedReadOnlyCollection,\n type LoadedReference,\n type ManyToManyCollection,\n type OneToOneReference,\n type PolymorphicReference,\n type Property,\n PropertyImpl,\n type ReactiveGetter,\n type ReadOnlyCollection,\n type Reference,\n} from \"src/relations/index.ts\";\nimport type { LoadedOneToOneReference } from \"src/relations/OneToOneReference.ts\";\nimport { ReactiveGetterImpl } from \"src/relations/ReactiveGetter.ts\";\nimport {\n RecursiveChildrenCollectionImpl,\n RecursiveM2mCollectionImpl,\n RecursiveParentsCollectionImpl,\n} from \"src/relations/RecursiveCollection.ts\";\nimport type { FieldsOf, RelationsOf } from \"src/typeMap.ts\";\nimport { fail, flatAndUnique, mergeNormalizedHints } from \"src/utils.ts\";\n\n/** The keys in `T` that rules & hooks can react to. */\nexport type Reactable<T extends Entity> =\n // This will be primitives + enums + m2os\n FieldsOf<T> &\n // We include `Loadable` so that we include hasReactiveProperties,\n // which are reversable but won't be in any of our codegen types.\n Loadable<T> &\n Gettable<T> &\n SuffixedFieldsOf<T> &\n SuffixedLoadable<T> &\n SuffixedGettable<T>;\n\n/** Finds `hasReactiveGetters` which are not \"loadable\" b/c they're always loaded. */\nexport type Gettable<T extends Entity> = {\n -readonly [K in keyof T as GettableValue<T[K]> extends never ? never : K]: GettableValue<T[K]>;\n};\n\ntype GettableValue<V> = V extends ReactiveGetter<any, infer P> ? P : never;\n\n/** The fields of `T` suffixed with `:ro` or `_ro`. */\ntype SuffixedFieldsOf<T extends Entity> = {\n [K in keyof FieldsOf<T> & string as `${K}${SuffixSeperator}ro`]: FieldsOf<T>[K];\n};\n\ntype SuffixedLoadable<T extends Entity> = {\n [K in keyof Loadable<T> & string as `${K}${SuffixSeperator}ro`]: Loadable<T>[K];\n};\n\ntype SuffixedGettable<T extends Entity> = {\n [K in keyof Gettable<T> & string as `${K}${SuffixSeperator}ro`]: Gettable<T>[K];\n};\n\n/**\n * A reactive hint of a single key, multiple keys, or nested keys and sub-hints.\n *\n * Reactive hints are different from load hints in that reactive hints include specific\n * fields to react against, while load hints contain only relations, collections, and async\n * properties to preload.\n */\nexport type ReactiveHint<T extends Entity> =\n | (keyof Reactable<T> & string)\n | ReadonlyArray<keyof Reactable<T> & string>\n | NestedReactiveHint<T>;\n\nexport type NestedReactiveHint<T extends Entity> = {\n [K in keyof Reactable<T>]?: Reactable<T>[K] extends infer U extends Entity ? ReactiveHint<U> : {};\n};\n\n/** Given an entity `T` that is being reacted with hint `H`, mark only the `H` attributes visible & populated. */\nexport type Reacted<T extends Entity, H> = Entity & {\n [K in keyof NormalizeHint<H> & keyof T]: T[K] extends OneToOneReference<any, infer U>\n ? LoadedOneToOneReference<T, Entity & Reacted<U, NormalizeHint<H>[K]>>\n : // Add an explicit check for PolymorphicReference because it can add `PolymorphicReference &` which gives access to `idIfSet`\n // ...although maybe LoadedReference should just have `idIfSet` in it as well? LoadedOneToOneReference does...\n T[K] extends PolymorphicReference<any, infer U, infer N>\n ? PolymorphicReference<T, U, N> & LoadedReference<T, Entity & Reacted<U, NormalizeHint<H>[K]>, N>\n : T[K] extends Reference<any, infer U, infer N>\n ? LoadedReference<T, Entity & Reacted<U, NormalizeHint<H>[K]>, N>\n : T[K] extends EnumCollection<any, infer E>\n ? LoadedEnumCollection<T, E>\n : T[K] extends Collection<any, infer U>\n ? LoadedCollection<T, Entity & Reacted<U, NormalizeHint<H>[K]>>\n : T[K] extends ReadOnlyCollection<any, infer U>\n ? LoadedReadOnlyCollection<T, Entity & Reacted<U, NormalizeHint<H>[K]>>\n : T[K] extends Property<any, infer V>\n ? LoadedProperty<any, V>\n : T[K];\n} & {\n /**\n * Gives reactive rules & fields a way to get the full entity if they really need it.\n *\n * This should be used carefully b/c it will side-step the reactive system, and instead\n * reactive rules & fields should explicitly declare access to the fields they need, which\n * will then allow accessing the field via the `Reacted<Author, ...>` type.\n */\n fullNonReactiveAccess: Loaded<T, H>;\n /** Allow detecting if a reactive change is due to nuances like `hasUpdated` or `hasChanged`. */\n changes: Changes<T, keyof (FieldsOf<T> & RelationsOf<T>), keyof NormalizeHint<H>>;\n} & MaybeTransientFields<T>;\n\n/** Allow returning reacted entities from `hasReactiveReference`. */\nexport type MaybeReactedEntity<V> = V extends Entity ? { fullNonReactiveAccess: V } : V;\n\n/**\n * Allow returning reacted entities from `hasReactiveProperty`.\n *\n * The `MaybeReactedEntity` is used by `hasReactiveReference` which already has `undefined | never`\n * broken out as a separate generic; `hasReactiveProperty` does not, and so this type conditionally\n * pulls `undefined` out first, and then defers to `MaybeReactedEntity`.\n */\nexport type MaybeReactedPropertyEntity<V> = V extends (infer V1 extends Entity) | undefined\n ? MaybeReactedEntity<V1> | undefined\n : MaybeReactedEntity<V>;\n\n/**\n * A reactive hint that only allows fields immediately on the entity, i.e. no nested hints.\n *\n * This is used by `ReactiveGetter`s to guarantee that their lambda functions will\n * always be `.get`-able and hence not need loading.\n */\nexport type ShallowReactiveHint<T extends Entity> =\n | (keyof FieldsOf<T> & string)\n | ReadonlyArray<keyof FieldsOf<T> & string>;\n\n/** If the domain model has transient fields, allow reactive behavior to see it, i.e. don't run validation rules for special operations. */\nexport type MaybeTransientFields<T> = \"transientFields\" extends keyof T\n ? { transientFields: T[\"transientFields\"] }\n : {};\n\n/**\n * Takes a reactive hint and returns the `ReactiveTarget`s that, should they change, need to\n * walk back to this watching reactable.\n *\n * @param rootType The original type that contained the reactive rule/field, used for helpful error messages\n * @param entityType The current entity of the step we're walking\n * @param hint The current hint we're walking\n * @param reactForOtherSide the name of our FK column, if the previous collection needs to react to our FK changing\n * @param isFirst\n */\nexport function reverseReactiveHint<T extends Entity>(\n rootType: MaybeAbstractEntityConstructor<T>,\n entityType: MaybeAbstractEntityConstructor<T>,\n hint: ReactiveHint<T>,\n reactForOtherSide?: string,\n reactForOtherSideIsReadOnly?: \"field-read-only\" | \"hint-read-only\" | false,\n isFirst: boolean = true,\n): ReactiveTarget[] {\n const meta = getMetadata(entityType);\n // This is the list of fields for this `entityType` that we will react to their changing values\n const fields: string[] = [];\n const readOnlyFields: string[] = [];\n const maybeRecursive: ReactiveTarget[] = [];\n\n // If the hint before us was a collection, i.e. { books: [\"title\"] }, besides just reacting\n // to our `title` changing, `reactForOtherSide` tells us to react to our `author` field as well.\n if (reactForOtherSide) {\n const _fields = reactForOtherSideIsReadOnly ? readOnlyFields : fields;\n _fields.push(reactForOtherSide);\n if (meta.timestampFields?.deletedAt) {\n _fields.push(meta.timestampFields.deletedAt);\n }\n }\n\n // Look through the hint for our own fields, i.e. `[\"firstName\", \"lastName]`, and nested hints like `{ books: \"title\" }`.\n const subHintTargets: ReactiveTarget[] = Object.entries(normalizeHint(hint)).flatMap(([keyMaybeSuffix, subHint]) => {\n return reverseSubHint(\n rootType,\n entityType,\n hint,\n meta,\n // Pass fields & readOnlyFields, so `reverseSubHint` can append any of our primitive fields it finds\n fields,\n readOnlyFields,\n maybeRecursive,\n keyMaybeSuffix, // maybeSuffix = might end with `_ro` like `name_ro`\n subHint,\n );\n });\n\n const myTargets: ReactiveTarget[] = [];\n // If any of our primitives (or m2o fields) change, establish a reactive path from \"here\" (entityType)\n // that is initially empty (path: []) but will have paths layered on by upstream callers\n if (fields.length > 0 || isFirst || reactForOtherSideIsReadOnly === \"field-read-only\") {\n myTargets.push({ kind: \"update\", entity: entityType, fields, path: [] });\n }\n // Normally read-only fields don't trigger true async reactivity, but IsLoadedCache\n // needs them for same-EM cache invalidation.\n if (readOnlyFields.length > 0) {\n myTargets.push({ kind: \"read-only\", entity: entityType, fields: readOnlyFields, path: [] });\n }\n\n return [...myTargets, ...maybeRecursive, ...subHintTargets];\n}\n\nfunction reverseSubHint(\n rootType: MaybeAbstractEntityConstructor<any>,\n entityType: MaybeAbstractEntityConstructor<any>,\n hint: any,\n meta: EntityMetadata,\n fields: string[],\n readOnlyFields: string[],\n maybeRecursive: ReactiveTarget[],\n keyMaybeSuffix: string,\n subHint: any,\n): ReactiveTarget[] {\n const key = keyMaybeSuffix.replace(suffixRe, \"\");\n const field = meta.allFields[key];\n const isReadOnly = !!keyMaybeSuffix.match(suffixRe) || (field && field.immutable);\n const _fields = isReadOnly ? readOnlyFields : fields;\n if (field) {\n switch (field.kind) {\n case \"m2o\": {\n // If this is a ReactiveReference, maybe we do something different?\n _fields.push(field.fieldName);\n const otherMeta = field.otherMetadata();\n // If this is a ReactiveReference, it won't have an \"other\" side, which is fine if:\n // - we're only reacting to the RR value itself (and have no sub-hints), or\n // - all of our sub-hints are read-only\n const otherField = otherMeta.allFields[field.otherFieldName];\n if (!otherField && !isAllReadOnly(subHint)) {\n throw new Error(\n `Invalid hint in ${rootType.name}.ts, we don't currently support reacting through ReactiveReferences, ${JSON.stringify(hint)}`,\n );\n }\n return reverseReactiveHint(rootType, otherMeta.cstr, subHint, undefined, false, false).map(\n appendPath(maybeAddTypeFilterSuffix(meta, field)),\n );\n }\n case \"poly\": {\n _fields.push(field.fieldName);\n // A poly is basically multiple m2os glued together, so copy/paste the `case m2o` code\n // above but do it for each component FK, and glue them together.\n return field.components.flatMap((comp) => {\n return reverseReactiveHint(rootType, comp.otherMetadata().cstr, subHint, undefined, false, false).map(\n appendPath(maybeAddTypeFilterSuffix(meta, comp)),\n );\n });\n }\n case \"m2m\": {\n // While o2m and o2o can watch for just FK changes by passing `reactForOtherSide` (the FK lives in the other\n // table), for m2m reactivity we push the collection name into the reactive hint, because it's effectively\n // \"the other/reverse side\", and JoinRows will trigger `queueDownstreamReactables` explicitly, instead of\n // `setField` which handles regular/non-m2m mutations.\n _fields.push(field.fieldName);\n const otherFieldName = maybeAddTypeFilterSuffix(meta, field);\n return reverseReactiveHint(\n rootType,\n field.otherMetadata().cstr,\n subHint,\n // For m2m, we can always pass undefined here, as otherwise having the opposite m2m collection\n // recalc all of its children will cause over-reactivity\n undefined,\n false,\n false,\n ).map(appendPath(otherFieldName));\n }\n case \"o2m\":\n case \"o2o\": {\n const isOtherReadOnly = field.otherMetadata().allFields[field.otherFieldName].immutable;\n const otherFieldName = maybeAddTypeFilterSuffix(meta, field);\n // This is not a field, but we want our reverse side to be reactive, so pass reactForOtherSide\n return reverseReactiveHint(\n rootType,\n field.otherMetadata().cstr,\n subHint,\n // IsLoadedCache invalidation always needs to know the otherField name, so we must\n // end up with either a kind=update or kind=read-only target with `otherFieldName` in it.\n field.otherFieldName,\n // We can have three outcomes:\n // - kind=update fields=[otherFieldName] (normal behavior)\n // - kind=update fields=[] + kind=read-only fields=[otherFieldName] (isOtherReadOnly)\n // - kind=read-only fields=[otherFieldName] (hint isReadOnly)\n isReadOnly ? \"hint-read-only\" : isOtherReadOnly ? \"field-read-only\" : false,\n false,\n ).map(appendPath(otherFieldName));\n }\n case \"primaryKey\":\n case \"primitive\":\n case \"enum\":\n _fields.push(key);\n return [];\n case \"m2mEnum\":\n // An enum m2m is a leaf of enum codes (no \"other side\" entity to recurse into); like a m2m,\n // `JoinRows` calls `queueDownstreamReactables` explicitly when its membership changes.\n _fields.push(key);\n return [];\n default:\n throw new Error(`Invalid hint in ${rootType.name}.ts hint ${JSON.stringify(hint)}`);\n }\n } else {\n // We only need to look for ReactiveProperties here, because ReactiveFields & ReactiveReferences\n // have underlying primitive fields that, when/if they change, will be handled in the ^ code.\n //\n // I.e. we specifically don't need to handle RFs & RRs ^, because the EntityManager.flush loop will\n // notice their primitive values changing, and kicking off any downstream reactive fields as necessary.\n const p = getProperties(meta)[key];\n if (p instanceof PropertyImpl) {\n // If the field is marked as readonly (i.e. using the `_ro` suffix), then we can assume that applies to its\n // entire hint as well and can simply omit it. This also allows us to use non-reactive async props as long as\n // they are readonly.\n if (isReadOnly) return [];\n if (!p.reactiveHint) {\n throw new Error(\n `Property ${key} cannot be used in reactive hints in ${rootType.name}.ts hint ${JSON.stringify(\n hint,\n )}, please use hasReactiveProperty instead`,\n );\n }\n return reverseReactiveHint(rootType, meta.cstr, p.reactiveHint, undefined, false, false);\n } else if (p instanceof ReactiveGetterImpl) {\n // If the field is marked as readonly, then we can assume that applies to its entire hint as well and can\n // simply omit it.\n if (isReadOnly) return [];\n return reverseReactiveHint(rootType, meta.cstr, p.reactiveHint, undefined, false, false);\n } else if (p instanceof RecursiveParentsCollectionImpl) {\n // I.e. if this is `Author.mentorsRecursive`:\n // - fieldName=mentorsRecursive (being watched by the caller)\n // - otherFieldName=menteesRecursive (our reversal for walking down)\n // - m2oFieldName = mentor\n //\n // Note that \"reversing parents\" is generally easier, b/c when the tree changes, any given node can\n // still \"walk down\" to find its children w/the \"current value\" childrenRecursive; and any just-now\n // orphaned parents will themselves trigger their own childrenRecursive changes (i.e. we do not need\n // any \"old reference value\" / \"new reference value\" handling).\n //\n // I.e. given a tree of `A is mentor of B is mentor of C`, when B changes mentors, and we need to\n // \"walk to all affected/children mentees, so they can observe their new mentorsRecursive\", merely\n // walking down from (b itself & it's mentees) will find all affected mentees.\n const { otherFieldName, m2oFieldName } = p;\n // When a mentor.firstName/etc field changes, walk to its transitive children (through otherFieldName)\n // via the `return reverseReactiveHint` line below.\n //\n // But if the `author.mentor` field itself changes, we ourselves are also \"a new mentee\" of the mentor, so\n // do `fields.push(m2oFieldName)` to notify our direct reactable (a new mentee), and then our transitive\n // children/mentees. This avoids going \"up to the mentor, and walking down all of his mentees\", which would go\n // through our siblings, which would not have changed/need reactivity.\n _fields.push(m2oFieldName);\n maybeRecursive.push({\n kind: isReadOnly ? \"read-only\" : \"update\",\n entity: entityType,\n fields: recursiveMembershipFields(meta, m2oFieldName),\n path: [otherFieldName],\n });\n // Any reactables who were watching `parentsRecursive`, we'll back down to by going through `childrenRecursive`\n return reverseReactiveHint(rootType, entityType, subHint, undefined, false).map(appendPath(otherFieldName));\n } else if (p instanceof RecursiveChildrenCollectionImpl) {\n const { otherFieldName, o2mFieldName } = p;\n // I.e. this is `Author.menteesRecursive`\n // - fieldName=menteesRecursive` (being watched by the caller)\n // - otherFieldName=mentorsRecursive` (our reversal for walking up)\n // - The o2m's otherFieldName gives us the FK field (e.g., \"mentor\") on the child entity.\n //\n // Note that \"reversing children\" is trickier than reversing parents, b/c when the tree changes,\n // we need to consider both \"old reference value\" and \"new reference value\" scenarios.\n const o2mField = meta.allFields[o2mFieldName] as OneToManyField | OneToOneField;\n const m2oFieldName = o2mField.otherFieldName;\n _fields.push(m2oFieldName);\n maybeRecursive.push({\n kind: isReadOnly ? \"read-only\" : \"update\",\n entity: entityType,\n fields: recursiveMembershipFields(meta, m2oFieldName), // i.e. parent\n path: [otherFieldName], // i.e. parentsRecursive to tell them \"observe your new childrenRecursive\"\n });\n // Any reactables who were watching `childrenRecursive`, we'll up to by going through `parentsRecursive`\n return reverseReactiveHint(rootType, entityType, subHint, undefined, false).map(appendPath(otherFieldName));\n } else if (p instanceof RecursiveM2mCollectionImpl) {\n // I.e. if this is `User.parentsRecursive`:\n // - fieldName=parentsRecursive (being watched by the caller)\n // - otherFieldName=childrenRecursive (our reversal for walking down)\n // - m2mFieldName=parents (the underlying m2m collection)\n //\n // For m2m recursive, the \"change signal\" is when join rows are added/removed, so we push\n // the m2m collection name into fields (like regular m2m handling). To find affected entities\n // that are watching `parentsRecursive`, we walk through `childrenRecursive` (the reverse direction).\n const { otherFieldName, m2mFieldName } = p;\n // When a join row changes (e.g. parents collection is mutated), notify both this entity\n // and its transitive children/parents via otherFieldName\n _fields.push(m2mFieldName);\n maybeRecursive.push({\n kind: isReadOnly ? \"read-only\" : \"update\",\n entity: entityType,\n fields: recursiveMembershipFields(meta, m2mFieldName),\n path: [otherFieldName],\n });\n // Any reactables who were watching this recursive collection, we'll reach via the other direction\n return reverseReactiveHint(rootType, entityType, subHint, undefined, false).map(appendPath(otherFieldName));\n } else {\n throw new Error(`Invalid hint in ${rootType.name}.ts ${JSON.stringify(hint)}`);\n }\n }\n}\n\nfunction maybeAddTypeFilterSuffix(\n meta: EntityMetadata,\n field: ManyToOneField | OneToManyField | ManyToManyField | OneToOneField | PolymorphicFieldComponent,\n): string {\n const otherField = field.otherMetadata().allFields[field.otherFieldName];\n if (!otherField) {\n // This is usually an error, unless if its ReactiveReference which we don't create o2m-s for\n const isReactiveReference = \"kind\" in field && field.kind === \"m2o\" && field.derived === \"async\";\n if (!isReactiveReference) fail(`No field ${field.otherMetadata().type}.${field.otherFieldName}`);\n return field.otherFieldName;\n }\n // If we're Foo, and the other field (which we'll traverse back from at runtime) is actually\n // a poly FK pointing back to multiple `owner=Foo | Bar | Zaz`, add a suffix of `@Foo` so\n // that any runtime traversal that hit `owner` and see a `Bar | Zaz` will stop.\n const nextFieldIsPoly = otherField.kind === \"poly\";\n // If we're a SubType, and the other field points to our base type, assume there might be\n // SubType-specific fields in the path so far, so tell the other side to only walk back through\n // us if its value is actually a SubType.\n const nextFieldPointsToBaseType =\n !!meta.baseType && otherField.kind !== \"poly\" && (otherField as any).otherMetadata().cstr !== meta.cstr;\n return nextFieldIsPoly || nextFieldPointsToBaseType ? `${field.otherFieldName}@${meta.type}` : field.otherFieldName;\n}\n\n/**\n * Walks `reverseHint` for every entity in `entities`.\n *\n * I.e. given `[book1, book2]` and `[\"author\", 'publisher\"]`, will return all the books' authors' publishers.\n *\n * Note that for references (and only references), we walk both through \"the current value\"\n * and prior values remembered in `referenceHistory`. This is fundamentally because references are the true \"owner\"\n * of the relation, while collections are derived.\n *\n * For example, with reversals walking through collections:\n *\n * - Given a hint `book: { author: firstName }`\n * - Which reverses to `author[firstName] -> books`\n * - When we walk through `a1.books`, we use only the current value, which might have only `[b2]` in it.\n * - We don't need to worry about \"the old a1.books\" value, which might have previously had `[b1]` in it,\n * because the `b1.author` changing authors (from `a1` to `a2`) will trigger its own reactivity.\n *\n * Contrast with reversals walking through references:\n *\n * - Given a hint `author: { books: \"title\" }`\n * - Which reverses to `book[title] -> author`\n * - When we traverse through `b1.author`, we use both the current value and remembered values, so that both\n * \"our prior authors\" and \"our new author\" see their latest `author.books` collection values.\n *\n * Delete cleanup runs before reverse hints are walked, so the current relation value may already be unset by the\n * time we get here. For persisted entities, `cleanupOnEntityDeleted` clears the owning FK via `setField`, which\n * remembers the prior FK in `referenceHistory`; this lets us still find the old owner. For collection reversals\n * this is enough because collections are derived from the owning FK: removing `b1` from `a1.books` is represented\n * by `b1.author` changing, not by an independent \"old books\" snapshot.\n *\n * References can also have transient values during a unit of work, i.e. a ReactiveField might observe `b1.author=a2`\n * before the final flush changes it to `a3`. `setField` remembers both original and transient reference values\n * separately from `originalData`, and we walk them in addition to the current value.\n */\nexport async function followReverseHint(\n reactionName: string,\n entities: Entity[],\n reverseHint: string[],\n): Promise<Entity[]> {\n // Start at the current (unique) entities\n let current = new Set(entities);\n const paths = [...reverseHint];\n // And \"walk backwards\" through the reverse hint\n while (paths.length) {\n const path = paths.shift()!;\n const [fieldName, viaType] = path.split(\"@\");\n const promises = new Array(current.size);\n // The path might touch either a reference or a collection\n for (const c of current as Set<any>) {\n const relation = c[fieldName];\n // If `!relation`, I'd really like to `fail`, but atm we're knowingly creating invalid reversals for when\n // a ReactiveField depends on a ReactiveReference; b/c RRs lack an \"other side\", we cannot walk them in\n // reverse, and so for awhile we \"supported\" the RF -> RR pattern by relying on a totally-read-only reactive\n // hint -> dropped during reversal.\n //\n // However, now IsLoadedCache means that even these \"totally read only\" RFs need to be reactive at least\n // during their initial EM creation, to avoid stale values being observed during first-flush mutations,\n // and so we're now stuck with these \"invalid RR reversals\" until we can figure out a better way to handle them.\n // (Now that IsLoadedCache doesn't technically walk paths, it just invalidates given entity+field pairs, so\n // it is not bothered by an invalid/missing RR reversal.)\n if (!relation) continue;\n // fail(`Attempting to react for ${reactionName} for ${c.toString()}, but there is no \"reverse walkable\" field ${c.constructor.name}.${fieldName}`,);\n const currentValuePromise = maybeApplyTypeFilter(relation.load(), viaType);\n // Always wait for the relation itself\n promises.push(currentValuePromise);\n // If we're going from Book.author back to Author to re-validate the Author.books collection,\n // see if Book.author has changed, so we can re-validate both the old author's books and the\n // new author's books.\n const fieldKind = getMetadata(c).allFields[fieldName]?.kind;\n const isReference = fieldKind === \"m2o\" || fieldKind === \"poly\";\n const isManyToMany = fieldKind === \"m2m\";\n // See jsdoc comment about why this is only necessary for references...\n if (isReference) {\n for (const value of getInstanceData(c).getReferenceHistory(fieldName)) {\n promises.push(maybeApplyTypeFilter(loadReferenceHistoryValue(c, value), viaType));\n }\n }\n if (isManyToMany) {\n const m2m = c[fieldName] as ManyToManyCollection<any, any>;\n const joinRows = getEmInternalApi(m2m.entity.em).joinRows(m2m);\n // Return a tuple of [currentRows, removedRows]\n promises.push(joinRows.removedFor(m2m, c));\n }\n // Walk up the old parents\n if (relation instanceof RecursiveParentsCollectionImpl) {\n const { m2oFieldName } = relation;\n for (const value of getInstanceData(c).getReferenceHistory(m2oFieldName)) {\n promises.push(\n // First load the old parent itself\n loadReferenceHistoryValue(c, value).then((oldParent) => {\n if (!oldParent) return undefined;\n // And then resolve its fieldName=parentsRecursive\n const oldParentRecursiveParents = (oldParent as any)[fieldName];\n return oldParentRecursiveParents.load().then((parents: any) => {\n return [oldParent, ...parents];\n });\n }),\n );\n }\n }\n }\n const nextLevel = await Promise.all(promises);\n // Make a new set so that we dedupe as we go\n current = flatAndUnique(nextLevel);\n }\n return [...current];\n}\n\n/** Converts a normalized reactive `hint` into a load hint. */\nexport function convertToLoadHint<T extends Entity>(\n meta: EntityMetadata,\n hint: ReactiveHint<T>,\n allowCustomKeys = false,\n): LoadHint<T> {\n const loadHint = {};\n // Process the hints individually instead of just calling Object.fromEntries so that\n // we can handle inlined reactive hints that overlap.\n for (const [keyMaybeSuffix, subHint] of Object.entries(normalizeHint(hint))) {\n let key = keyMaybeSuffix.replace(suffixRe, \"\");\n let field = meta.allFields[key];\n\n // If `allowCustomKeys` is enabled, we're probably doing `toJSON`,\n // so look for `companyId` & `companyIds` to turn into `company`\n if (!field && allowCustomKeys) {\n const realField = Object.values(meta.allFields).find((f) => f.fieldIdName === key);\n if (realField) {\n field = realField;\n key = realField.fieldName;\n }\n }\n\n if (field) {\n switch (field.kind) {\n case \"m2m\":\n case \"m2o\":\n case \"o2m\":\n case \"o2o\":\n mergeNormalizedHints(loadHint, { [key]: convertToLoadHint(field.otherMetadata(), subHint, allowCustomKeys) });\n break;\n case \"poly\":\n for (const comp of field.components) {\n // Write `{ parent: { child@Type: hint } }` into the load hint\n mergeNormalizedHints(loadHint, {\n [key]: Object.fromEntries(\n Object.entries(convertToLoadHint(comp.otherMetadata(), subHint, allowCustomKeys)).map(\n // Map the subHint keys to add in `@Type`\n ([subKey, subHint]) => [`${subKey}@${comp.otherMetadata().type}`, subHint],\n ),\n ),\n });\n }\n break;\n case \"primitive\":\n case \"enum\":\n // Lazy primitives are excluded from the default SELECT, so rules need them loaded before using `.get`.\n if (field.derived === \"async\" || (field.kind === \"primitive\" && field.lazy)) {\n mergeNormalizedHints(loadHint, { [key]: {} });\n }\n continue;\n case \"m2mEnum\":\n // The enum codes are a leaf, but the collection itself must be loaded for the reaction to read it.\n mergeNormalizedHints(loadHint, { [key]: {} });\n break;\n case \"primaryKey\":\n continue;\n default:\n throw new Error(`Invalid reactive hint ${meta.tableName} ${JSON.stringify(hint)}`);\n }\n } else {\n const p = getProperties(meta)[key];\n if (p instanceof RecursiveParentsCollectionImpl) {\n mergeNormalizedHints(loadHint, { [p.fieldName]: convertToLoadHint(meta, subHint, allowCustomKeys) });\n } else if (p instanceof RecursiveChildrenCollectionImpl) {\n mergeNormalizedHints(loadHint, { [p.fieldName]: convertToLoadHint(meta, subHint, allowCustomKeys) });\n } else if (p instanceof RecursiveM2mCollectionImpl) {\n mergeNormalizedHints(loadHint, { [p.fieldName]: convertToLoadHint(meta, subHint, allowCustomKeys) });\n } else if (p && p.reactiveHint) {\n mergeNormalizedHints(loadHint, convertToLoadHint(meta, p.reactiveHint, allowCustomKeys));\n } else if (!allowCustomKeys) {\n fail(`Invalid reactive hint on ${meta.tableName} ${JSON.stringify(hint)}`);\n }\n }\n }\n return loadHint as any;\n}\n\n/** An entity that, when `fields` change, should trigger the reactive rule/field pointed to by `path`. */\nexport interface ReactiveTarget {\n kind: \"read-only\" | \"update\";\n /** The entity that contains a field our reactive rule/field accesses. */\n entity: MaybeAbstractEntityConstructor<any>;\n /** The field(s) that our reactive rule/field accesses, plus any implicit fields like FKs. */\n fields: string[];\n /** The path from this `entity` back to the source reactive rule/field. */\n path: string[];\n}\n\nfunction maybeApplyTypeFilter(loadPromise: Promise<Entity | Entity[] | undefined>, viaType: string | undefined) {\n if (viaType) {\n return loadPromise.then((loaded) => {\n if (Array.isArray(loaded)) {\n return loaded.filter((e) => isTypeOrSubType(e, viaType));\n } else if (loaded && isTypeOrSubType(loaded, viaType)) {\n return loaded;\n } else {\n return undefined;\n }\n });\n }\n return loadPromise;\n}\n\n/** Loads a remembered reference value so reverse-hint walking can revisit it. */\nfunction loadReferenceHistoryValue(entity: Entity, value: any): Promise<Entity | undefined> {\n if (isEntity(value)) return Promise.resolve(value);\n if (isId(value)) return entity.em.load(value) as Promise<Entity>;\n return Promise.resolve(undefined);\n}\n\n/** Returns true if `entity` is exactly `typeName` or a subtype of `typeName`.\n *\n * Used to honor poly/type filters like `@Publisher` during reverse-hint walks:\n * a `SmallPublisher` entity should pass an `@Publisher` filter because it is\n * a Publisher via inheritance.\n */\nexport function isTypeOrSubType(entity: Entity, typeName: string): boolean {\n const meta = getMetadata(entity);\n return meta.type === typeName || meta.baseTypes.some((b) => b.type === typeName);\n}\n\nexport function isPolyHint(key: string): boolean {\n return key.includes(\"@\");\n}\n\nexport function splitPolyHintToKeyAndType(key: string): [string, string] {\n const [k, type] = key.split(\"@\");\n return [k, type];\n}\n\n/** If this hint is a poly like `author@Book`, return the type, otherwise return undefined. */\nexport function getRelationFromMaybePolyKey(entity: Entity, key: string): any {\n if (isPolyHint(key)) {\n const [realKey, typeName] = splitPolyHintToKeyAndType(key);\n // Even though `entity[realKey]` might exist, if it's from a different poly type, we don't want it\n const isApplicable = getBaseAndSelfMetas(getMetadata(entity)).some((meta) => meta.type === typeName);\n if (!isApplicable) return undefined;\n return (entity as any)[realKey];\n } else {\n return (entity as any)[key];\n }\n}\n\n/**\n * For an `author -> books -> bookReviews` hint, appends `BookReview.book`, `Book.author`,\n * i.e. the \"other side\" relation, to the reversed path on the way \"out\" from a mutated BookReview\n * to the target `Author` RF/RR/rule.\n */\nfunction appendPath(reverseField: string): (rt: ReactiveTarget) => ReactiveTarget {\n return (rt) => {\n // We can mutate this array b/c it's only owned by a single/continually-updated `ReactiveTarget`\n rt.path = [...rt.path, reverseField];\n return rt;\n };\n}\n\n/** Recursive membership changes on soft deletion or resurrection, even when the underlying relation is unchanged. */\nfunction recursiveMembershipFields(meta: EntityMetadata, fieldName: string): string[] {\n const deletedAt = getBaseMeta(meta).timestampFields?.deletedAt;\n return deletedAt ? [fieldName, deletedAt] : [fieldName];\n}\n\nfunction isAllReadOnly(hint: any): boolean {\n for (const [key, subHint] of Object.entries(normalizeHint(hint))) {\n const okay = (key.endsWith(\"_ro\") || key.endsWith(\":ro\")) && isAllReadOnly(subHint);\n if (!okay) return false;\n }\n return true;\n}\n"],"mappings":"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"}
@@ -1,12 +1,12 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_relations_AbstractPropertyImpl = require("./AbstractPropertyImpl.cjs");
3
- const require_normalizeHints = require("../normalizeHints.cjs");
4
- const require_relations_PropertySymbols = require("./PropertySymbols.cjs");
5
- const require_reactivity_reactiveHints = require("../reactivity/reactiveHints.cjs");
6
3
  const require_loading_loadHints = require("../loading/loadHints.cjs");
4
+ const require_relations_PropertySymbols = require("./PropertySymbols.cjs");
7
5
  const require_fields = require("../fields.cjs");
8
6
  const require_utils = require("../utils.cjs");
9
7
  const require_newEntity = require("../newEntity.cjs");
8
+ const require_normalizeHints = require("../normalizeHints.cjs");
9
+ const require_reactivity_reactiveHints = require("../reactivity/reactiveHints.cjs");
10
10
  const require_EntityMetadata = require("../EntityMetadata.cjs");
11
11
  require("../index.cjs");
12
12
  require("../Entity.cjs");
@@ -1,11 +1,11 @@
1
1
  import { AbstractPropertyImpl } from "./AbstractPropertyImpl.js";
2
- import { deepNormalizeHint } from "../normalizeHints.js";
3
- import { PropertyT } from "./PropertySymbols.js";
4
- import { convertToLoadHint } from "../reactivity/reactiveHints.js";
5
2
  import { isLoaded } from "../loading/loadHints.js";
3
+ import { PropertyT } from "./PropertySymbols.js";
6
4
  import { getField, isFieldSet, setField } from "../fields.js";
7
5
  import { mergeNormalizedHints } from "../utils.js";
8
6
  import { lazyField } from "../newEntity.js";
7
+ import { deepNormalizeHint } from "../normalizeHints.js";
8
+ import { convertToLoadHint } from "../reactivity/reactiveHints.js";
9
9
  import { getMetadata } from "../EntityMetadata.js";
10
10
  import "../index.js";
11
11
  import "../Entity.js";
@@ -72,6 +72,20 @@ var LazyFieldImpl = class extends require_relations_AbstractPropertyImpl.Abstrac
72
72
  if (require_BaseEntity.getInstanceData(this.entity).isOrWasNew) return true;
73
73
  return this.#loaded;
74
74
  }
75
+ /**
76
+ * Marks this field loaded, using its value in entity data or reading it from the entity's row.
77
+ *
78
+ * The caller must know the value is present in entity data or the row; default SELECTs omit lazy columns.
79
+ */
80
+ preload() {
81
+ const { data, rowData, rowIndex } = require_BaseEntity.getInstanceData(this.entity);
82
+ if (!(this.#fieldName in data)) {
83
+ const field = require_EntityMetadata.getMetadata(this.entity).allFields[this.#fieldName];
84
+ data[this.#fieldName] = field.serde.fromRow(rowData, rowIndex);
85
+ }
86
+ this.#loaded = true;
87
+ this.#loadPromise = void 0;
88
+ }
75
89
  /** Called after em.flush to invalidate the cached value, since the db state may have changed. */
76
90
  resetAfterFlush() {
77
91
  this.#loaded = false;
@@ -1 +1 @@
1
- {"version":3,"file":"LazyField.cjs","names":["lazyField","AbstractPropertyImpl","#fieldName","#loaded","#loadPromise","#value","getMetadata","getInstanceData","PropertyT","abbreviation","keyToNumber","kqDot","PojoRowData","tagId"],"sources":["../../src/relations/LazyField.ts"],"sourcesContent":["import { getInstanceData } from \"src/BaseEntity.ts\";\nimport { type BatchLoader } from \"src/batchloaders/BatchLoader.ts\";\nimport { type Entity } from \"src/Entity.ts\";\nimport { type EntityManager } from \"src/EntityManager.ts\";\nimport { type EntityMetadata, type PrimitiveField, getMetadata } from \"src/EntityMetadata.ts\";\nimport { setField } from \"src/fields.ts\";\nimport { keyToNumber, tagId } from \"src/keys.ts\";\nimport { lazyField } from \"src/newEntity.ts\";\nimport type { ParsedFindQuery } from \"src/queries/find/QueryParser.ts\";\nimport { kqDot } from \"src/queries/sql/keywords.ts\";\nimport { AbstractPropertyImpl } from \"src/relations/AbstractPropertyImpl.ts\";\nimport { type LoadedProperty, type Property } from \"src/relations/hasProperty.ts\";\nimport { PropertyT } from \"src/relations/PropertySymbols.ts\";\nimport { PojoRowData } from \"src/RowData.ts\";\nimport { abbreviation } from \"src/utils.ts\";\n\nexport const lazyColumnLoadOperation = \"lazy-column-load\";\n\n/**\n * The \"original value\" of an unloaded lazy column: unknown, and never equal to any set value.\n *\n * Seeded into `__orm.data` by `LazyFieldImpl.set` so that setting an unloaded column (even to\n * `undefined`) always registers as a change, i.e. instead of comparing equal to the `undefined`\n * that the excluded column would otherwise read as.\n */\nexport const notLoadedValue = Symbol(\"notLoadedValue\");\n\n/**\n * A single-column primitive (i.e. a large `jsonb`/`text`) that is deliberately excluded from the\n * entity's default SELECT and only fetched from the database on demand.\n *\n * I.e. `await author.someBigColumn.load()`, or synchronously via `.get` after `em.populate`.\n */\nexport interface LazyField<T extends Entity, V> extends Property<T, V> {\n load(opts?: { forceReload?: boolean }): Promise<V>;\n /** Sets the value in memory (dirtied for the next flush), i.e. without loading the current db value first. */\n set(value: V): void;\n}\n\n/** Creates a `LazyField` for a column that codegen has marked `lazy: true`. */\nexport function hasLazyField<T extends Entity, V>(): LazyField<T, V> {\n return lazyField((entity: T, fieldName: string) => new LazyFieldImpl<T, V>(entity, fieldName));\n}\n\nexport class LazyFieldImpl<T extends Entity, V> extends AbstractPropertyImpl<T> implements LazyField<T, V> {\n #fieldName: string;\n #loaded = false;\n #loadPromise: Promise<V> | undefined;\n\n constructor(entity: T, fieldName: string) {\n super(entity);\n this.#fieldName = fieldName;\n }\n\n /** Fetches this column from the database (batched across peers) and caches it on the entity. */\n load(opts?: { forceReload?: boolean }): Promise<V> {\n const { entity } = this;\n if (opts?.forceReload) {\n this.#loaded = false;\n this.#loadPromise = undefined;\n }\n if (this.isLoaded) return Promise.resolve(this.#value);\n const meta = getMetadata(entity);\n return (this.#loadPromise ??= lazyColumnBatchLoader(entity.em, meta, this.#fieldName)\n .load(entity.idTagged)\n .then(() => {\n this.#loaded = true;\n return this.#value;\n }));\n }\n\n get get(): V {\n if (!this.isLoaded) {\n throw new Error(`${this.#fieldName} has not been loaded yet, use '.load()' or an 'em.populate' hint`);\n }\n return this.#value;\n }\n\n /** Sets the value in memory; the column is dirtied and written on the next `em.flush`. */\n set(value: V): void {\n if (this.isLoaded) {\n setField(this.entity, this.#fieldName, value);\n } else {\n // Seed `notLoadedValue` as the current value so the set always registers as a change against\n // the unknown db value: an unloaded column otherwise reads as `undefined`, which would make\n // `set(undefined)` a silent no-op, and a later `set(undefined)` falsely revert to clean.\n const { data } = getInstanceData(this.entity);\n data[this.#fieldName] = notLoadedValue;\n try {\n setField(this.entity, this.#fieldName, value);\n } catch (e) {\n // Don't leak the marker as a readable value, i.e. if setting on a deleted entity throws\n delete data[this.#fieldName];\n throw e;\n }\n }\n this.#loaded = true;\n }\n\n get isLoaded(): boolean {\n // New entities keep their value in-memory, so there's nothing to lazy-load.\n if (getInstanceData(this.entity).isOrWasNew) return true;\n return this.#loaded;\n }\n\n /** Called after em.flush to invalidate the cached value, since the db state may have changed. */\n resetAfterFlush(): void {\n this.#loaded = false;\n this.#loadPromise = undefined;\n }\n\n /** The domain value, which `load`/`set` keep in the entity's `__orm.data` like any other column. */\n get #value(): V {\n return getInstanceData(this.entity).data[this.#fieldName] as V;\n }\n\n [PropertyT] = undefined as any as T;\n}\n\n/** Type guard utility for determining if an entity field is a LazyField. */\nexport function isLazyField(maybe: any): maybe is LazyField<any, any> {\n return maybe instanceof LazyFieldImpl;\n}\n\n/** Type guard utility for determining if an entity field is a loaded LazyField. */\nexport function isLoadedLazyField(maybe: any): maybe is LazyField<any, any> & LoadedProperty<any, any> {\n return isLazyField(maybe) && maybe.isLoaded;\n}\n\n/** Batches `LazyField.load`s for the same column into a single `SELECT id, <column> ... WHERE id IN (...)`. */\nfunction lazyColumnBatchLoader(em: EntityManager, meta: EntityMetadata, fieldName: string): BatchLoader<string> {\n return em.getBatchLoader(lazyColumnLoadOperation, `${meta.tableName}-${fieldName}`, async (taggedIds) => {\n const field = meta.allFields[fieldName] as PrimitiveField;\n const column = field.serde.columns[0];\n const alias = abbreviation(meta.tableName);\n const keys = taggedIds.map((id) => keyToNumber(meta, id));\n const query: ParsedFindQuery = {\n selects: [`${kqDot(alias, \"id\")} as id`, kqDot(alias, column.columnName)],\n tables: [{ alias, join: \"primary\", table: meta.tableName }],\n condition: {\n kind: \"exp\",\n op: \"and\",\n conditions: [{ kind: \"column\", alias, column: \"id\", dbType: meta.idDbType, cond: { kind: \"in\", value: keys } }],\n },\n orderBys: [],\n };\n const rows = await em[\"executeFind\"](meta, lazyColumnLoadOperation, query, {});\n const rowData = new PojoRowData(rows);\n for (let i = 0; i < rows.length; i++) {\n const entity = em.findExistingInstance(tagId(meta, rows[i].id));\n if (entity) getInstanceData(entity).data[fieldName] = field.serde.fromRow(rowData, i);\n }\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"LazyField.cjs","names":["lazyField","AbstractPropertyImpl","#fieldName","#loaded","#loadPromise","#value","getMetadata","getInstanceData","PropertyT","abbreviation","keyToNumber","kqDot","PojoRowData","tagId"],"sources":["../../src/relations/LazyField.ts"],"sourcesContent":["import { getInstanceData } from \"src/BaseEntity.ts\";\nimport { type BatchLoader } from \"src/batchloaders/BatchLoader.ts\";\nimport { type Entity } from \"src/Entity.ts\";\nimport { type EntityManager } from \"src/EntityManager.ts\";\nimport { type EntityMetadata, type PrimitiveField, getMetadata } from \"src/EntityMetadata.ts\";\nimport { setField } from \"src/fields.ts\";\nimport { keyToNumber, tagId } from \"src/keys.ts\";\nimport { lazyField } from \"src/newEntity.ts\";\nimport type { ParsedFindQuery } from \"src/queries/find/QueryParser.ts\";\nimport { kqDot } from \"src/queries/sql/keywords.ts\";\nimport { AbstractPropertyImpl } from \"src/relations/AbstractPropertyImpl.ts\";\nimport { type LoadedProperty, type Property } from \"src/relations/hasProperty.ts\";\nimport { PropertyT } from \"src/relations/PropertySymbols.ts\";\nimport { PojoRowData } from \"src/RowData.ts\";\nimport { abbreviation } from \"src/utils.ts\";\n\nexport const lazyColumnLoadOperation = \"lazy-column-load\";\n\n/**\n * The \"original value\" of an unloaded lazy column: unknown, and never equal to any set value.\n *\n * Seeded into `__orm.data` by `LazyFieldImpl.set` so that setting an unloaded column (even to\n * `undefined`) always registers as a change, i.e. instead of comparing equal to the `undefined`\n * that the excluded column would otherwise read as.\n */\nexport const notLoadedValue = Symbol(\"notLoadedValue\");\n\n/**\n * A single-column primitive (i.e. a large `jsonb`/`text`) that is deliberately excluded from the\n * entity's default SELECT and only fetched from the database on demand.\n *\n * I.e. `await author.someBigColumn.load()`, or synchronously via `.get` after `em.populate`.\n */\nexport interface LazyField<T extends Entity, V> extends Property<T, V> {\n load(opts?: { forceReload?: boolean }): Promise<V>;\n /** Sets the value in memory (dirtied for the next flush), i.e. without loading the current db value first. */\n set(value: V): void;\n}\n\n/** Creates a `LazyField` for a column that codegen has marked `lazy: true`. */\nexport function hasLazyField<T extends Entity, V>(): LazyField<T, V> {\n return lazyField((entity: T, fieldName: string) => new LazyFieldImpl<T, V>(entity, fieldName));\n}\n\nexport class LazyFieldImpl<T extends Entity, V> extends AbstractPropertyImpl<T> implements LazyField<T, V> {\n #fieldName: string;\n #loaded = false;\n #loadPromise: Promise<V> | undefined;\n\n constructor(entity: T, fieldName: string) {\n super(entity);\n this.#fieldName = fieldName;\n }\n\n /** Fetches this column from the database (batched across peers) and caches it on the entity. */\n load(opts?: { forceReload?: boolean }): Promise<V> {\n const { entity } = this;\n if (opts?.forceReload) {\n this.#loaded = false;\n this.#loadPromise = undefined;\n }\n if (this.isLoaded) return Promise.resolve(this.#value);\n const meta = getMetadata(entity);\n return (this.#loadPromise ??= lazyColumnBatchLoader(entity.em, meta, this.#fieldName)\n .load(entity.idTagged)\n .then(() => {\n this.#loaded = true;\n return this.#value;\n }));\n }\n\n get get(): V {\n if (!this.isLoaded) {\n throw new Error(`${this.#fieldName} has not been loaded yet, use '.load()' or an 'em.populate' hint`);\n }\n return this.#value;\n }\n\n /** Sets the value in memory; the column is dirtied and written on the next `em.flush`. */\n set(value: V): void {\n if (this.isLoaded) {\n setField(this.entity, this.#fieldName, value);\n } else {\n // Seed `notLoadedValue` as the current value so the set always registers as a change against\n // the unknown db value: an unloaded column otherwise reads as `undefined`, which would make\n // `set(undefined)` a silent no-op, and a later `set(undefined)` falsely revert to clean.\n const { data } = getInstanceData(this.entity);\n data[this.#fieldName] = notLoadedValue;\n try {\n setField(this.entity, this.#fieldName, value);\n } catch (e) {\n // Don't leak the marker as a readable value, i.e. if setting on a deleted entity throws\n delete data[this.#fieldName];\n throw e;\n }\n }\n this.#loaded = true;\n }\n\n get isLoaded(): boolean {\n // New entities keep their value in-memory, so there's nothing to lazy-load.\n if (getInstanceData(this.entity).isOrWasNew) return true;\n return this.#loaded;\n }\n\n /**\n * Marks this field loaded, using its value in entity data or reading it from the entity's row.\n *\n * The caller must know the value is present in entity data or the row; default SELECTs omit lazy columns.\n */\n preload(): void {\n const { data, rowData, rowIndex } = getInstanceData(this.entity);\n if (!(this.#fieldName in data)) {\n const field = getMetadata(this.entity).allFields[this.#fieldName];\n data[this.#fieldName] = field.serde!.fromRow(rowData, rowIndex);\n }\n\n this.#loaded = true;\n this.#loadPromise = undefined;\n }\n\n /** Called after em.flush to invalidate the cached value, since the db state may have changed. */\n resetAfterFlush(): void {\n this.#loaded = false;\n this.#loadPromise = undefined;\n }\n\n /** The domain value, which `load`/`set` keep in the entity's `__orm.data` like any other column. */\n get #value(): V {\n return getInstanceData(this.entity).data[this.#fieldName] as V;\n }\n\n [PropertyT] = undefined as any as T;\n}\n\n/** Type guard utility for determining if an entity field is a LazyField. */\nexport function isLazyField(maybe: any): maybe is LazyField<any, any> {\n return maybe instanceof LazyFieldImpl;\n}\n\n/** Type guard utility for determining if an entity field is a loaded LazyField. */\nexport function isLoadedLazyField(maybe: any): maybe is LazyField<any, any> & LoadedProperty<any, any> {\n return isLazyField(maybe) && maybe.isLoaded;\n}\n\n/** Batches `LazyField.load`s for the same column into a single `SELECT id, <column> ... WHERE id IN (...)`. */\nfunction lazyColumnBatchLoader(em: EntityManager, meta: EntityMetadata, fieldName: string): BatchLoader<string> {\n return em.getBatchLoader(lazyColumnLoadOperation, `${meta.tableName}-${fieldName}`, async (taggedIds) => {\n const field = meta.allFields[fieldName] as PrimitiveField;\n const column = field.serde.columns[0];\n const alias = abbreviation(meta.tableName);\n const keys = taggedIds.map((id) => keyToNumber(meta, id));\n const query: ParsedFindQuery = {\n selects: [`${kqDot(alias, \"id\")} as id`, kqDot(alias, column.columnName)],\n tables: [{ alias, join: \"primary\", table: meta.tableName }],\n condition: {\n kind: \"exp\",\n op: \"and\",\n conditions: [{ kind: \"column\", alias, column: \"id\", dbType: meta.idDbType, cond: { kind: \"in\", value: keys } }],\n },\n orderBys: [],\n };\n const rows = await em[\"executeFind\"](meta, lazyColumnLoadOperation, query, {});\n const rowData = new PojoRowData(rows);\n for (let i = 0; i < rows.length; i++) {\n const entity = em.findExistingInstance(tagId(meta, rows[i].id));\n if (entity) getInstanceData(entity).data[fieldName] = field.serde.fromRow(rowData, i);\n }\n });\n}\n"],"mappings":"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"}
@@ -38,6 +38,12 @@ declare class LazyFieldImpl<T extends Entity, V> extends AbstractPropertyImpl<T>
38
38
  /** Sets the value in memory; the column is dirtied and written on the next `em.flush`. */
39
39
  set(value: V): void;
40
40
  get isLoaded(): boolean;
41
+ /**
42
+ * Marks this field loaded, using its value in entity data or reading it from the entity's row.
43
+ *
44
+ * The caller must know the value is present in entity data or the row; default SELECTs omit lazy columns.
45
+ */
46
+ preload(): void;
41
47
  /** Called after em.flush to invalidate the cached value, since the db state may have changed. */
42
48
  resetAfterFlush(): void;
43
49
  [PropertyT]: T;
@@ -1 +1 @@
1
- {"version":3,"file":"LazyField.d.cts","names":[],"sources":["../../src/relations/LazyField.ts"],"mappings":";;;;;cAgBa;;;;;;;;cASA;;;;;;;UAQI,UAAU,UAAU,QAAQ,WAAW,SAAS,GAAG;EAClE,KAAK;IAAS;MAA0B,QAAQ;;EAEhD,IAAI,OAAO;;;iBAIG,aAAa,UAAU,QAAQ,MAAM,UAAU,GAAG;cAIrD,cAAc,UAAU,QAAQ,WAAW,qBAAqB,cAAc,UAAU,GAAG;;EAKtG,YAAY,QAAQ,GAAG;;EAMvB,KAAK;IAAS;MAA0B,QAAQ;MAgB5C,OAAO;;EAQX,IAAI,OAAO;MAoBP;;EAOJ;GAUC,YAAiC;;;iBAIpB,YAAY,aAAa,SAAS;;iBAKlC,kBAAkB,aAAa,SAAS,sBAAsB"}
1
+ {"version":3,"file":"LazyField.d.cts","names":[],"sources":["../../src/relations/LazyField.ts"],"mappings":";;;;;cAgBa;;;;;;;;cASA;;;;;;;UAQI,UAAU,UAAU,QAAQ,WAAW,SAAS,GAAG;EAClE,KAAK;IAAS;MAA0B,QAAQ;;EAEhD,IAAI,OAAO;;;iBAIG,aAAa,UAAU,QAAQ,MAAM,UAAU,GAAG;cAIrD,cAAc,UAAU,QAAQ,WAAW,qBAAqB,cAAc,UAAU,GAAG;;EAKtG,YAAY,QAAQ,GAAG;;EAMvB,KAAK;IAAS;MAA0B,QAAQ;MAgB5C,OAAO;;EAQX,IAAI,OAAO;MAoBP;;;;;;EAWJ;;EAYA;GAUC,YAAiC;;;iBAIpB,YAAY,aAAa,SAAS;;iBAKlC,kBAAkB,aAAa,SAAS,sBAAsB"}
@@ -38,6 +38,12 @@ declare class LazyFieldImpl<T extends Entity, V> extends AbstractPropertyImpl<T>
38
38
  /** Sets the value in memory; the column is dirtied and written on the next `em.flush`. */
39
39
  set(value: V): void;
40
40
  get isLoaded(): boolean;
41
+ /**
42
+ * Marks this field loaded, using its value in entity data or reading it from the entity's row.
43
+ *
44
+ * The caller must know the value is present in entity data or the row; default SELECTs omit lazy columns.
45
+ */
46
+ preload(): void;
41
47
  /** Called after em.flush to invalidate the cached value, since the db state may have changed. */
42
48
  resetAfterFlush(): void;
43
49
  [PropertyT]: T;
@@ -1 +1 @@
1
- {"version":3,"file":"LazyField.d.mts","names":[],"sources":["../../src/relations/LazyField.ts"],"mappings":";;;;;cAgBa;;;;;;;;cASA;;;;;;;UAQI,UAAU,UAAU,QAAQ,WAAW,SAAS,GAAG;EAClE,KAAK;IAAS;MAA0B,QAAQ;;EAEhD,IAAI,OAAO;;;iBAIG,aAAa,UAAU,QAAQ,MAAM,UAAU,GAAG;cAIrD,cAAc,UAAU,QAAQ,WAAW,qBAAqB,cAAc,UAAU,GAAG;;EAKtG,YAAY,QAAQ,GAAG;;EAMvB,KAAK;IAAS;MAA0B,QAAQ;MAgB5C,OAAO;;EAQX,IAAI,OAAO;MAoBP;;EAOJ;GAUC,YAAiC;;;iBAIpB,YAAY,aAAa,SAAS;;iBAKlC,kBAAkB,aAAa,SAAS,sBAAsB"}
1
+ {"version":3,"file":"LazyField.d.mts","names":[],"sources":["../../src/relations/LazyField.ts"],"mappings":";;;;;cAgBa;;;;;;;;cASA;;;;;;;UAQI,UAAU,UAAU,QAAQ,WAAW,SAAS,GAAG;EAClE,KAAK;IAAS;MAA0B,QAAQ;;EAEhD,IAAI,OAAO;;;iBAIG,aAAa,UAAU,QAAQ,MAAM,UAAU,GAAG;cAIrD,cAAc,UAAU,QAAQ,WAAW,qBAAqB,cAAc,UAAU,GAAG;;EAKtG,YAAY,QAAQ,GAAG;;EAMvB,KAAK;IAAS;MAA0B,QAAQ;MAgB5C,OAAO;;EAQX,IAAI,OAAO;MAoBP;;;;;;EAWJ;;EAYA;GAUC,YAAiC;;;iBAIpB,YAAY,aAAa,SAAS;;iBAKlC,kBAAkB,aAAa,SAAS,sBAAsB"}
@@ -71,6 +71,20 @@ var LazyFieldImpl = class extends AbstractPropertyImpl {
71
71
  if (getInstanceData(this.entity).isOrWasNew) return true;
72
72
  return this.#loaded;
73
73
  }
74
+ /**
75
+ * Marks this field loaded, using its value in entity data or reading it from the entity's row.
76
+ *
77
+ * The caller must know the value is present in entity data or the row; default SELECTs omit lazy columns.
78
+ */
79
+ preload() {
80
+ const { data, rowData, rowIndex } = getInstanceData(this.entity);
81
+ if (!(this.#fieldName in data)) {
82
+ const field = getMetadata(this.entity).allFields[this.#fieldName];
83
+ data[this.#fieldName] = field.serde.fromRow(rowData, rowIndex);
84
+ }
85
+ this.#loaded = true;
86
+ this.#loadPromise = void 0;
87
+ }
74
88
  /** Called after em.flush to invalidate the cached value, since the db state may have changed. */
75
89
  resetAfterFlush() {
76
90
  this.#loaded = false;
@@ -1 +1 @@
1
- {"version":3,"file":"LazyField.js","names":["#fieldName","#loaded","#loadPromise","#value"],"sources":["../../src/relations/LazyField.ts"],"sourcesContent":["import { getInstanceData } from \"src/BaseEntity.ts\";\nimport { type BatchLoader } from \"src/batchloaders/BatchLoader.ts\";\nimport { type Entity } from \"src/Entity.ts\";\nimport { type EntityManager } from \"src/EntityManager.ts\";\nimport { type EntityMetadata, type PrimitiveField, getMetadata } from \"src/EntityMetadata.ts\";\nimport { setField } from \"src/fields.ts\";\nimport { keyToNumber, tagId } from \"src/keys.ts\";\nimport { lazyField } from \"src/newEntity.ts\";\nimport type { ParsedFindQuery } from \"src/queries/find/QueryParser.ts\";\nimport { kqDot } from \"src/queries/sql/keywords.ts\";\nimport { AbstractPropertyImpl } from \"src/relations/AbstractPropertyImpl.ts\";\nimport { type LoadedProperty, type Property } from \"src/relations/hasProperty.ts\";\nimport { PropertyT } from \"src/relations/PropertySymbols.ts\";\nimport { PojoRowData } from \"src/RowData.ts\";\nimport { abbreviation } from \"src/utils.ts\";\n\nexport const lazyColumnLoadOperation = \"lazy-column-load\";\n\n/**\n * The \"original value\" of an unloaded lazy column: unknown, and never equal to any set value.\n *\n * Seeded into `__orm.data` by `LazyFieldImpl.set` so that setting an unloaded column (even to\n * `undefined`) always registers as a change, i.e. instead of comparing equal to the `undefined`\n * that the excluded column would otherwise read as.\n */\nexport const notLoadedValue = Symbol(\"notLoadedValue\");\n\n/**\n * A single-column primitive (i.e. a large `jsonb`/`text`) that is deliberately excluded from the\n * entity's default SELECT and only fetched from the database on demand.\n *\n * I.e. `await author.someBigColumn.load()`, or synchronously via `.get` after `em.populate`.\n */\nexport interface LazyField<T extends Entity, V> extends Property<T, V> {\n load(opts?: { forceReload?: boolean }): Promise<V>;\n /** Sets the value in memory (dirtied for the next flush), i.e. without loading the current db value first. */\n set(value: V): void;\n}\n\n/** Creates a `LazyField` for a column that codegen has marked `lazy: true`. */\nexport function hasLazyField<T extends Entity, V>(): LazyField<T, V> {\n return lazyField((entity: T, fieldName: string) => new LazyFieldImpl<T, V>(entity, fieldName));\n}\n\nexport class LazyFieldImpl<T extends Entity, V> extends AbstractPropertyImpl<T> implements LazyField<T, V> {\n #fieldName: string;\n #loaded = false;\n #loadPromise: Promise<V> | undefined;\n\n constructor(entity: T, fieldName: string) {\n super(entity);\n this.#fieldName = fieldName;\n }\n\n /** Fetches this column from the database (batched across peers) and caches it on the entity. */\n load(opts?: { forceReload?: boolean }): Promise<V> {\n const { entity } = this;\n if (opts?.forceReload) {\n this.#loaded = false;\n this.#loadPromise = undefined;\n }\n if (this.isLoaded) return Promise.resolve(this.#value);\n const meta = getMetadata(entity);\n return (this.#loadPromise ??= lazyColumnBatchLoader(entity.em, meta, this.#fieldName)\n .load(entity.idTagged)\n .then(() => {\n this.#loaded = true;\n return this.#value;\n }));\n }\n\n get get(): V {\n if (!this.isLoaded) {\n throw new Error(`${this.#fieldName} has not been loaded yet, use '.load()' or an 'em.populate' hint`);\n }\n return this.#value;\n }\n\n /** Sets the value in memory; the column is dirtied and written on the next `em.flush`. */\n set(value: V): void {\n if (this.isLoaded) {\n setField(this.entity, this.#fieldName, value);\n } else {\n // Seed `notLoadedValue` as the current value so the set always registers as a change against\n // the unknown db value: an unloaded column otherwise reads as `undefined`, which would make\n // `set(undefined)` a silent no-op, and a later `set(undefined)` falsely revert to clean.\n const { data } = getInstanceData(this.entity);\n data[this.#fieldName] = notLoadedValue;\n try {\n setField(this.entity, this.#fieldName, value);\n } catch (e) {\n // Don't leak the marker as a readable value, i.e. if setting on a deleted entity throws\n delete data[this.#fieldName];\n throw e;\n }\n }\n this.#loaded = true;\n }\n\n get isLoaded(): boolean {\n // New entities keep their value in-memory, so there's nothing to lazy-load.\n if (getInstanceData(this.entity).isOrWasNew) return true;\n return this.#loaded;\n }\n\n /** Called after em.flush to invalidate the cached value, since the db state may have changed. */\n resetAfterFlush(): void {\n this.#loaded = false;\n this.#loadPromise = undefined;\n }\n\n /** The domain value, which `load`/`set` keep in the entity's `__orm.data` like any other column. */\n get #value(): V {\n return getInstanceData(this.entity).data[this.#fieldName] as V;\n }\n\n [PropertyT] = undefined as any as T;\n}\n\n/** Type guard utility for determining if an entity field is a LazyField. */\nexport function isLazyField(maybe: any): maybe is LazyField<any, any> {\n return maybe instanceof LazyFieldImpl;\n}\n\n/** Type guard utility for determining if an entity field is a loaded LazyField. */\nexport function isLoadedLazyField(maybe: any): maybe is LazyField<any, any> & LoadedProperty<any, any> {\n return isLazyField(maybe) && maybe.isLoaded;\n}\n\n/** Batches `LazyField.load`s for the same column into a single `SELECT id, <column> ... WHERE id IN (...)`. */\nfunction lazyColumnBatchLoader(em: EntityManager, meta: EntityMetadata, fieldName: string): BatchLoader<string> {\n return em.getBatchLoader(lazyColumnLoadOperation, `${meta.tableName}-${fieldName}`, async (taggedIds) => {\n const field = meta.allFields[fieldName] as PrimitiveField;\n const column = field.serde.columns[0];\n const alias = abbreviation(meta.tableName);\n const keys = taggedIds.map((id) => keyToNumber(meta, id));\n const query: ParsedFindQuery = {\n selects: [`${kqDot(alias, \"id\")} as id`, kqDot(alias, column.columnName)],\n tables: [{ alias, join: \"primary\", table: meta.tableName }],\n condition: {\n kind: \"exp\",\n op: \"and\",\n conditions: [{ kind: \"column\", alias, column: \"id\", dbType: meta.idDbType, cond: { kind: \"in\", value: keys } }],\n },\n orderBys: [],\n };\n const rows = await em[\"executeFind\"](meta, lazyColumnLoadOperation, query, {});\n const rowData = new PojoRowData(rows);\n for (let i = 0; i < rows.length; i++) {\n const entity = em.findExistingInstance(tagId(meta, rows[i].id));\n if (entity) getInstanceData(entity).data[fieldName] = field.serde.fromRow(rowData, i);\n }\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"LazyField.js","names":["#fieldName","#loaded","#loadPromise","#value"],"sources":["../../src/relations/LazyField.ts"],"sourcesContent":["import { getInstanceData } from \"src/BaseEntity.ts\";\nimport { type BatchLoader } from \"src/batchloaders/BatchLoader.ts\";\nimport { type Entity } from \"src/Entity.ts\";\nimport { type EntityManager } from \"src/EntityManager.ts\";\nimport { type EntityMetadata, type PrimitiveField, getMetadata } from \"src/EntityMetadata.ts\";\nimport { setField } from \"src/fields.ts\";\nimport { keyToNumber, tagId } from \"src/keys.ts\";\nimport { lazyField } from \"src/newEntity.ts\";\nimport type { ParsedFindQuery } from \"src/queries/find/QueryParser.ts\";\nimport { kqDot } from \"src/queries/sql/keywords.ts\";\nimport { AbstractPropertyImpl } from \"src/relations/AbstractPropertyImpl.ts\";\nimport { type LoadedProperty, type Property } from \"src/relations/hasProperty.ts\";\nimport { PropertyT } from \"src/relations/PropertySymbols.ts\";\nimport { PojoRowData } from \"src/RowData.ts\";\nimport { abbreviation } from \"src/utils.ts\";\n\nexport const lazyColumnLoadOperation = \"lazy-column-load\";\n\n/**\n * The \"original value\" of an unloaded lazy column: unknown, and never equal to any set value.\n *\n * Seeded into `__orm.data` by `LazyFieldImpl.set` so that setting an unloaded column (even to\n * `undefined`) always registers as a change, i.e. instead of comparing equal to the `undefined`\n * that the excluded column would otherwise read as.\n */\nexport const notLoadedValue = Symbol(\"notLoadedValue\");\n\n/**\n * A single-column primitive (i.e. a large `jsonb`/`text`) that is deliberately excluded from the\n * entity's default SELECT and only fetched from the database on demand.\n *\n * I.e. `await author.someBigColumn.load()`, or synchronously via `.get` after `em.populate`.\n */\nexport interface LazyField<T extends Entity, V> extends Property<T, V> {\n load(opts?: { forceReload?: boolean }): Promise<V>;\n /** Sets the value in memory (dirtied for the next flush), i.e. without loading the current db value first. */\n set(value: V): void;\n}\n\n/** Creates a `LazyField` for a column that codegen has marked `lazy: true`. */\nexport function hasLazyField<T extends Entity, V>(): LazyField<T, V> {\n return lazyField((entity: T, fieldName: string) => new LazyFieldImpl<T, V>(entity, fieldName));\n}\n\nexport class LazyFieldImpl<T extends Entity, V> extends AbstractPropertyImpl<T> implements LazyField<T, V> {\n #fieldName: string;\n #loaded = false;\n #loadPromise: Promise<V> | undefined;\n\n constructor(entity: T, fieldName: string) {\n super(entity);\n this.#fieldName = fieldName;\n }\n\n /** Fetches this column from the database (batched across peers) and caches it on the entity. */\n load(opts?: { forceReload?: boolean }): Promise<V> {\n const { entity } = this;\n if (opts?.forceReload) {\n this.#loaded = false;\n this.#loadPromise = undefined;\n }\n if (this.isLoaded) return Promise.resolve(this.#value);\n const meta = getMetadata(entity);\n return (this.#loadPromise ??= lazyColumnBatchLoader(entity.em, meta, this.#fieldName)\n .load(entity.idTagged)\n .then(() => {\n this.#loaded = true;\n return this.#value;\n }));\n }\n\n get get(): V {\n if (!this.isLoaded) {\n throw new Error(`${this.#fieldName} has not been loaded yet, use '.load()' or an 'em.populate' hint`);\n }\n return this.#value;\n }\n\n /** Sets the value in memory; the column is dirtied and written on the next `em.flush`. */\n set(value: V): void {\n if (this.isLoaded) {\n setField(this.entity, this.#fieldName, value);\n } else {\n // Seed `notLoadedValue` as the current value so the set always registers as a change against\n // the unknown db value: an unloaded column otherwise reads as `undefined`, which would make\n // `set(undefined)` a silent no-op, and a later `set(undefined)` falsely revert to clean.\n const { data } = getInstanceData(this.entity);\n data[this.#fieldName] = notLoadedValue;\n try {\n setField(this.entity, this.#fieldName, value);\n } catch (e) {\n // Don't leak the marker as a readable value, i.e. if setting on a deleted entity throws\n delete data[this.#fieldName];\n throw e;\n }\n }\n this.#loaded = true;\n }\n\n get isLoaded(): boolean {\n // New entities keep their value in-memory, so there's nothing to lazy-load.\n if (getInstanceData(this.entity).isOrWasNew) return true;\n return this.#loaded;\n }\n\n /**\n * Marks this field loaded, using its value in entity data or reading it from the entity's row.\n *\n * The caller must know the value is present in entity data or the row; default SELECTs omit lazy columns.\n */\n preload(): void {\n const { data, rowData, rowIndex } = getInstanceData(this.entity);\n if (!(this.#fieldName in data)) {\n const field = getMetadata(this.entity).allFields[this.#fieldName];\n data[this.#fieldName] = field.serde!.fromRow(rowData, rowIndex);\n }\n\n this.#loaded = true;\n this.#loadPromise = undefined;\n }\n\n /** Called after em.flush to invalidate the cached value, since the db state may have changed. */\n resetAfterFlush(): void {\n this.#loaded = false;\n this.#loadPromise = undefined;\n }\n\n /** The domain value, which `load`/`set` keep in the entity's `__orm.data` like any other column. */\n get #value(): V {\n return getInstanceData(this.entity).data[this.#fieldName] as V;\n }\n\n [PropertyT] = undefined as any as T;\n}\n\n/** Type guard utility for determining if an entity field is a LazyField. */\nexport function isLazyField(maybe: any): maybe is LazyField<any, any> {\n return maybe instanceof LazyFieldImpl;\n}\n\n/** Type guard utility for determining if an entity field is a loaded LazyField. */\nexport function isLoadedLazyField(maybe: any): maybe is LazyField<any, any> & LoadedProperty<any, any> {\n return isLazyField(maybe) && maybe.isLoaded;\n}\n\n/** Batches `LazyField.load`s for the same column into a single `SELECT id, <column> ... WHERE id IN (...)`. */\nfunction lazyColumnBatchLoader(em: EntityManager, meta: EntityMetadata, fieldName: string): BatchLoader<string> {\n return em.getBatchLoader(lazyColumnLoadOperation, `${meta.tableName}-${fieldName}`, async (taggedIds) => {\n const field = meta.allFields[fieldName] as PrimitiveField;\n const column = field.serde.columns[0];\n const alias = abbreviation(meta.tableName);\n const keys = taggedIds.map((id) => keyToNumber(meta, id));\n const query: ParsedFindQuery = {\n selects: [`${kqDot(alias, \"id\")} as id`, kqDot(alias, column.columnName)],\n tables: [{ alias, join: \"primary\", table: meta.tableName }],\n condition: {\n kind: \"exp\",\n op: \"and\",\n conditions: [{ kind: \"column\", alias, column: \"id\", dbType: meta.idDbType, cond: { kind: \"in\", value: keys } }],\n },\n orderBys: [],\n };\n const rows = await em[\"executeFind\"](meta, lazyColumnLoadOperation, query, {});\n const rowData = new PojoRowData(rows);\n for (let i = 0; i < rows.length; i++) {\n const entity = em.findExistingInstance(tagId(meta, rows[i].id));\n if (entity) getInstanceData(entity).data[fieldName] = field.serde.fromRow(rowData, i);\n }\n });\n}\n"],"mappings":"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"}
@@ -1,10 +1,10 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_relations_AbstractPropertyImpl = require("./AbstractPropertyImpl.cjs");
3
- const require_relations_PropertySymbols = require("./PropertySymbols.cjs");
4
- const require_reactivity_reactiveHints = require("../reactivity/reactiveHints.cjs");
5
3
  const require_loading_loadHints = require("../loading/loadHints.cjs");
4
+ const require_relations_PropertySymbols = require("./PropertySymbols.cjs");
6
5
  const require_fields = require("../fields.cjs");
7
6
  const require_newEntity = require("../newEntity.cjs");
7
+ const require_reactivity_reactiveHints = require("../reactivity/reactiveHints.cjs");
8
8
  const require_EntityMetadata = require("../EntityMetadata.cjs");
9
9
  require("../index.cjs");
10
10
  const require_EntityManager = require("../EntityManager.cjs");
@@ -1,9 +1,9 @@
1
1
  import { AbstractPropertyImpl } from "./AbstractPropertyImpl.js";
2
- import { PropertyT } from "./PropertySymbols.js";
3
- import { convertToLoadHint } from "../reactivity/reactiveHints.js";
4
2
  import { isLoaded } from "../loading/loadHints.js";
3
+ import { PropertyT } from "./PropertySymbols.js";
5
4
  import { getField, isFieldSet, setField } from "../fields.js";
6
5
  import { lazyField } from "../newEntity.js";
6
+ import { convertToLoadHint } from "../reactivity/reactiveHints.js";
7
7
  import { getMetadata } from "../EntityMetadata.js";
8
8
  import "../index.js";
9
9
  import { getEmInternalApi } from "../EntityManager.js";
@@ -1,12 +1,12 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_relations_AbstractRelationImpl = require("./AbstractRelationImpl.cjs");
3
3
  const require_relations_RelationSymbols = require("./RelationSymbols.cjs");
4
- const require_reactivity_reactiveHints = require("../reactivity/reactiveHints.cjs");
5
4
  const require_loading_loadHints = require("../loading/loadHints.cjs");
6
5
  const require_batchloaders_manyToManyBatchLoader = require("../batchloaders/manyToManyBatchLoader.cjs");
7
6
  const require_relations_isCascadeDelete = require("./isCascadeDelete.cjs");
8
7
  const require_utils = require("../utils.cjs");
9
8
  const require_newEntity = require("../newEntity.cjs");
9
+ const require_reactivity_reactiveHints = require("../reactivity/reactiveHints.cjs");
10
10
  const require_EntityMetadata = require("../EntityMetadata.cjs");
11
11
  const require_index = require("../index.cjs");
12
12
  const require_EntityManager = require("../EntityManager.cjs");
@@ -1,11 +1,11 @@
1
1
  import { AbstractRelationImpl } from "./AbstractRelationImpl.js";
2
2
  import { RelationT, RelationU } from "./RelationSymbols.js";
3
- import { convertToLoadHint } from "../reactivity/reactiveHints.js";
4
3
  import { isLoaded } from "../loading/loadHints.js";
5
4
  import { manyToManyBatchLoader } from "../batchloaders/manyToManyBatchLoader.js";
6
5
  import { isCascadeDelete } from "./isCascadeDelete.js";
7
6
  import { fail } from "../utils.js";
8
7
  import { lazyField } from "../newEntity.js";
8
+ import { convertToLoadHint } from "../reactivity/reactiveHints.js";
9
9
  import { getMetadata, getMetadataForField } from "../EntityMetadata.js";
10
10
  import { ensureNotDeleted } from "../index.js";
11
11
  import { getEmInternalApi } from "../EntityManager.js";
@@ -2,7 +2,6 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_relations_AbstractRelationImpl = require("./AbstractRelationImpl.cjs");
3
3
  const require_relations_RelationSymbols = require("./RelationSymbols.cjs");
4
4
  const require_relations_ReferenceSymbols = require("./ReferenceSymbols.cjs");
5
- const require_reactivity_reactiveHints = require("../reactivity/reactiveHints.cjs");
6
5
  const require_loading_loadHints = require("../loading/loadHints.cjs");
7
6
  const require_fields = require("../fields.cjs");
8
7
  const require_keys = require("../keys.cjs");
@@ -11,6 +10,7 @@ const require_relations_isCascadeDelete = require("./isCascadeDelete.cjs");
11
10
  const require_relations_ManyToOneReference = require("./ManyToOneReference.cjs");
12
11
  const require_utils = require("../utils.cjs");
13
12
  const require_newEntity = require("../newEntity.cjs");
13
+ const require_reactivity_reactiveHints = require("../reactivity/reactiveHints.cjs");
14
14
  const require_EntityMetadata = require("../EntityMetadata.cjs");
15
15
  const require_index = require("../index.cjs");
16
16
  const require_EntityManager = require("../EntityManager.cjs");
@@ -1,7 +1,6 @@
1
1
  import { AbstractRelationImpl } from "./AbstractRelationImpl.js";
2
2
  import { RelationT, RelationU } from "./RelationSymbols.js";
3
3
  import { ReferenceN } from "./ReferenceSymbols.js";
4
- import { convertToLoadHint } from "../reactivity/reactiveHints.js";
5
4
  import { isLoaded } from "../loading/loadHints.js";
6
5
  import { getField, setField } from "../fields.js";
7
6
  import { deTagId, maybeResolveReferenceToId, toIdOf } from "../keys.js";
@@ -10,6 +9,7 @@ import { isCascadeDelete } from "./isCascadeDelete.js";
10
9
  import { failIfNewEntity, failNoId } from "./ManyToOneReference.js";
11
10
  import { fail } from "../utils.js";
12
11
  import { lazyField } from "../newEntity.js";
12
+ import { convertToLoadHint } from "../reactivity/reactiveHints.js";
13
13
  import { getMetadata } from "../EntityMetadata.js";
14
14
  import { ensureNotDeleted } from "../index.js";
15
15
  import { getEmInternalApi, sameEntity } from "../EntityManager.js";
@@ -2,13 +2,13 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_relations_Collection = require("./Collection.cjs");
3
3
  const require_relations_AbstractRelationImpl = require("./AbstractRelationImpl.cjs");
4
4
  const require_relations_RelationSymbols = require("./RelationSymbols.cjs");
5
+ const require_relations_RecursiveCycleError = require("./RecursiveCycleError.cjs");
6
+ const require_relations_Reference = require("./Reference.cjs");
7
+ const require_relations_OneToOneReference = require("./OneToOneReference.cjs");
5
8
  const require_batchloaders_recursiveChildrenBatchLoader = require("../batchloaders/recursiveChildrenBatchLoader.cjs");
6
9
  const require_batchloaders_recursiveM2mBatchLoader = require("../batchloaders/recursiveM2mBatchLoader.cjs");
7
10
  const require_batchloaders_recursiveParentsBatchLoader = require("../batchloaders/recursiveParentsBatchLoader.cjs");
8
11
  require("./ReadOnlyCollection.cjs");
9
- const require_relations_RecursiveCycleError = require("./RecursiveCycleError.cjs");
10
- const require_relations_Reference = require("./Reference.cjs");
11
- const require_relations_OneToOneReference = require("./OneToOneReference.cjs");
12
12
  const require_utils = require("../utils.cjs");
13
13
  const require_newEntity = require("../newEntity.cjs");
14
14
  const require_EntityMetadata = require("../EntityMetadata.cjs");