@memberjunction/server 6.1.0-edge.5 → 6.1.0-edge.7

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 (250) hide show
  1. package/README.md +30 -11
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/agentSessions/SessionJanitor.js +2 -2
  4. package/dist/agentSessions/SessionJanitor.js.map +1 -1
  5. package/dist/auth/exampleNewUserSubClass.d.ts.map +1 -1
  6. package/dist/auth/exampleNewUserSubClass.js +9 -4
  7. package/dist/auth/exampleNewUserSubClass.js.map +1 -1
  8. package/dist/auth/magicLink/MagicLinkService.d.ts.map +1 -1
  9. package/dist/auth/magicLink/MagicLinkService.js +2 -8
  10. package/dist/auth/magicLink/MagicLinkService.js.map +1 -1
  11. package/dist/auth/newUsers.d.ts.map +1 -1
  12. package/dist/auth/newUsers.js +4 -12
  13. package/dist/auth/newUsers.js.map +1 -1
  14. package/dist/auth/principals.d.ts +92 -0
  15. package/dist/auth/principals.d.ts.map +1 -0
  16. package/dist/auth/principals.js +193 -0
  17. package/dist/auth/principals.js.map +1 -0
  18. package/dist/config.d.ts +106 -12
  19. package/dist/config.d.ts.map +1 -1
  20. package/dist/config.js +41 -4
  21. package/dist/config.js.map +1 -1
  22. package/dist/entitySubclasses/MJEntityPermissionEntityServer.server.d.ts +30 -0
  23. package/dist/entitySubclasses/MJEntityPermissionEntityServer.server.d.ts.map +1 -1
  24. package/dist/entitySubclasses/MJEntityPermissionEntityServer.server.js +58 -5
  25. package/dist/entitySubclasses/MJEntityPermissionEntityServer.server.js.map +1 -1
  26. package/dist/generated/graphql-schemas/__mj.d.ts +7607 -6853
  27. package/dist/generated/graphql-schemas/__mj.d.ts.map +1 -1
  28. package/dist/generated/graphql-schemas/__mj.js +25141 -21645
  29. package/dist/generated/graphql-schemas/__mj.js.map +1 -1
  30. package/dist/generic/ResolverBase.d.ts +152 -2
  31. package/dist/generic/ResolverBase.d.ts.map +1 -1
  32. package/dist/generic/ResolverBase.js +389 -10
  33. package/dist/generic/ResolverBase.js.map +1 -1
  34. package/dist/generic/RunViewResolver.d.ts.map +1 -1
  35. package/dist/generic/RunViewResolver.js +26 -2
  36. package/dist/generic/RunViewResolver.js.map +1 -1
  37. package/dist/generic/refusalExtensions.d.ts +52 -0
  38. package/dist/generic/refusalExtensions.d.ts.map +1 -0
  39. package/dist/generic/refusalExtensions.js +16 -0
  40. package/dist/generic/refusalExtensions.js.map +1 -0
  41. package/dist/index.d.ts +11 -3
  42. package/dist/index.d.ts.map +1 -1
  43. package/dist/index.js +131 -93
  44. package/dist/index.js.map +1 -1
  45. package/dist/realtimeWidget/WidgetSessionService.d.ts.map +1 -1
  46. package/dist/realtimeWidget/WidgetSessionService.js +2 -12
  47. package/dist/realtimeWidget/WidgetSessionService.js.map +1 -1
  48. package/dist/realtimeWidget/visitorIdentity.d.ts.map +1 -1
  49. package/dist/realtimeWidget/visitorIdentity.js +20 -5
  50. package/dist/realtimeWidget/visitorIdentity.js.map +1 -1
  51. package/dist/resolvers/EntityRecordNameResolver.d.ts +34 -5
  52. package/dist/resolvers/EntityRecordNameResolver.d.ts.map +1 -1
  53. package/dist/resolvers/EntityRecordNameResolver.js +64 -7
  54. package/dist/resolvers/EntityRecordNameResolver.js.map +1 -1
  55. package/dist/resolvers/FileCategoryResolver.d.ts.map +1 -1
  56. package/dist/resolvers/FileCategoryResolver.js +1 -2
  57. package/dist/resolvers/FileCategoryResolver.js.map +1 -1
  58. package/dist/resolvers/IntegrationDiscoveryResolver.d.ts.map +1 -1
  59. package/dist/resolvers/IntegrationDiscoveryResolver.js +2 -0
  60. package/dist/resolvers/IntegrationDiscoveryResolver.js.map +1 -1
  61. package/dist/resolvers/QuerySystemUserResolver.js +1 -1
  62. package/dist/resolvers/QuerySystemUserResolver.js.map +1 -1
  63. package/dist/resolvers/RealtimeClientSessionResolver.d.ts +10 -2
  64. package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -1
  65. package/dist/resolvers/RealtimeClientSessionResolver.js +80 -4
  66. package/dist/resolvers/RealtimeClientSessionResolver.js.map +1 -1
  67. package/dist/resolvers/RemoteBrowserActionResolver.d.ts +13 -8
  68. package/dist/resolvers/RemoteBrowserActionResolver.d.ts.map +1 -1
  69. package/dist/resolvers/RemoteBrowserActionResolver.js +45 -8
  70. package/dist/resolvers/RemoteBrowserActionResolver.js.map +1 -1
  71. package/dist/resolvers/SyncRolesUsersResolver.d.ts.map +1 -1
  72. package/dist/resolvers/SyncRolesUsersResolver.js +11 -2
  73. package/dist/resolvers/SyncRolesUsersResolver.js.map +1 -1
  74. package/dist/resolvers/UserViewResolver.d.ts.map +1 -1
  75. package/dist/resolvers/UserViewResolver.js +8 -4
  76. package/dist/resolvers/UserViewResolver.js.map +1 -1
  77. package/dist/rest/EntityCRUDHandler.d.ts +2 -1
  78. package/dist/rest/EntityCRUDHandler.d.ts.map +1 -1
  79. package/dist/rest/EntityCRUDHandler.js +3 -18
  80. package/dist/rest/EntityCRUDHandler.js.map +1 -1
  81. package/dist/rest/RESTEndpointHandler.d.ts +2 -1
  82. package/dist/rest/RESTEndpointHandler.d.ts.map +1 -1
  83. package/dist/rest/RESTEndpointHandler.js +3 -16
  84. package/dist/rest/RESTEndpointHandler.js.map +1 -1
  85. package/dist/rest/RealtimeSdpBrokerHandler.d.ts +23 -0
  86. package/dist/rest/RealtimeSdpBrokerHandler.d.ts.map +1 -0
  87. package/dist/rest/RealtimeSdpBrokerHandler.js +78 -0
  88. package/dist/rest/RealtimeSdpBrokerHandler.js.map +1 -0
  89. package/dist/serverExtensionReservedRoots.d.ts.map +1 -1
  90. package/dist/serverExtensionReservedRoots.js +2 -3
  91. package/dist/serverExtensionReservedRoots.js.map +1 -1
  92. package/package.json +92 -91
  93. package/src/__tests__/EntityCRUDHandler.test.ts +18 -5
  94. package/src/__tests__/EntityRecordNameResolver.fieldSecurity.test.ts +118 -0
  95. package/src/__tests__/RealtimeClientSessionResolver.test.ts +20 -0
  96. package/src/__tests__/RealtimeSdpBrokerHandler.test.ts +191 -0
  97. package/src/__tests__/RemoteBrowserAudioStream.test.ts +81 -2
  98. package/src/__tests__/ResolverBase.filterEscaping.test.ts +190 -3
  99. package/src/__tests__/ResolverBase.readableFieldsTransport.test.ts +106 -0
  100. package/src/__tests__/UserViewResolver.updateWhereClause.test.ts +114 -0
  101. package/src/__tests__/WidgetSessionService.test.ts +4 -1
  102. package/src/__tests__/generatedGraphQLSchema.structural.test.ts +46 -0
  103. package/src/__tests__/newUserRolesDefault.test.ts +81 -0
  104. package/src/__tests__/newUsers.test.ts +36 -14
  105. package/src/__tests__/principals.callSites.test.ts +112 -0
  106. package/src/__tests__/principals.logging.test.ts +160 -0
  107. package/src/__tests__/principals.test.ts +305 -0
  108. package/src/__tests__/resolverBase.fls.test.ts +483 -0
  109. package/src/__tests__/resolverBase.recordChangesFls.test.ts +358 -0
  110. package/src/__tests__/resolverBase.refusalExtensions.test.ts +101 -0
  111. package/src/__tests__/resolverBase.refusalMessage.test.ts +88 -0
  112. package/src/__tests__/serverExtensionReservedRoots.test.ts +79 -11
  113. package/src/__tests__/visitorIdentity.resolve.test.ts +61 -0
  114. package/src/agentSessions/SessionJanitor.ts +2 -2
  115. package/src/auth/exampleNewUserSubClass.ts +9 -6
  116. package/src/auth/magicLink/MagicLinkService.ts +2 -8
  117. package/src/auth/newUsers.ts +7 -15
  118. package/src/auth/principals.ts +257 -0
  119. package/src/config.ts +44 -4
  120. package/src/entitySubclasses/MJEntityPermissionEntityServer.server.ts +65 -6
  121. package/src/generated/graphql-schemas/__mj.ts +18966 -16452
  122. package/src/generic/ResolverBase.ts +467 -9
  123. package/src/generic/RunViewResolver.ts +28 -2
  124. package/src/generic/refusalExtensions.ts +59 -0
  125. package/src/index.ts +154 -102
  126. package/src/realtimeWidget/WidgetSessionService.ts +2 -14
  127. package/src/realtimeWidget/visitorIdentity.ts +21 -6
  128. package/src/resolvers/EntityRecordNameResolver.ts +74 -8
  129. package/src/resolvers/FileCategoryResolver.ts +2 -3
  130. package/src/resolvers/IntegrationDiscoveryResolver.ts +2 -0
  131. package/src/resolvers/QuerySystemUserResolver.ts +1 -1
  132. package/src/resolvers/RealtimeClientSessionResolver.ts +128 -2
  133. package/src/resolvers/RemoteBrowserActionResolver.ts +47 -8
  134. package/src/resolvers/SyncRolesUsersResolver.ts +11 -2
  135. package/src/resolvers/UserViewResolver.ts +8 -4
  136. package/src/rest/EntityCRUDHandler.ts +3 -19
  137. package/src/rest/RESTEndpointHandler.ts +3 -17
  138. package/src/rest/RealtimeSdpBrokerHandler.ts +101 -0
  139. package/src/serverExtensionReservedRoots.ts +2 -3
  140. package/dist/resolvers/RingCentralTelephonyResolver.d.ts +0 -27
  141. package/dist/resolvers/RingCentralTelephonyResolver.d.ts.map +0 -1
  142. package/dist/resolvers/RingCentralTelephonyResolver.js +0 -87
  143. package/dist/resolvers/RingCentralTelephonyResolver.js.map +0 -1
  144. package/dist/resolvers/TeamsMeetingsResolver.d.ts +0 -28
  145. package/dist/resolvers/TeamsMeetingsResolver.d.ts.map +0 -1
  146. package/dist/resolvers/TeamsMeetingsResolver.js +0 -91
  147. package/dist/resolvers/TeamsMeetingsResolver.js.map +0 -1
  148. package/dist/resolvers/TelephonyResolver.d.ts +0 -26
  149. package/dist/resolvers/TelephonyResolver.d.ts.map +0 -1
  150. package/dist/resolvers/TelephonyResolver.js +0 -86
  151. package/dist/resolvers/TelephonyResolver.js.map +0 -1
  152. package/dist/resolvers/VonageTelephonyResolver.d.ts +0 -26
  153. package/dist/resolvers/VonageTelephonyResolver.d.ts.map +0 -1
  154. package/dist/resolvers/VonageTelephonyResolver.js +0 -86
  155. package/dist/resolvers/VonageTelephonyResolver.js.map +0 -1
  156. package/dist/telephony/RingCentralTelephonyService.d.ts +0 -115
  157. package/dist/telephony/RingCentralTelephonyService.d.ts.map +0 -1
  158. package/dist/telephony/RingCentralTelephonyService.js +0 -262
  159. package/dist/telephony/RingCentralTelephonyService.js.map +0 -1
  160. package/dist/telephony/TeamsMeetingsRouter.d.ts +0 -40
  161. package/dist/telephony/TeamsMeetingsRouter.d.ts.map +0 -1
  162. package/dist/telephony/TeamsMeetingsRouter.js +0 -96
  163. package/dist/telephony/TeamsMeetingsRouter.js.map +0 -1
  164. package/dist/telephony/TeamsMeetingsService.d.ts +0 -113
  165. package/dist/telephony/TeamsMeetingsService.d.ts.map +0 -1
  166. package/dist/telephony/TeamsMeetingsService.js +0 -196
  167. package/dist/telephony/TeamsMeetingsService.js.map +0 -1
  168. package/dist/telephony/TwilioTelephonyRouter.d.ts +0 -36
  169. package/dist/telephony/TwilioTelephonyRouter.d.ts.map +0 -1
  170. package/dist/telephony/TwilioTelephonyRouter.js +0 -143
  171. package/dist/telephony/TwilioTelephonyRouter.js.map +0 -1
  172. package/dist/telephony/TwilioTelephonyService.d.ts +0 -95
  173. package/dist/telephony/TwilioTelephonyService.d.ts.map +0 -1
  174. package/dist/telephony/TwilioTelephonyService.js +0 -193
  175. package/dist/telephony/TwilioTelephonyService.js.map +0 -1
  176. package/dist/telephony/VonageTelephonyRouter.d.ts +0 -41
  177. package/dist/telephony/VonageTelephonyRouter.d.ts.map +0 -1
  178. package/dist/telephony/VonageTelephonyRouter.js +0 -201
  179. package/dist/telephony/VonageTelephonyRouter.js.map +0 -1
  180. package/dist/telephony/VonageTelephonyService.d.ts +0 -90
  181. package/dist/telephony/VonageTelephonyService.d.ts.map +0 -1
  182. package/dist/telephony/VonageTelephonyService.js +0 -191
  183. package/dist/telephony/VonageTelephonyService.js.map +0 -1
  184. package/dist/telephony/calendar-scheduler.d.ts +0 -67
  185. package/dist/telephony/calendar-scheduler.d.ts.map +0 -1
  186. package/dist/telephony/calendar-scheduler.js +0 -133
  187. package/dist/telephony/calendar-scheduler.js.map +0 -1
  188. package/dist/telephony/index.d.ts +0 -29
  189. package/dist/telephony/index.d.ts.map +0 -1
  190. package/dist/telephony/index.js +0 -38
  191. package/dist/telephony/index.js.map +0 -1
  192. package/dist/telephony/media-upgrade-router.d.ts +0 -47
  193. package/dist/telephony/media-upgrade-router.d.ts.map +0 -1
  194. package/dist/telephony/media-upgrade-router.js +0 -72
  195. package/dist/telephony/media-upgrade-router.js.map +0 -1
  196. package/dist/telephony/ringcentral-runtime.d.ts +0 -14
  197. package/dist/telephony/ringcentral-runtime.d.ts.map +0 -1
  198. package/dist/telephony/ringcentral-runtime.js +0 -18
  199. package/dist/telephony/ringcentral-runtime.js.map +0 -1
  200. package/dist/telephony/teams-meetings-runtime.d.ts +0 -16
  201. package/dist/telephony/teams-meetings-runtime.d.ts.map +0 -1
  202. package/dist/telephony/teams-meetings-runtime.js +0 -20
  203. package/dist/telephony/teams-meetings-runtime.js.map +0 -1
  204. package/dist/telephony/teamsAcsMediaRegistry.d.ts +0 -69
  205. package/dist/telephony/teamsAcsMediaRegistry.d.ts.map +0 -1
  206. package/dist/telephony/teamsAcsMediaRegistry.js +0 -118
  207. package/dist/telephony/teamsAcsMediaRegistry.js.map +0 -1
  208. package/dist/telephony/telephony-runtime.d.ts +0 -14
  209. package/dist/telephony/telephony-runtime.d.ts.map +0 -1
  210. package/dist/telephony/telephony-runtime.js +0 -18
  211. package/dist/telephony/telephony-runtime.js.map +0 -1
  212. package/dist/telephony/twilioMediaRegistry.d.ts +0 -60
  213. package/dist/telephony/twilioMediaRegistry.d.ts.map +0 -1
  214. package/dist/telephony/twilioMediaRegistry.js +0 -106
  215. package/dist/telephony/twilioMediaRegistry.js.map +0 -1
  216. package/dist/telephony/vonage-runtime.d.ts +0 -14
  217. package/dist/telephony/vonage-runtime.d.ts.map +0 -1
  218. package/dist/telephony/vonage-runtime.js +0 -18
  219. package/dist/telephony/vonage-runtime.js.map +0 -1
  220. package/dist/telephony/vonageMediaRegistry.d.ts +0 -78
  221. package/dist/telephony/vonageMediaRegistry.d.ts.map +0 -1
  222. package/dist/telephony/vonageMediaRegistry.js +0 -158
  223. package/dist/telephony/vonageMediaRegistry.js.map +0 -1
  224. package/src/__tests__/media-upgrade-router.test.ts +0 -43
  225. package/src/__tests__/teams-meetings-service.test.ts +0 -65
  226. package/src/__tests__/teamsAcsMediaRegistry.test.ts +0 -82
  227. package/src/__tests__/twilio-telephony-service.test.ts +0 -23
  228. package/src/__tests__/twilioMediaRegistry.test.ts +0 -103
  229. package/src/__tests__/vonageMediaRegistry.test.ts +0 -180
  230. package/src/resolvers/RingCentralTelephonyResolver.ts +0 -64
  231. package/src/resolvers/TeamsMeetingsResolver.ts +0 -68
  232. package/src/resolvers/TelephonyResolver.ts +0 -63
  233. package/src/resolvers/VonageTelephonyResolver.ts +0 -63
  234. package/src/telephony/RingCentralTelephonyService.ts +0 -342
  235. package/src/telephony/TeamsMeetingsRouter.ts +0 -124
  236. package/src/telephony/TeamsMeetingsService.ts +0 -268
  237. package/src/telephony/TwilioTelephonyRouter.ts +0 -184
  238. package/src/telephony/TwilioTelephonyService.ts +0 -261
  239. package/src/telephony/VonageTelephonyRouter.ts +0 -239
  240. package/src/telephony/VonageTelephonyService.ts +0 -259
  241. package/src/telephony/calendar-scheduler.ts +0 -176
  242. package/src/telephony/index.ts +0 -43
  243. package/src/telephony/media-upgrade-router.ts +0 -79
  244. package/src/telephony/ringcentral-runtime.ts +0 -22
  245. package/src/telephony/teams-meetings-runtime.ts +0 -24
  246. package/src/telephony/teamsAcsMediaRegistry.ts +0 -152
  247. package/src/telephony/telephony-runtime.ts +0 -22
  248. package/src/telephony/twilioMediaRegistry.ts +0 -137
  249. package/src/telephony/vonage-runtime.ts +0 -22
  250. package/src/telephony/vonageMediaRegistry.ts +0 -200
package/src/config.ts CHANGED
@@ -25,6 +25,14 @@ const userHandlingInfoSchema = z.object({
25
25
  newUserRoles: z.array(z.string()).optional().default([]),
26
26
  updateCacheWhenNotFound: z.boolean().optional().default(false),
27
27
  updateCacheWhenNotFoundDelay: z.number().optional().default(30000),
28
+ /**
29
+ * The internal user whose context creates new user records. Matched against `User.Name` FIRST,
30
+ * then `User.Email` — so either spelling of an existing user resolves. On a stock database the
31
+ * system user is `Name='System'` / `Email='not.set@nowhere.com'`; both reach it.
32
+ *
33
+ * When unset, or when the value matches no user, resolution falls back to the system user and
34
+ * then to the lowest-ID active Owner. See `src/auth/principals.ts`.
35
+ */
28
36
  contextUserForNewUserCreation: z.string().optional().default(''),
29
37
  CreateUserApplicationRecords: z.boolean().optional().default(false),
30
38
  UserApplications: z.array(z.string()).optional().default([]),
@@ -328,7 +336,11 @@ const magicLinkSchema = z.object({
328
336
  * from attaching a privileged role (e.g. Owner) to an external magic-link user.
329
337
  */
330
338
  grantableRoleNames: z.array(z.string()).optional().default([]),
331
- /** Email of the internal user whose context provisions magic-link users (falls back to userHandling.contextUserForNewUserCreation). */
339
+ /**
340
+ * The internal user whose context provisions magic-link users, matched against `User.Name` then
341
+ * `User.Email` (falls back to `userHandling.contextUserForNewUserCreation`, then to the system
342
+ * user, then to the lowest-ID active Owner).
343
+ */
332
344
  contextUserForProvisioning: z.string().optional(),
333
345
  /**
334
346
  * Guard against bolting an external magic-link role/app onto an EXISTING account
@@ -391,7 +403,10 @@ const widgetSchema = z.object({
391
403
  rateLimitWindowMs: z.coerce.number().optional().default(60_000),
392
404
  /** Server-wide default hard ceiling (minutes) on a voice session when an instance omits one (W4). */
393
405
  voiceDefaultMaxSessionMinutes: z.coerce.number().optional().default(10),
394
- /** Email/name of the internal user whose context READS widget config at mint time (falls back to system/Owner). */
406
+ /**
407
+ * The internal user whose context READS widget config at mint time, matched against `User.Name`
408
+ * then `User.Email` (falls back to the system user, then the lowest-ID active Owner).
409
+ */
395
410
  contextUserForLookup: z.string().optional(),
396
411
  /**
397
412
  * Host-identity public keys (PEM), keyed by widget PublicKey, for the `host-identity` auth
@@ -524,11 +539,17 @@ const telephonySchema = z.object({
524
539
  teams: teamsMeetingsSchema.optional(),
525
540
  }).passthrough();
526
541
 
542
+ const realtimeSchema = z.object({
543
+ /** Master switch. When false (default), the WebRTC SDP broker router is not mounted. */
544
+ enabled: zodBooleanWithTransforms().default(false),
545
+ }).passthrough();
546
+
527
547
  const configInfoSchema = z.object({
528
548
  userHandling: userHandlingInfoSchema,
529
549
  magicLink: magicLinkSchema.optional().default({}),
530
550
  widget: widgetSchema.optional().default({}),
531
551
  telephony: telephonySchema.optional().default({}),
552
+ realtime: realtimeSchema.optional().default({}),
532
553
  databaseSettings: databaseSettingsInfoSchema,
533
554
  viewingSystem: viewingSystemInfoSchema.optional(),
534
555
  restApiOptions: restApiOptionsSchema.optional().default({}),
@@ -583,6 +604,7 @@ export type UserHandlingInfo = z.infer<typeof userHandlingInfoSchema>;
583
604
  export type MagicLinkConfig = z.infer<typeof magicLinkSchema>;
584
605
  export type WidgetConfig = z.infer<typeof widgetSchema>;
585
606
  export type TelephonyConfig = z.infer<typeof telephonySchema>;
607
+ export type RealtimeConfig = z.infer<typeof realtimeSchema>;
586
608
  export type TwilioTelephonyConfig = z.infer<typeof twilioTelephonySchema>;
587
609
  export type VonageTelephonyConfig = z.infer<typeof vonageTelephonySchema>;
588
610
  export type RingCentralTelephonyConfig = z.infer<typeof ringcentralTelephonySchema>;
@@ -648,10 +670,23 @@ export const DEFAULT_SERVER_CONFIG: Partial<ConfigInfo> = {
648
670
  autoCreateNewUsers: true,
649
671
  newUserLimitedToAuthorizedDomains: false,
650
672
  newUserAuthorizedDomains: [],
651
- newUserRoles: ['UI', 'Developer'],
673
+ // 'UI' ONLY, deliberately (issue #4260). Auto-provisioning is on by default above, with no
674
+ // domain restriction, so this list is the standing authority of anyone the configured IdP will
675
+ // issue a token for. On the baseline seed 'Developer' and 'Integration' hold unfiltered
676
+ // CanUpdate on ~439 of the database's ~446 entities, so defaulting every such identity into
677
+ // either grants broad data-plane access no host should hand out by default. (The MJ: Users
678
+ // escalation this list also used to guard against — writing your own Type to 'Owner' — is now
679
+ // closed at the entity layer regardless of role: see MJUserEntityServer in
680
+ // @memberjunction/core-entities-server.) 'UI' carries the end-user surface (conversations,
681
+ // views, dashboards, settings) and no write on MJ: Users. Hosts that need more grant it
682
+ // per-deployment.
683
+ newUserRoles: ['UI'],
652
684
  updateCacheWhenNotFound: true,
653
685
  updateCacheWhenNotFoundDelay: 5000,
654
- contextUserForNewUserCreation: 'not.set@nowhere.com',
686
+ // The seeded system user, named by `Name`. Its Email ('not.set@nowhere.com') resolves too —
687
+ // resolution tries both columns — but naming it this way keeps the default readable as what it
688
+ // is, rather than as an address nobody can receive mail at.
689
+ contextUserForNewUserCreation: 'System',
655
690
  CreateUserApplicationRecords: true,
656
691
  UserApplications: []
657
692
  },
@@ -716,6 +751,11 @@ export const DEFAULT_SERVER_CONFIG: Partial<ConfigInfo> = {
716
751
  maxConcurrentRuns: 3
717
752
  },
718
753
 
754
+ // Realtime WebRTC SDP broker defaults (off by default)
755
+ realtime: {
756
+ enabled: parseBooleanEnv(process.env.MJ_REALTIME_ENABLED),
757
+ },
758
+
719
759
  // Telemetry defaults — on unless the operator turns it off via MJ_TELEMETRY_ENABLED.
720
760
  //
721
761
  // The env read lives HERE rather than in telemetrySchema for the same reason as
@@ -1,13 +1,26 @@
1
- import { RegisterClass } from '@memberjunction/global';
2
- import { BaseEntity, EntityDeleteOptions, EntitySaveOptions } from '@memberjunction/core';
1
+ import { RegisterClass, UUIDsEqual } from '@memberjunction/global';
2
+ import { BaseEntity, EntityDeleteOptions, EntitySaveOptions, IMetadataProvider } from '@memberjunction/core';
3
3
  import { MJEntityPermissionEntity } from '@memberjunction/core-entities';
4
4
  import { HttpPost } from '@memberjunction/network-utils';
5
+ import { ReconcileFieldPermissionsQuietly } from '@memberjunction/core-entities-server';
5
6
  import { ___codeGenAPIPort, ___codeGenAPISubmissionDelay, ___codeGenAPIURL } from '../config.js';
6
7
 
7
8
  /**
8
9
  * Server-side only class that extends the entity permissions object to watch for changes to entity permissions, build a queue of entities that have been changed, and then from time to time, submit
9
10
  * them to an API server that will execute the underlying permission changes at the database level.
10
- *
11
+ *
12
+ * It is also the field-level-security lifecycle adapter for entity-permission changes. Snapshot
13
+ * initialization creates field-permission rows for the roles holding entity access AT THE TIME
14
+ * the flag is flipped; a role granted access afterwards has none, and on an enabled entity a
15
+ * field with no rows is denied — so that role would pass the entity gate and then see zero
16
+ * fields, which reads as a broken screen rather than a permissions gap. The reverse matters
17
+ * too: a role that loses entity access leaves rows that can no longer affect any decision.
18
+ *
19
+ * Both responsibilities live here because `ClassFactory` resolves ONE class per entity name.
20
+ * A second `@RegisterClass(BaseEntity, 'MJ: Entity Permissions')` in another package would not
21
+ * compose with this one — priority auto-increments by load order, so whichever loaded last
22
+ * would silently displace the other.
23
+ *
11
24
  * This class is within the memberjunction/server package because it is closely coupled to other aspects of what
12
25
  * happens in the server. That's why it is not in the core-entities-server package.
13
26
  */
@@ -88,19 +101,65 @@ export class MJEntityPermissionEntityServer extends MJEntityPermissionEntity {
88
101
  }
89
102
  }
90
103
 
91
- override Save(options?: EntitySaveOptions): Promise<boolean> {
104
+ override async Save(options?: EntitySaveOptions): Promise<boolean> {
92
105
  // simply queue up the entity ID
93
106
  if (this.Dirty || options?.IgnoreDirtyState) MJEntityPermissionEntityServer.AddToQueue(this.EntityID);
94
107
 
95
- return super.Save(options);
108
+ // Capture before the save — afterwards nothing is dirty and the dirty check reads false.
109
+ const affectsFieldSecurity = this.affectsFieldPermissionRows();
110
+
111
+ if (!(await super.Save(options))) return false;
112
+
113
+ if (affectsFieldSecurity) await this.reconcileFieldPermissions(this.EntityID);
114
+ return true;
96
115
  }
97
116
 
98
117
  override async Delete(options: EntityDeleteOptions): Promise<boolean> {
118
+ // Capture before the delete — afterwards this record's EntityID is gone.
119
+ const targetEntityID = this.EntityID;
120
+
99
121
  const success = await super.Delete(options);
100
122
 
101
123
  // simply queue up the entity ID if the delete worked
102
- if (success) MJEntityPermissionEntityServer.AddToQueue(this.EntityID);
124
+ if (success) {
125
+ MJEntityPermissionEntityServer.AddToQueue(targetEntityID);
126
+ await this.reconcileFieldPermissions(targetEntityID);
127
+ }
103
128
 
104
129
  return success;
105
130
  }
131
+
132
+ /**
133
+ * Whether this save can change which field-permission rows should exist.
134
+ *
135
+ * Only the access flags and the role matter — the field-permission delta keys off a role's
136
+ * effective entity-level access, so an edit to an RLS filter changes nothing here. A new
137
+ * record always counts, since it may be the grant that brings a role into scope.
138
+ */
139
+ protected affectsFieldPermissionRows(): boolean {
140
+ if (!this.IsSaved) return true;
141
+
142
+ return ['Type', 'CanRead', 'CanUpdate', 'CanCreate', 'RoleID'].some(
143
+ (fieldName) => this.GetFieldByName(fieldName)?.Dirty === true
144
+ );
145
+ }
146
+
147
+ /**
148
+ * Reconciles the target entity's field-permission rows, when that entity has field security
149
+ * switched on. The flag check keeps this free for the overwhelming majority of permission
150
+ * edits, which are on entities that never opted in.
151
+ *
152
+ * Runs AFTER the permission write commits, and failures are logged rather than thrown: the
153
+ * save that triggered it has already succeeded, so reporting it as failed would be wrong.
154
+ * Missing rows fail closed until the next reconciliation.
155
+ */
156
+ protected async reconcileFieldPermissions(entityID: string): Promise<void> {
157
+ if (!entityID) return;
158
+
159
+ const provider = this.ProviderToUse as unknown as IMetadataProvider;
160
+ const entityInfo = provider?.Entities?.find((e) => UUIDsEqual(e.ID, entityID));
161
+ if (!entityInfo?.EnableFieldLevelSecurity) return;
162
+
163
+ await ReconcileFieldPermissionsQuietly(entityInfo, provider, this.ContextCurrentUser);
164
+ }
106
165
  }