@expo/entity 0.35.0 → 0.37.0

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 (309) hide show
  1. package/build/AuthorizationResultBasedEntityLoader.d.ts +128 -0
  2. package/build/AuthorizationResultBasedEntityLoader.js +196 -0
  3. package/build/AuthorizationResultBasedEntityLoader.js.map +1 -0
  4. package/build/ComposedEntityCacheAdapter.js +1 -0
  5. package/build/ComposedEntityCacheAdapter.js.map +1 -1
  6. package/build/ComposedSecondaryEntityCache.js +1 -0
  7. package/build/ComposedSecondaryEntityCache.js.map +1 -1
  8. package/build/EnforcingEntityLoader.d.ts +5 -4
  9. package/build/EnforcingEntityLoader.js +4 -2
  10. package/build/EnforcingEntityLoader.js.map +1 -1
  11. package/build/Entity.d.ts +0 -27
  12. package/build/Entity.js +0 -50
  13. package/build/Entity.js.map +1 -1
  14. package/build/EntityAssociationLoader.d.ts +1 -1
  15. package/build/EntityAssociationLoader.js +17 -8
  16. package/build/EntityAssociationLoader.js.map +1 -1
  17. package/build/EntityCompanion.js +9 -1
  18. package/build/EntityCompanion.js.map +1 -1
  19. package/build/EntityCompanionProvider.d.ts +3 -1
  20. package/build/EntityCompanionProvider.js +10 -4
  21. package/build/EntityCompanionProvider.js.map +1 -1
  22. package/build/EntityConfiguration.d.ts +2 -1
  23. package/build/EntityConfiguration.js +19 -1
  24. package/build/EntityConfiguration.js.map +1 -1
  25. package/build/EntityDatabaseAdapter.d.ts +2 -2
  26. package/build/EntityDatabaseAdapter.js +5 -3
  27. package/build/EntityDatabaseAdapter.js.map +1 -1
  28. package/build/EntityFieldDefinition.d.ts +21 -10
  29. package/build/EntityFieldDefinition.js +8 -9
  30. package/build/EntityFieldDefinition.js.map +1 -1
  31. package/build/EntityFields.d.ts +10 -0
  32. package/build/EntityFields.js +15 -1
  33. package/build/EntityFields.js.map +1 -1
  34. package/build/EntityLoader.d.ts +12 -125
  35. package/build/EntityLoader.js +24 -239
  36. package/build/EntityLoader.js.map +1 -1
  37. package/build/EntityLoaderFactory.d.ts +1 -1
  38. package/build/EntityLoaderFactory.js +3 -0
  39. package/build/EntityLoaderFactory.js.map +1 -1
  40. package/build/EntityLoaderUtils.d.ts +58 -0
  41. package/build/EntityLoaderUtils.js +109 -0
  42. package/build/EntityLoaderUtils.js.map +1 -0
  43. package/build/EntityMutator.d.ts +1 -0
  44. package/build/EntityMutator.js +71 -56
  45. package/build/EntityMutator.js.map +1 -1
  46. package/build/EntityMutatorFactory.js +9 -0
  47. package/build/EntityMutatorFactory.js.map +1 -1
  48. package/build/EntityPrivacyPolicy.d.ts +11 -5
  49. package/build/EntityPrivacyPolicy.js +5 -7
  50. package/build/EntityPrivacyPolicy.js.map +1 -1
  51. package/build/EntityQueryContext.d.ts +2 -1
  52. package/build/EntityQueryContext.js +11 -6
  53. package/build/EntityQueryContext.js.map +1 -1
  54. package/build/EntitySecondaryCacheLoader.js +5 -1
  55. package/build/EntitySecondaryCacheLoader.js.map +1 -1
  56. package/build/GenericEntityCacheAdapter.js +1 -0
  57. package/build/GenericEntityCacheAdapter.js.map +1 -1
  58. package/build/GenericSecondaryEntityCache.js +2 -0
  59. package/build/GenericSecondaryEntityCache.js.map +1 -1
  60. package/build/IEntityCacheAdapterProvider.d.ts +1 -1
  61. package/build/IEntityDatabaseAdapterProvider.d.ts +1 -1
  62. package/build/ReadonlyEntity.js +5 -1
  63. package/build/ReadonlyEntity.js.map +1 -1
  64. package/build/ViewerContext.js +2 -0
  65. package/build/ViewerContext.js.map +1 -1
  66. package/build/ViewerScopedEntityCompanion.js +2 -0
  67. package/build/ViewerScopedEntityCompanion.js.map +1 -1
  68. package/build/ViewerScopedEntityCompanionProvider.d.ts +0 -1
  69. package/build/ViewerScopedEntityCompanionProvider.js +2 -1
  70. package/build/ViewerScopedEntityCompanionProvider.js.map +1 -1
  71. package/build/ViewerScopedEntityLoaderFactory.d.ts +1 -1
  72. package/build/ViewerScopedEntityLoaderFactory.js +2 -0
  73. package/build/ViewerScopedEntityLoaderFactory.js.map +1 -1
  74. package/build/ViewerScopedEntityMutatorFactory.js +2 -0
  75. package/build/ViewerScopedEntityMutatorFactory.js.map +1 -1
  76. package/build/__tests__/ComposedCacheAdapter-test.js +2 -0
  77. package/build/__tests__/ComposedCacheAdapter-test.js.map +1 -1
  78. package/build/__tests__/ComposedSecondaryEntityCache-test.js +1 -0
  79. package/build/__tests__/ComposedSecondaryEntityCache-test.js.map +1 -1
  80. package/build/__tests__/EnforcingEntityLoader-test.js +101 -113
  81. package/build/__tests__/EnforcingEntityLoader-test.js.map +1 -1
  82. package/build/__tests__/Entity-test.js +0 -132
  83. package/build/__tests__/Entity-test.js.map +1 -1
  84. package/build/__tests__/EntityAssociationLoader-test.js +6 -2
  85. package/build/__tests__/EntityAssociationLoader-test.js.map +1 -1
  86. package/build/__tests__/EntityCommonUseCases-test.js +24 -22
  87. package/build/__tests__/EntityCommonUseCases-test.js.map +1 -1
  88. package/build/__tests__/EntityCompanion-test.js +26 -3
  89. package/build/__tests__/EntityCompanion-test.js.map +1 -1
  90. package/build/__tests__/EntityConfiguration-test.js +103 -0
  91. package/build/__tests__/EntityConfiguration-test.js.map +1 -0
  92. package/build/__tests__/EntityDatabaseAdapter-test.js +6 -0
  93. package/build/__tests__/EntityDatabaseAdapter-test.js.map +1 -1
  94. package/build/__tests__/EntityEdges-test.js +61 -20
  95. package/build/__tests__/EntityEdges-test.js.map +1 -1
  96. package/build/__tests__/EntityFields-test.js +6 -0
  97. package/build/__tests__/EntityFields-test.js.map +1 -1
  98. package/build/__tests__/EntityLoader-constructor-test.js +16 -17
  99. package/build/__tests__/EntityLoader-constructor-test.js.map +1 -1
  100. package/build/__tests__/EntityLoader-test.js +74 -22
  101. package/build/__tests__/EntityLoader-test.js.map +1 -1
  102. package/build/__tests__/EntityMutator-MutationCacheConsistency-test.js +12 -15
  103. package/build/__tests__/EntityMutator-MutationCacheConsistency-test.js.map +1 -1
  104. package/build/__tests__/EntityMutator-test.js +54 -9
  105. package/build/__tests__/EntityMutator-test.js.map +1 -1
  106. package/build/__tests__/EntityPrivacyPolicy-test.js +77 -59
  107. package/build/__tests__/EntityPrivacyPolicy-test.js.map +1 -1
  108. package/build/__tests__/EntityQueryContext-test.js +9 -0
  109. package/build/__tests__/EntityQueryContext-test.js.map +1 -1
  110. package/build/__tests__/EntitySelfReferentialEdges-test.js +42 -25
  111. package/build/__tests__/EntitySelfReferentialEdges-test.js.map +1 -1
  112. package/build/__tests__/ViewerScopedEntityLoaderFactory-test.js.map +1 -1
  113. package/build/__tests__/cases/TwoEntitySameTableDisjointRows-test.js +20 -18
  114. package/build/__tests__/cases/TwoEntitySameTableDisjointRows-test.js.map +1 -1
  115. package/build/__tests__/cases/TwoEntitySameTableOverlappingRows-test.js +12 -15
  116. package/build/__tests__/cases/TwoEntitySameTableOverlappingRows-test.js.map +1 -1
  117. package/build/entityUtils.d.ts +1 -1
  118. package/build/entityUtils.js.map +1 -1
  119. package/build/errors/EntityCacheAdapterError.js +2 -5
  120. package/build/errors/EntityCacheAdapterError.js.map +1 -1
  121. package/build/errors/EntityDatabaseAdapterError.js +14 -35
  122. package/build/errors/EntityDatabaseAdapterError.js.map +1 -1
  123. package/build/errors/EntityError.js +1 -0
  124. package/build/errors/EntityError.js.map +1 -1
  125. package/build/errors/EntityInvalidFieldValueError.js +2 -2
  126. package/build/errors/EntityInvalidFieldValueError.js.map +1 -1
  127. package/build/errors/EntityNotAuthorizedError.js +3 -2
  128. package/build/errors/EntityNotAuthorizedError.js.map +1 -1
  129. package/build/errors/EntityNotFoundError.js +2 -2
  130. package/build/errors/EntityNotFoundError.js.map +1 -1
  131. package/build/index.d.ts +26 -20
  132. package/build/index.js +38 -28
  133. package/build/index.js.map +1 -1
  134. package/build/internal/EntityDataManager.d.ts +1 -1
  135. package/build/internal/EntityDataManager.js +6 -1
  136. package/build/internal/EntityDataManager.js.map +1 -1
  137. package/build/internal/EntityFieldTransformationUtils.d.ts +5 -5
  138. package/build/internal/EntityFieldTransformationUtils.js +5 -8
  139. package/build/internal/EntityFieldTransformationUtils.js.map +1 -1
  140. package/build/internal/EntityTableDataCoordinator.d.ts +1 -1
  141. package/build/internal/EntityTableDataCoordinator.js +5 -0
  142. package/build/internal/EntityTableDataCoordinator.js.map +1 -1
  143. package/build/internal/ReadThroughEntityCache.d.ts +1 -1
  144. package/build/internal/ReadThroughEntityCache.js +2 -0
  145. package/build/internal/ReadThroughEntityCache.js.map +1 -1
  146. package/build/internal/__tests__/EntityFieldTransformationUtils-test.js +6 -2
  147. package/build/internal/__tests__/EntityFieldTransformationUtils-test.js.map +1 -1
  148. package/build/internal/__tests__/ReadThroughEntityCache-test.js +33 -0
  149. package/build/internal/__tests__/ReadThroughEntityCache-test.js.map +1 -1
  150. package/build/metrics/IEntityMetricsAdapter.d.ts +1 -1
  151. package/build/rules/AlwaysAllowPrivacyPolicyRule.d.ts +1 -1
  152. package/build/rules/AlwaysAllowPrivacyPolicyRule.js.map +1 -1
  153. package/build/rules/AlwaysDenyPrivacyPolicyRule.d.ts +1 -1
  154. package/build/rules/AlwaysDenyPrivacyPolicyRule.js.map +1 -1
  155. package/build/rules/AlwaysSkipPrivacyPolicyRule.d.ts +1 -1
  156. package/build/rules/AlwaysSkipPrivacyPolicyRule.js.map +1 -1
  157. package/build/rules/PrivacyPolicyRule.d.ts +1 -1
  158. package/build/rules/PrivacyPolicyRule.js.map +1 -1
  159. package/build/rules/__tests__/AlwaysAllowPrivacyPolicyRule-test.js.map +1 -1
  160. package/build/rules/__tests__/AlwaysDenyPrivacyPolicyRule-test.js.map +1 -1
  161. package/build/rules/__tests__/AlwaysSkipPrivacyPolicyRule-test.js.map +1 -1
  162. package/build/testfixtures/DateIDTestEntity.js +12 -15
  163. package/build/testfixtures/DateIDTestEntity.js.map +1 -1
  164. package/build/testfixtures/SimpleTestEntity.js +12 -15
  165. package/build/testfixtures/SimpleTestEntity.js.map +1 -1
  166. package/build/testfixtures/TestEntity.js +12 -15
  167. package/build/testfixtures/TestEntity.js.map +1 -1
  168. package/build/testfixtures/TestEntity2.js +12 -15
  169. package/build/testfixtures/TestEntity2.js.map +1 -1
  170. package/build/testfixtures/TestEntityNumberKey.js +12 -15
  171. package/build/testfixtures/TestEntityNumberKey.js.map +1 -1
  172. package/build/testfixtures/TestEntityWithMutationTriggers.d.ts +36 -0
  173. package/build/testfixtures/TestEntityWithMutationTriggers.js +82 -0
  174. package/build/testfixtures/TestEntityWithMutationTriggers.js.map +1 -0
  175. package/build/utils/EntityPrivacyUtils.d.ts +34 -0
  176. package/build/utils/EntityPrivacyUtils.js +160 -0
  177. package/build/utils/EntityPrivacyUtils.js.map +1 -0
  178. package/build/utils/__tests__/EntityPrivacyUtils-test.d.ts +1 -0
  179. package/build/utils/__tests__/EntityPrivacyUtils-test.js +395 -0
  180. package/build/utils/__tests__/EntityPrivacyUtils-test.js.map +1 -0
  181. package/build/utils/__tests__/mergeEntityMutationTriggerConfigurations-test.d.ts +1 -0
  182. package/build/utils/__tests__/mergeEntityMutationTriggerConfigurations-test.js +26 -0
  183. package/build/utils/__tests__/mergeEntityMutationTriggerConfigurations-test.js.map +1 -0
  184. package/build/utils/collections/maps.js.map +1 -1
  185. package/build/utils/mergeEntityMutationTriggerConfigurations.d.ts +4 -0
  186. package/build/utils/mergeEntityMutationTriggerConfigurations.js +28 -0
  187. package/build/utils/mergeEntityMutationTriggerConfigurations.js.map +1 -0
  188. package/build/utils/testing/PrivacyPolicyRuleTestUtils.d.ts +1 -1
  189. package/build/utils/testing/PrivacyPolicyRuleTestUtils.js.map +1 -1
  190. package/build/utils/testing/StubCacheAdapter.d.ts +3 -3
  191. package/build/utils/testing/StubCacheAdapter.js +3 -3
  192. package/build/utils/testing/StubCacheAdapter.js.map +1 -1
  193. package/build/utils/testing/StubDatabaseAdapter.d.ts +2 -2
  194. package/build/utils/testing/StubDatabaseAdapter.js +4 -2
  195. package/build/utils/testing/StubDatabaseAdapter.js.map +1 -1
  196. package/build/utils/testing/StubDatabaseAdapterProvider.d.ts +1 -1
  197. package/build/utils/testing/StubDatabaseAdapterProvider.js +1 -3
  198. package/build/utils/testing/StubDatabaseAdapterProvider.js.map +1 -1
  199. package/build/utils/testing/__tests__/PrivacyPolicyRuleTestUtils-test.d.ts +1 -0
  200. package/build/utils/testing/__tests__/PrivacyPolicyRuleTestUtils-test.js +42 -0
  201. package/build/utils/testing/__tests__/PrivacyPolicyRuleTestUtils-test.js.map +1 -0
  202. package/build/utils/testing/__tests__/StubDatabaseAdapter-test.js +53 -0
  203. package/build/utils/testing/__tests__/StubDatabaseAdapter-test.js.map +1 -1
  204. package/build/utils/testing/describeFieldTestCase.js.map +1 -1
  205. package/package.json +5 -4
  206. package/src/AuthorizationResultBasedEntityLoader.ts +297 -0
  207. package/src/ComposedEntityCacheAdapter.ts +6 -6
  208. package/src/ComposedSecondaryEntityCache.ts +8 -8
  209. package/src/EnforcingEntityLoader.ts +20 -19
  210. package/src/Entity.ts +11 -126
  211. package/src/EntityAssociationLoader.ts +40 -41
  212. package/src/EntityCompanion.ts +8 -4
  213. package/src/EntityCompanionProvider.ts +24 -16
  214. package/src/EntityConfiguration.ts +18 -7
  215. package/src/EntityDatabaseAdapter.ts +41 -41
  216. package/src/EntityFieldDefinition.ts +28 -18
  217. package/src/EntityFields.ts +15 -0
  218. package/src/EntityLoader.ts +63 -357
  219. package/src/EntityLoaderFactory.ts +10 -4
  220. package/src/EntityLoaderUtils.ts +149 -0
  221. package/src/EntityMutationInfo.ts +2 -2
  222. package/src/EntityMutationTriggerConfiguration.ts +5 -5
  223. package/src/EntityMutationValidator.ts +2 -2
  224. package/src/EntityMutator.ts +146 -144
  225. package/src/EntityMutatorFactory.ts +8 -8
  226. package/src/EntityPrivacyPolicy.ts +78 -28
  227. package/src/EntityQueryContext.ts +14 -13
  228. package/src/EntityQueryContextProvider.ts +5 -5
  229. package/src/EntitySecondaryCacheLoader.ts +13 -11
  230. package/src/GenericEntityCacheAdapter.ts +10 -10
  231. package/src/GenericSecondaryEntityCache.ts +6 -6
  232. package/src/IEntityCacheAdapter.ts +4 -4
  233. package/src/IEntityCacheAdapterProvider.ts +2 -2
  234. package/src/IEntityDatabaseAdapterProvider.ts +2 -2
  235. package/src/ReadonlyEntity.ts +5 -5
  236. package/src/ViewerContext.ts +5 -5
  237. package/src/ViewerScopedEntityCompanion.ts +4 -4
  238. package/src/ViewerScopedEntityCompanionProvider.ts +4 -5
  239. package/src/ViewerScopedEntityLoaderFactory.ts +10 -4
  240. package/src/ViewerScopedEntityMutatorFactory.ts +5 -5
  241. package/src/__tests__/ComposedCacheAdapter-test.ts +12 -10
  242. package/src/__tests__/ComposedSecondaryEntityCache-test.ts +8 -8
  243. package/src/__tests__/EnforcingEntityLoader-test.ts +236 -159
  244. package/src/__tests__/Entity-test.ts +0 -202
  245. package/src/__tests__/EntityAssociationLoader-test.ts +29 -25
  246. package/src/__tests__/EntityCommonUseCases-test.ts +29 -13
  247. package/src/__tests__/EntityCompanion-test.ts +57 -5
  248. package/src/__tests__/EntityConfiguration-test.ts +118 -0
  249. package/src/__tests__/EntityDatabaseAdapter-test.ts +11 -11
  250. package/src/__tests__/EntityEdges-test.ts +108 -36
  251. package/src/__tests__/EntityFields-test.ts +14 -2
  252. package/src/__tests__/EntityLoader-constructor-test.ts +20 -7
  253. package/src/__tests__/EntityLoader-test.ts +214 -86
  254. package/src/__tests__/EntityMutator-MutationCacheConsistency-test.ts +2 -2
  255. package/src/__tests__/EntityMutator-test.ts +281 -96
  256. package/src/__tests__/EntityPrivacyPolicy-test.ts +166 -53
  257. package/src/__tests__/EntityQueryContext-test.ts +30 -12
  258. package/src/__tests__/EntitySecondaryCacheLoader-test.ts +7 -7
  259. package/src/__tests__/EntitySelfReferentialEdges-test.ts +46 -26
  260. package/src/__tests__/GenericEntityCacheAdapter-test.ts +2 -2
  261. package/src/__tests__/ViewerContext-test.ts +1 -1
  262. package/src/__tests__/ViewerScopedEntityCompanion-test.ts +2 -2
  263. package/src/__tests__/ViewerScopedEntityCompanionProvider-test.ts +2 -2
  264. package/src/__tests__/ViewerScopedEntityLoaderFactory-test.ts +2 -1
  265. package/src/__tests__/cases/TwoEntitySameTableDisjointRows-test.ts +19 -19
  266. package/src/__tests__/entityUtils-test.ts +2 -2
  267. package/src/entityUtils.ts +4 -4
  268. package/src/errors/EntityError.ts +4 -1
  269. package/src/errors/EntityInvalidFieldValueError.ts +2 -2
  270. package/src/errors/EntityNotAuthorizedError.ts +3 -3
  271. package/src/errors/EntityNotFoundError.ts +2 -2
  272. package/src/index.ts +26 -20
  273. package/src/internal/EntityDataManager.ts +24 -24
  274. package/src/internal/EntityFieldTransformationUtils.ts +39 -32
  275. package/src/internal/EntityTableDataCoordinator.ts +3 -3
  276. package/src/internal/ReadThroughEntityCache.ts +9 -9
  277. package/src/internal/__tests__/EntityDataManager-test.ts +51 -51
  278. package/src/internal/__tests__/EntityFieldTransformationUtils-test.ts +14 -10
  279. package/src/internal/__tests__/ReadThroughEntityCache-test.ts +74 -18
  280. package/src/metrics/EntityMetricsUtils.ts +4 -4
  281. package/src/metrics/IEntityMetricsAdapter.ts +1 -1
  282. package/src/rules/AlwaysAllowPrivacyPolicyRule.ts +9 -3
  283. package/src/rules/AlwaysDenyPrivacyPolicyRule.ts +9 -3
  284. package/src/rules/AlwaysSkipPrivacyPolicyRule.ts +9 -3
  285. package/src/rules/PrivacyPolicyRule.ts +9 -3
  286. package/src/rules/__tests__/AlwaysAllowPrivacyPolicyRule-test.ts +2 -1
  287. package/src/rules/__tests__/AlwaysDenyPrivacyPolicyRule-test.ts +2 -1
  288. package/src/rules/__tests__/AlwaysSkipPrivacyPolicyRule-test.ts +2 -1
  289. package/src/testfixtures/TestEntity.ts +1 -1
  290. package/src/testfixtures/TestEntityWithMutationTriggers.ts +156 -0
  291. package/src/utils/EntityPrivacyUtils.ts +325 -0
  292. package/src/utils/__tests__/EntityPrivacyUtils-test.ts +570 -0
  293. package/src/utils/__tests__/mergeEntityMutationTriggerConfigurations-test.ts +29 -0
  294. package/src/utils/collections/__tests__/maps-test.ts +2 -2
  295. package/src/utils/collections/maps.ts +11 -11
  296. package/src/utils/mergeEntityMutationTriggerConfigurations.ts +44 -0
  297. package/src/utils/testing/PrivacyPolicyRuleTestUtils.ts +25 -22
  298. package/src/utils/testing/StubCacheAdapter.ts +17 -15
  299. package/src/utils/testing/StubDatabaseAdapter.ts +35 -30
  300. package/src/utils/testing/StubDatabaseAdapterProvider.ts +2 -2
  301. package/src/utils/testing/StubQueryContextProvider.ts +2 -2
  302. package/src/utils/testing/__tests__/PrivacyPolicyRuleTestUtils-test.ts +42 -0
  303. package/src/utils/testing/__tests__/StubDatabaseAdapter-test.ts +111 -29
  304. package/src/utils/testing/createUnitTestEntityCompanionProvider.ts +2 -2
  305. package/src/utils/testing/describeFieldTestCase.ts +1 -1
  306. package/build/__tests__/EntityDataConfiguration-test.js +0 -68
  307. package/build/__tests__/EntityDataConfiguration-test.js.map +0 -1
  308. package/src/__tests__/EntityDataConfiguration-test.ts +0 -77
  309. /package/build/__tests__/{EntityDataConfiguration-test.d.ts → EntityConfiguration-test.d.ts} +0 -0
package/src/Entity.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Result, asyncResult } from '@expo/results';
1
+ import { Result } from '@expo/results';
2
2
 
3
3
  import { EntityCompanionDefinition } from './EntityCompanionProvider';
4
4
  import { CreateMutator, UpdateMutator } from './EntityMutator';
@@ -29,7 +29,7 @@ export default abstract class Entity<
29
29
  TFields extends object,
30
30
  TID extends NonNullable<TFields[TSelectedFields]>,
31
31
  TViewerContext extends ViewerContext,
32
- TSelectedFields extends keyof TFields = keyof TFields
32
+ TSelectedFields extends keyof TFields = keyof TFields,
33
33
  > extends ReadonlyEntity<TFields, TID, TViewerContext, TSelectedFields> {
34
34
  /**
35
35
  * Vend mutator for creating a new entity in given query context.
@@ -50,7 +50,7 @@ export default abstract class Entity<
50
50
  TMEntity,
51
51
  TMSelectedFields
52
52
  >,
53
- TMSelectedFields extends keyof TMFields = keyof TMFields
53
+ TMSelectedFields extends keyof TMFields = keyof TMFields,
54
54
  >(
55
55
  this: IEntityClass<
56
56
  TMFields,
@@ -64,7 +64,7 @@ export default abstract class Entity<
64
64
  queryContext: EntityQueryContext = viewerContext
65
65
  .getViewerScopedEntityCompanionForClass(this)
66
66
  .getQueryContextProvider()
67
- .getQueryContext()
67
+ .getQueryContext(),
68
68
  ): CreateMutator<TMFields, TMID, TMViewerContext, TMEntity, TMPrivacyPolicy, TMSelectedFields> {
69
69
  return viewerContext
70
70
  .getViewerScopedEntityCompanionForClass(this)
@@ -90,7 +90,7 @@ export default abstract class Entity<
90
90
  TMEntity,
91
91
  TMSelectedFields
92
92
  >,
93
- TMSelectedFields extends keyof TMFields = keyof TMFields
93
+ TMSelectedFields extends keyof TMFields = keyof TMFields,
94
94
  >(
95
95
  this: IEntityClass<
96
96
  TMFields,
@@ -105,7 +105,7 @@ export default abstract class Entity<
105
105
  .getViewerContext()
106
106
  .getViewerScopedEntityCompanionForClass(this)
107
107
  .getQueryContextProvider()
108
- .getQueryContext()
108
+ .getQueryContext(),
109
109
  ): UpdateMutator<TMFields, TMID, TMViewerContext, TMEntity, TMPrivacyPolicy, TMSelectedFields> {
110
110
  return existingEntity
111
111
  .getViewerContext()
@@ -131,7 +131,7 @@ export default abstract class Entity<
131
131
  TMEntity,
132
132
  TMSelectedFields
133
133
  >,
134
- TMSelectedFields extends keyof TMFields = keyof TMFields
134
+ TMSelectedFields extends keyof TMFields = keyof TMFields,
135
135
  >(
136
136
  this: IEntityClass<
137
137
  TMFields,
@@ -146,7 +146,7 @@ export default abstract class Entity<
146
146
  .getViewerContext()
147
147
  .getViewerScopedEntityCompanionForClass(this)
148
148
  .getQueryContextProvider()
149
- .getQueryContext()
149
+ .getQueryContext(),
150
150
  ): Promise<Result<void>> {
151
151
  return existingEntity
152
152
  .getViewerContext()
@@ -173,7 +173,7 @@ export default abstract class Entity<
173
173
  TMEntity,
174
174
  TMSelectedFields
175
175
  >,
176
- TMSelectedFields extends keyof TMFields = keyof TMFields
176
+ TMSelectedFields extends keyof TMFields = keyof TMFields,
177
177
  >(
178
178
  this: IEntityClass<
179
179
  TMFields,
@@ -188,7 +188,7 @@ export default abstract class Entity<
188
188
  .getViewerContext()
189
189
  .getViewerScopedEntityCompanionForClass(this)
190
190
  .getQueryContextProvider()
191
- .getQueryContext()
191
+ .getQueryContext(),
192
192
  ): Promise<void> {
193
193
  return existingEntity
194
194
  .getViewerContext()
@@ -197,121 +197,6 @@ export default abstract class Entity<
197
197
  .forDelete(existingEntity, queryContext)
198
198
  .enforceDeleteAsync();
199
199
  }
200
-
201
- /**
202
- * Check whether an entity loaded by a viewer can be updated by that same viewer.
203
- *
204
- * @remarks
205
- *
206
- * This may be useful in situations relying upon the thrown privacy policy thrown authorization error
207
- * is insufficient for the task at hand. When dealing with purely a sequence of mutations it is easy
208
- * to roll back all mutations given a single authorization error by wrapping them in a single transaction.
209
- * When certain portions of a mutation cannot be rolled back transactionally (third pary calls,
210
- * legacy code, etc), using this method can help decide whether the sequence of mutations will fail before
211
- * attempting them. Note that if any privacy policy rules use a piece of data being updated in the mutations
212
- * the result of this method and the update mutation itself may differ.
213
- *
214
- * @param existingEntity - entity loaded by viewer
215
- * @param queryContext - query context in which to perform the check
216
- */
217
- static async canViewerUpdateAsync<
218
- TMFields extends object,
219
- TMID extends NonNullable<TMFields[TMSelectedFields]>,
220
- TMViewerContext extends ViewerContext,
221
- TMEntity extends Entity<TMFields, TMID, TMViewerContext, TMSelectedFields>,
222
- TMPrivacyPolicy extends EntityPrivacyPolicy<
223
- TMFields,
224
- TMID,
225
- TMViewerContext,
226
- TMEntity,
227
- TMSelectedFields
228
- >,
229
- TMSelectedFields extends keyof TMFields = keyof TMFields
230
- >(
231
- this: IEntityClass<
232
- TMFields,
233
- TMID,
234
- TMViewerContext,
235
- TMEntity,
236
- TMPrivacyPolicy,
237
- TMSelectedFields
238
- >,
239
- existingEntity: TMEntity,
240
- queryContext: EntityQueryContext = existingEntity
241
- .getViewerContext()
242
- .getViewerScopedEntityCompanionForClass(this)
243
- .getQueryContextProvider()
244
- .getQueryContext()
245
- ): Promise<boolean> {
246
- const companion = existingEntity
247
- .getViewerContext()
248
- .getViewerScopedEntityCompanionForClass(this);
249
- const privacyPolicy = companion.entityCompanion.privacyPolicy;
250
- const evaluationResult = await asyncResult(
251
- privacyPolicy.authorizeUpdateAsync(
252
- existingEntity.getViewerContext(),
253
- queryContext,
254
- { cascadingDeleteCause: null },
255
- existingEntity,
256
- companion.getMetricsAdapter()
257
- )
258
- );
259
- return evaluationResult.ok;
260
- }
261
-
262
- /**
263
- * Check whether an entity loaded by a viewer can be deleted by that same viewer.
264
- *
265
- * @remarks
266
- * See remarks for canViewerUpdate.
267
- *
268
- * @param existingEntity - entity loaded by viewer
269
- * @param queryContext - query context in which to perform the check
270
- */
271
- static async canViewerDeleteAsync<
272
- TMFields extends object,
273
- TMID extends NonNullable<TMFields[TMSelectedFields]>,
274
- TMViewerContext extends ViewerContext,
275
- TMEntity extends Entity<TMFields, TMID, TMViewerContext, TMSelectedFields>,
276
- TMPrivacyPolicy extends EntityPrivacyPolicy<
277
- TMFields,
278
- TMID,
279
- TMViewerContext,
280
- TMEntity,
281
- TMSelectedFields
282
- >,
283
- TMSelectedFields extends keyof TMFields = keyof TMFields
284
- >(
285
- this: IEntityClass<
286
- TMFields,
287
- TMID,
288
- TMViewerContext,
289
- TMEntity,
290
- TMPrivacyPolicy,
291
- TMSelectedFields
292
- >,
293
- existingEntity: TMEntity,
294
- queryContext: EntityQueryContext = existingEntity
295
- .getViewerContext()
296
- .getViewerScopedEntityCompanionForClass(this)
297
- .getQueryContextProvider()
298
- .getQueryContext()
299
- ): Promise<boolean> {
300
- const companion = existingEntity
301
- .getViewerContext()
302
- .getViewerScopedEntityCompanionForClass(this);
303
- const privacyPolicy = companion.entityCompanion.privacyPolicy;
304
- const evaluationResult = await asyncResult(
305
- privacyPolicy.authorizeDeleteAsync(
306
- existingEntity.getViewerContext(),
307
- queryContext,
308
- { cascadingDeleteCause: null },
309
- existingEntity,
310
- companion.getMetricsAdapter()
311
- )
312
- );
313
- return evaluationResult.ok;
314
- }
315
200
  }
316
201
 
317
202
  /**
@@ -329,7 +214,7 @@ export interface IEntityClass<
329
214
  TEntity,
330
215
  TSelectedFields
331
216
  >,
332
- TSelectedFields extends keyof TFields = keyof TFields
217
+ TSelectedFields extends keyof TFields = keyof TFields,
333
218
  > {
334
219
  new (constructorParam: {
335
220
  viewerContext: TViewerContext;
@@ -16,7 +16,7 @@ export default class EntityAssociationLoader<
16
16
  TID extends NonNullable<TFields[TSelectedFields]>,
17
17
  TViewerContext extends ViewerContext,
18
18
  TEntity extends ReadonlyEntity<TFields, TID, TViewerContext, TSelectedFields>,
19
- TSelectedFields extends keyof TFields
19
+ TSelectedFields extends keyof TFields,
20
20
  > {
21
21
  constructor(private readonly entity: TEntity) {}
22
22
 
@@ -44,7 +44,7 @@ export default class EntityAssociationLoader<
44
44
  TAssociatedEntity,
45
45
  TAssociatedSelectedFields
46
46
  >,
47
- TAssociatedSelectedFields extends keyof TAssociatedFields = keyof TAssociatedFields
47
+ TAssociatedSelectedFields extends keyof TAssociatedFields = keyof TAssociatedFields,
48
48
  >(
49
49
  fieldIdentifyingAssociatedEntity: TIdentifyingField,
50
50
  associatedEntityClass: IEntityClass<
@@ -59,7 +59,7 @@ export default class EntityAssociationLoader<
59
59
  .getViewerContext()
60
60
  .getViewerScopedEntityCompanionForClass(associatedEntityClass)
61
61
  .getQueryContextProvider()
62
- .getQueryContext()
62
+ .getQueryContext(),
63
63
  ): Promise<
64
64
  Result<null extends TFields[TIdentifyingField] ? TAssociatedEntity | null : TAssociatedEntity>
65
65
  > {
@@ -74,9 +74,11 @@ export default class EntityAssociationLoader<
74
74
  .getViewerContext()
75
75
  .getViewerScopedEntityCompanionForClass(associatedEntityClass)
76
76
  .getLoaderFactory()
77
- .forLoad(queryContext, { cascadingDeleteCause: null });
77
+ .forLoad(queryContext, { previousValue: null, cascadingDeleteCause: null });
78
78
 
79
- return (await loader.loadByIDAsync(associatedEntityID as unknown as TAssociatedID)) as Result<
79
+ return (await loader
80
+ .withAuthorizationResults()
81
+ .loadByIDAsync(associatedEntityID as unknown as TAssociatedID)) as Result<
80
82
  null extends TFields[TIdentifyingField] ? TAssociatedEntity | null : TAssociatedEntity
81
83
  >;
82
84
  }
@@ -106,7 +108,7 @@ export default class EntityAssociationLoader<
106
108
  TAssociatedEntity,
107
109
  TAssociatedSelectedFields
108
110
  >,
109
- TAssociatedSelectedFields extends keyof TAssociatedFields = keyof TAssociatedFields
111
+ TAssociatedSelectedFields extends keyof TAssociatedFields = keyof TAssociatedFields,
110
112
  >(
111
113
  associatedEntityClass: IEntityClass<
112
114
  TAssociatedFields,
@@ -121,18 +123,17 @@ export default class EntityAssociationLoader<
121
123
  .getViewerContext()
122
124
  .getViewerScopedEntityCompanionForClass(associatedEntityClass)
123
125
  .getQueryContextProvider()
124
- .getQueryContext()
126
+ .getQueryContext(),
125
127
  ): Promise<readonly Result<TAssociatedEntity>[]> {
126
128
  const thisID = this.entity.getID();
127
129
  const loader = this.entity
128
130
  .getViewerContext()
129
131
  .getViewerScopedEntityCompanionForClass(associatedEntityClass)
130
132
  .getLoaderFactory()
131
- .forLoad(queryContext, { cascadingDeleteCause: null });
132
- return await loader.loadManyByFieldEqualingAsync(
133
- associatedEntityFieldContainingThisID,
134
- thisID as any
135
- );
133
+ .forLoad(queryContext, { previousValue: null, cascadingDeleteCause: null });
134
+ return await loader
135
+ .withAuthorizationResults()
136
+ .loadManyByFieldEqualingAsync(associatedEntityFieldContainingThisID, thisID as any);
136
137
  }
137
138
 
138
139
  /**
@@ -159,7 +160,7 @@ export default class EntityAssociationLoader<
159
160
  TAssociatedEntity,
160
161
  TAssociatedSelectedFields
161
162
  >,
162
- TAssociatedSelectedFields extends keyof TAssociatedFields = keyof TAssociatedFields
163
+ TAssociatedSelectedFields extends keyof TAssociatedFields = keyof TAssociatedFields,
163
164
  >(
164
165
  fieldIdentifyingAssociatedEntity: keyof Pick<TFields, TSelectedFields>,
165
166
  associatedEntityClass: IEntityClass<
@@ -175,7 +176,7 @@ export default class EntityAssociationLoader<
175
176
  .getViewerContext()
176
177
  .getViewerScopedEntityCompanionForClass(associatedEntityClass)
177
178
  .getQueryContextProvider()
178
- .getQueryContext()
179
+ .getQueryContext(),
179
180
  ): Promise<Result<TAssociatedEntity> | null> {
180
181
  const associatedFieldValue = this.entity.getField(fieldIdentifyingAssociatedEntity);
181
182
  if (!associatedFieldValue) {
@@ -185,11 +186,10 @@ export default class EntityAssociationLoader<
185
186
  .getViewerContext()
186
187
  .getViewerScopedEntityCompanionForClass(associatedEntityClass)
187
188
  .getLoaderFactory()
188
- .forLoad(queryContext, { cascadingDeleteCause: null });
189
- return await loader.loadByFieldEqualingAsync(
190
- associatedEntityLookupByField,
191
- associatedFieldValue as any
192
- );
189
+ .forLoad(queryContext, { previousValue: null, cascadingDeleteCause: null });
190
+ return await loader
191
+ .withAuthorizationResults()
192
+ .loadByFieldEqualingAsync(associatedEntityLookupByField, associatedFieldValue as any);
193
193
  }
194
194
 
195
195
  /**
@@ -216,7 +216,7 @@ export default class EntityAssociationLoader<
216
216
  TAssociatedEntity,
217
217
  TAssociatedSelectedFields
218
218
  >,
219
- TAssociatedSelectedFields extends keyof TAssociatedFields = keyof TAssociatedFields
219
+ TAssociatedSelectedFields extends keyof TAssociatedFields = keyof TAssociatedFields,
220
220
  >(
221
221
  fieldIdentifyingAssociatedEntity: keyof Pick<TFields, TSelectedFields>,
222
222
  associatedEntityClass: IEntityClass<
@@ -232,7 +232,7 @@ export default class EntityAssociationLoader<
232
232
  .getViewerContext()
233
233
  .getViewerScopedEntityCompanionForClass(associatedEntityClass)
234
234
  .getQueryContextProvider()
235
- .getQueryContext()
235
+ .getQueryContext(),
236
236
  ): Promise<readonly Result<TAssociatedEntity>[]> {
237
237
  const associatedFieldValue = this.entity.getField(fieldIdentifyingAssociatedEntity);
238
238
  if (!associatedFieldValue) {
@@ -243,11 +243,10 @@ export default class EntityAssociationLoader<
243
243
  .getViewerContext()
244
244
  .getViewerScopedEntityCompanionForClass(associatedEntityClass)
245
245
  .getLoaderFactory()
246
- .forLoad(queryContext, { cascadingDeleteCause: null });
247
- return await loader.loadManyByFieldEqualingAsync(
248
- associatedEntityLookupByField,
249
- associatedFieldValue as any
250
- );
246
+ .forLoad(queryContext, { previousValue: null, cascadingDeleteCause: null });
247
+ return await loader
248
+ .withAuthorizationResults()
249
+ .loadManyByFieldEqualingAsync(associatedEntityLookupByField, associatedFieldValue as any);
251
250
  }
252
251
 
253
252
  /**
@@ -267,7 +266,7 @@ export default class EntityAssociationLoader<
267
266
  TEntity2,
268
267
  TSelectedFields2
269
268
  >,
270
- TSelectedFields2 extends keyof TFields2 = keyof TFields2
269
+ TSelectedFields2 extends keyof TFields2 = keyof TFields2,
271
270
  >(
272
271
  loadDirectives: [
273
272
  EntityLoadThroughDirective<
@@ -279,9 +278,9 @@ export default class EntityAssociationLoader<
279
278
  TPrivacyPolicy2,
280
279
  TSelectedFields,
281
280
  TSelectedFields2
282
- >
281
+ >,
283
282
  ],
284
- queryContext?: EntityQueryContext
283
+ queryContext?: EntityQueryContext,
285
284
  ): Promise<Result<TEntity2> | null>;
286
285
 
287
286
  /**
@@ -312,7 +311,7 @@ export default class EntityAssociationLoader<
312
311
  TSelectedFields3
313
312
  >,
314
313
  TSelectedFields2 extends keyof TFields2 = keyof TFields2,
315
- TSelectedFields3 extends keyof TFields3 = keyof TFields3
314
+ TSelectedFields3 extends keyof TFields3 = keyof TFields3,
316
315
  >(
317
316
  loadDirectives: [
318
317
  EntityLoadThroughDirective<
@@ -334,9 +333,9 @@ export default class EntityAssociationLoader<
334
333
  TPrivacyPolicy3,
335
334
  TSelectedFields2,
336
335
  TSelectedFields3
337
- >
336
+ >,
338
337
  ],
339
- queryContext?: EntityQueryContext
338
+ queryContext?: EntityQueryContext,
340
339
  ): Promise<Result<TEntity3> | null>;
341
340
 
342
341
  /**
@@ -378,9 +377,9 @@ export default class EntityAssociationLoader<
378
377
  >,
379
378
  TSelectedFields2 extends keyof TFields2 = keyof TFields2,
380
379
  TSelectedFields3 extends keyof TFields3 = keyof TFields3,
381
- TSelectedFields4 extends keyof TFields4 = keyof TFields4
380
+ TSelectedFields4 extends keyof TFields4 = keyof TFields4,
382
381
  >(
383
- loadDirective: [
382
+ loadDirectives: [
384
383
  EntityLoadThroughDirective<
385
384
  TViewerContext,
386
385
  TFields,
@@ -410,9 +409,9 @@ export default class EntityAssociationLoader<
410
409
  TPrivacyPolicy4,
411
410
  TSelectedFields3,
412
411
  TSelectedFields4
413
- >
412
+ >,
414
413
  ],
415
- queryContext?: EntityQueryContext
414
+ queryContext?: EntityQueryContext,
416
415
  ): Promise<Result<TEntity4> | null>;
417
416
 
418
417
  /**
@@ -423,12 +422,12 @@ export default class EntityAssociationLoader<
423
422
  */
424
423
  async loadAssociatedEntityThroughAsync(
425
424
  loadDirectives: EntityLoadThroughDirective<TViewerContext, any, any, any, any, any, any, any>[],
426
- queryContext?: EntityQueryContext
425
+ queryContext?: EntityQueryContext,
427
426
  ): Promise<Result<ReadonlyEntity<any, any, any, any>> | null>;
428
427
 
429
428
  async loadAssociatedEntityThroughAsync(
430
429
  loadDirectives: EntityLoadThroughDirective<TViewerContext, any, any, any, any, any, any, any>[],
431
- queryContext?: EntityQueryContext
430
+ queryContext?: EntityQueryContext,
432
431
  ): Promise<Result<ReadonlyEntity<any, any, any, any>> | null> {
433
432
  let currentEntity: ReadonlyEntity<any, any, any, any> = this.entity;
434
433
  for (const loadDirective of loadDirectives) {
@@ -445,7 +444,7 @@ export default class EntityAssociationLoader<
445
444
  fieldIdentifyingAssociatedEntity,
446
445
  associatedEntityClass,
447
446
  associatedEntityLookupByField,
448
- queryContext
447
+ queryContext,
449
448
  );
450
449
  } else {
451
450
  const associatedEntityResultLocal = await currentEntity
@@ -453,7 +452,7 @@ export default class EntityAssociationLoader<
453
452
  .loadAssociatedEntityAsync(
454
453
  fieldIdentifyingAssociatedEntity,
455
454
  associatedEntityClass,
456
- queryContext
455
+ queryContext,
457
456
  );
458
457
 
459
458
  if (associatedEntityResultLocal.ok && associatedEntityResultLocal.value === null) {
@@ -498,7 +497,7 @@ export interface EntityLoadThroughDirective<
498
497
  TAssociatedSelectedFields
499
498
  >,
500
499
  TSelectedFields extends keyof TFields = keyof TFields,
501
- TAssociatedSelectedFields extends keyof TAssociatedFields = keyof TAssociatedFields
500
+ TAssociatedSelectedFields extends keyof TAssociatedFields = keyof TAssociatedFields,
502
501
  > {
503
502
  /**
504
503
  * Class of entity to load at this step.
@@ -7,6 +7,7 @@ import ReadonlyEntity from './ReadonlyEntity';
7
7
  import ViewerContext from './ViewerContext';
8
8
  import EntityTableDataCoordinator from './internal/EntityTableDataCoordinator';
9
9
  import IEntityMetricsAdapter from './metrics/IEntityMetricsAdapter';
10
+ import { mergeEntityMutationTriggerConfigurations } from './utils/mergeEntityMutationTriggerConfigurations';
10
11
 
11
12
  export interface IPrivacyPolicyClass<TPrivacyPolicy> {
12
13
  new (): TPrivacyPolicy;
@@ -27,7 +28,7 @@ export default class EntityCompanion<
27
28
  TEntity,
28
29
  TSelectedFields
29
30
  >,
30
- TSelectedFields extends keyof TFields
31
+ TSelectedFields extends keyof TFields,
31
32
  > {
32
33
  public readonly privacyPolicy: TPrivacyPolicy;
33
34
 
@@ -59,7 +60,7 @@ export default class EntityCompanion<
59
60
  TSelectedFields
60
61
  >,
61
62
  private readonly tableDataCoordinator: EntityTableDataCoordinator<TFields>,
62
- private readonly metricsAdapter: IEntityMetricsAdapter
63
+ private readonly metricsAdapter: IEntityMetricsAdapter,
63
64
  ) {
64
65
  this.privacyPolicy = new entityCompanionDefinition.privacyPolicyClass();
65
66
  this.entityLoaderFactory = new EntityLoaderFactory<
@@ -76,10 +77,13 @@ export default class EntityCompanion<
76
77
  entityCompanionDefinition.entityClass,
77
78
  this.privacyPolicy,
78
79
  entityCompanionDefinition.mutationValidators ?? [],
79
- entityCompanionDefinition.mutationTriggers ?? {},
80
+ mergeEntityMutationTriggerConfigurations(
81
+ entityCompanionDefinition.mutationTriggers ?? {},
82
+ entityCompanionProvider.globalMutationTriggers ?? {},
83
+ ),
80
84
  this.entityLoaderFactory,
81
85
  tableDataCoordinator.databaseAdapter,
82
- metricsAdapter
86
+ metricsAdapter,
83
87
  );
84
88
  }
85
89
 
@@ -61,7 +61,7 @@ export interface EntityCompanionDefinition<
61
61
  TEntity,
62
62
  TSelectedFields
63
63
  >,
64
- TSelectedFields extends keyof TFields = keyof TFields
64
+ TSelectedFields extends keyof TFields = keyof TFields,
65
65
  > {
66
66
  /**
67
67
  * The concrete Entity class for which this is the definition.
@@ -139,6 +139,7 @@ export default class EntityCompanionProvider {
139
139
  * @param metricsAdapter - An IEntityMetricsAdapter for collecting metrics on this instance
140
140
  * @param databaseAdapterFlavors - Database adapter configurations for this instance
141
141
  * @param cacheAdapterFlavors - Cache adapter configurations for this instance
142
+ * @param globalMutationTriggers - Optional set of EntityMutationTrigger to run for all entity mutations systemwide.
142
143
  */
143
144
  constructor(
144
145
  public readonly metricsAdapter: IEntityMetricsAdapter,
@@ -146,7 +147,14 @@ export default class EntityCompanionProvider {
146
147
  DatabaseAdapterFlavor,
147
148
  DatabaseAdapterFlavorDefinition
148
149
  >,
149
- private cacheAdapterFlavors: ReadonlyMap<CacheAdapterFlavor, CacheAdapterFlavorDefinition>
150
+ private cacheAdapterFlavors: ReadonlyMap<CacheAdapterFlavor, CacheAdapterFlavorDefinition>,
151
+ readonly globalMutationTriggers: EntityMutationTriggerConfiguration<
152
+ any,
153
+ any,
154
+ any,
155
+ any,
156
+ any
157
+ > = {},
150
158
  ) {}
151
159
 
152
160
  /**
@@ -167,7 +175,7 @@ export default class EntityCompanionProvider {
167
175
  TEntity,
168
176
  TSelectedFields
169
177
  >,
170
- TSelectedFields extends keyof TFields
178
+ TSelectedFields extends keyof TFields,
171
179
  >(
172
180
  entityClass: IEntityClass<
173
181
  TFields,
@@ -176,58 +184,58 @@ export default class EntityCompanionProvider {
176
184
  TEntity,
177
185
  TPrivacyPolicy,
178
186
  TSelectedFields
179
- >
187
+ >,
180
188
  ): EntityCompanion<TFields, TID, TViewerContext, TEntity, TPrivacyPolicy, TSelectedFields> {
181
189
  const entityCompanionDefinition = computeIfAbsent(
182
190
  this.companionDefinitionMap,
183
191
  entityClass.name,
184
- () => entityClass.defineCompanionDefinition()
192
+ () => entityClass.defineCompanionDefinition(),
185
193
  );
186
194
  const tableDataCoordinator = this.getTableDataCoordinatorForEntity(
187
195
  entityCompanionDefinition.entityConfiguration,
188
- entityClass.name
196
+ entityClass.name,
189
197
  );
190
198
  return computeIfAbsent(this.companionMap, entityClass.name, () => {
191
199
  return new EntityCompanion(
192
200
  this,
193
201
  entityCompanionDefinition,
194
202
  tableDataCoordinator,
195
- this.metricsAdapter
203
+ this.metricsAdapter,
196
204
  );
197
205
  });
198
206
  }
199
207
 
200
208
  getQueryContextProviderForDatabaseAdaptorFlavor(
201
- databaseAdapterFlavor: DatabaseAdapterFlavor
209
+ databaseAdapterFlavor: DatabaseAdapterFlavor,
202
210
  ): EntityQueryContextProvider {
203
211
  const entityDatabaseAdapterFlavor = this.databaseAdapterFlavors.get(databaseAdapterFlavor);
204
212
  invariant(
205
213
  entityDatabaseAdapterFlavor,
206
- `No database adaptor configuration found for flavor: ${databaseAdapterFlavor}`
214
+ `No database adaptor configuration found for flavor: ${databaseAdapterFlavor}`,
207
215
  );
208
216
 
209
217
  return entityDatabaseAdapterFlavor.queryContextProvider;
210
218
  }
211
219
 
212
- private getTableDataCoordinatorForEntity<TFields>(
220
+ private getTableDataCoordinatorForEntity<TFields extends Record<string, any>>(
213
221
  entityConfiguration: EntityConfiguration<TFields>,
214
- entityClassName: string
222
+ entityClassName: string,
215
223
  ): EntityTableDataCoordinator<TFields> {
216
224
  return computeIfAbsent(this.tableDataCoordinatorMap, entityConfiguration.tableName, () => {
217
225
  const entityDatabaseAdapterFlavor = this.databaseAdapterFlavors.get(
218
- entityConfiguration.databaseAdapterFlavor
226
+ entityConfiguration.databaseAdapterFlavor,
219
227
  );
220
228
  invariant(
221
229
  entityDatabaseAdapterFlavor,
222
- `No database adaptor configuration found for flavor: ${entityConfiguration.databaseAdapterFlavor}`
230
+ `No database adaptor configuration found for flavor: ${entityConfiguration.databaseAdapterFlavor}`,
223
231
  );
224
232
 
225
233
  const entityCacheAdapterFlavor = this.cacheAdapterFlavors.get(
226
- entityConfiguration.cacheAdapterFlavor
234
+ entityConfiguration.cacheAdapterFlavor,
227
235
  );
228
236
  invariant(
229
237
  entityCacheAdapterFlavor,
230
- `No cache adaptor configuration found for flavor: ${entityConfiguration.cacheAdapterFlavor}`
238
+ `No cache adaptor configuration found for flavor: ${entityConfiguration.cacheAdapterFlavor}`,
231
239
  );
232
240
 
233
241
  return new EntityTableDataCoordinator(
@@ -236,7 +244,7 @@ export default class EntityCompanionProvider {
236
244
  entityCacheAdapterFlavor.cacheAdapterProvider,
237
245
  entityDatabaseAdapterFlavor.queryContextProvider,
238
246
  this.metricsAdapter,
239
- entityClassName
247
+ entityClassName,
240
248
  );
241
249
  });
242
250
  }
@@ -7,7 +7,7 @@ import { mapMap, invertMap, reduceMap } from './utils/collections/maps';
7
7
  * The data storage configuration for a type of Entity. Contains information relating to IDs,
8
8
  * cachable fields, field mappings, and types of cache and database adapter.
9
9
  */
10
- export default class EntityConfiguration<TFields> {
10
+ export default class EntityConfiguration<TFields extends Record<string, any>> {
11
11
  readonly idField: keyof TFields;
12
12
  readonly tableName: string;
13
13
  readonly cacheableKeys: ReadonlySet<keyof TFields>;
@@ -75,18 +75,29 @@ export default class EntityConfiguration<TFields> {
75
75
 
76
76
  // external schema is a Record to typecheck that all fields have FieldDefinitions,
77
77
  // but internally the most useful representation is a map for lookups
78
- // TODO(wschurman): validate schema
79
- this.schema = new Map(Object.entries(schema) as any);
78
+ EntityConfiguration.validateSchema(schema);
79
+ this.schema = new Map(Object.entries(schema));
80
80
 
81
81
  this.cacheableKeys = EntityConfiguration.computeCacheableKeys(this.schema);
82
82
  this.entityToDBFieldsKeyMapping = EntityConfiguration.computeEntityToDBFieldsKeyMapping(
83
- this.schema
83
+ this.schema,
84
84
  );
85
85
  this.dbToEntityFieldsKeyMapping = invertMap(this.entityToDBFieldsKeyMapping);
86
86
  }
87
87
 
88
+ private static validateSchema<TFields extends Record<string, any>>(schema: TFields): void {
89
+ const disallowedFieldsKeys = Object.getOwnPropertyNames(Object.prototype);
90
+ for (const disallowedFieldsKey of disallowedFieldsKeys) {
91
+ if (Object.hasOwn(schema, disallowedFieldsKey)) {
92
+ throw new Error(
93
+ `Entity field name not allowed to prevent conflicts with standard Object prototype fields: ${disallowedFieldsKey}`,
94
+ );
95
+ }
96
+ }
97
+ }
98
+
88
99
  private static computeCacheableKeys<TFields>(
89
- schema: ReadonlyMap<keyof TFields, EntityFieldDefinition<any>>
100
+ schema: ReadonlyMap<keyof TFields, EntityFieldDefinition<any>>,
90
101
  ): ReadonlySet<keyof TFields> {
91
102
  return reduceMap(
92
103
  schema,
@@ -96,12 +107,12 @@ export default class EntityConfiguration<TFields> {
96
107
  }
97
108
  return acc;
98
109
  },
99
- new Set<keyof TFields>()
110
+ new Set<keyof TFields>(),
100
111
  );
101
112
  }
102
113
 
103
114
  private static computeEntityToDBFieldsKeyMapping<TFields>(
104
- schema: ReadonlyMap<keyof TFields, EntityFieldDefinition<any>>
115
+ schema: ReadonlyMap<keyof TFields, EntityFieldDefinition<any>>,
105
116
  ): ReadonlyMap<keyof TFields, string> {
106
117
  return mapMap(schema, (v) => v.columnName);
107
118
  }