@memberjunction/server 6.1.0-edge.1 → 6.1.0-edge.3

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 (261) hide show
  1. package/LICENSE +180 -4
  2. package/dist/auth/AuthProviderCatalogRouter.d.ts +31 -0
  3. package/dist/auth/AuthProviderCatalogRouter.d.ts.map +1 -0
  4. package/dist/auth/AuthProviderCatalogRouter.js +69 -0
  5. package/dist/auth/AuthProviderCatalogRouter.js.map +1 -0
  6. package/dist/auth/AuthProviderEngine.d.ts +96 -0
  7. package/dist/auth/AuthProviderEngine.d.ts.map +1 -0
  8. package/dist/auth/AuthProviderEngine.js +245 -0
  9. package/dist/auth/AuthProviderEngine.js.map +1 -0
  10. package/dist/auth/exampleNewUserSubClass.js +1 -1
  11. package/dist/auth/exampleNewUserSubClass.js.map +1 -1
  12. package/dist/auth/index.d.ts +11 -0
  13. package/dist/auth/index.d.ts.map +1 -1
  14. package/dist/auth/index.js +65 -18
  15. package/dist/auth/index.js.map +1 -1
  16. package/dist/auth/initializeProviders.d.ts +64 -1
  17. package/dist/auth/initializeProviders.d.ts.map +1 -1
  18. package/dist/auth/initializeProviders.js +117 -5
  19. package/dist/auth/initializeProviders.js.map +1 -1
  20. package/dist/auth/magicLink/MagicLinkService.js +1 -1
  21. package/dist/auth/magicLink/MagicLinkService.js.map +1 -1
  22. package/dist/auth/newUsers.js +1 -1
  23. package/dist/auth/newUsers.js.map +1 -1
  24. package/dist/config.d.ts +89 -0
  25. package/dist/config.d.ts.map +1 -1
  26. package/dist/config.js +52 -35
  27. package/dist/config.js.map +1 -1
  28. package/dist/context.js +2 -2
  29. package/dist/context.js.map +1 -1
  30. package/dist/generated/generated.d.ts +540 -1595
  31. package/dist/generated/generated.d.ts.map +1 -1
  32. package/dist/generated/generated.js +2755 -16857
  33. package/dist/generated/generated.js.map +1 -1
  34. package/dist/generic/ResolverBase.d.ts +20 -4
  35. package/dist/generic/ResolverBase.d.ts.map +1 -1
  36. package/dist/generic/ResolverBase.js +58 -21
  37. package/dist/generic/ResolverBase.js.map +1 -1
  38. package/dist/generic/RunViewResolver.d.ts +4 -0
  39. package/dist/generic/RunViewResolver.d.ts.map +1 -1
  40. package/dist/generic/RunViewResolver.js +31 -0
  41. package/dist/generic/RunViewResolver.js.map +1 -1
  42. package/dist/index.d.ts +3 -0
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/index.js +183 -54
  45. package/dist/index.js.map +1 -1
  46. package/dist/integration/RSUProgressBridge.d.ts +83 -0
  47. package/dist/integration/RSUProgressBridge.d.ts.map +1 -0
  48. package/dist/integration/RSUProgressBridge.js +230 -0
  49. package/dist/integration/RSUProgressBridge.js.map +1 -0
  50. package/dist/integration/SchemaRefreshLaunch.d.ts +52 -0
  51. package/dist/integration/SchemaRefreshLaunch.d.ts.map +1 -0
  52. package/dist/integration/SchemaRefreshLaunch.js +50 -0
  53. package/dist/integration/SchemaRefreshLaunch.js.map +1 -0
  54. package/dist/logging/secretRedactor.d.ts +9 -0
  55. package/dist/logging/secretRedactor.d.ts.map +1 -1
  56. package/dist/logging/secretRedactor.js +39 -0
  57. package/dist/logging/secretRedactor.js.map +1 -1
  58. package/dist/middleware/BaseServerMiddleware.d.ts +5 -0
  59. package/dist/middleware/BaseServerMiddleware.d.ts.map +1 -1
  60. package/dist/middleware/BaseServerMiddleware.js +5 -0
  61. package/dist/middleware/BaseServerMiddleware.js.map +1 -1
  62. package/dist/providerConfigUnits.d.ts +20 -0
  63. package/dist/providerConfigUnits.d.ts.map +1 -0
  64. package/dist/providerConfigUnits.js +22 -0
  65. package/dist/providerConfigUnits.js.map +1 -0
  66. package/dist/realtimeWidget/WidgetSessionService.js +1 -1
  67. package/dist/realtimeWidget/WidgetSessionService.js.map +1 -1
  68. package/dist/realtimeWidget/widgetGuestElevation.js +1 -1
  69. package/dist/realtimeWidget/widgetGuestElevation.js.map +1 -1
  70. package/dist/resolvers/ActionResolver.d.ts.map +1 -1
  71. package/dist/resolvers/ActionResolver.js +12 -0
  72. package/dist/resolvers/ActionResolver.js.map +1 -1
  73. package/dist/resolvers/ComponentRegistryResolver.js +1 -1
  74. package/dist/resolvers/ComponentRegistryResolver.js.map +1 -1
  75. package/dist/resolvers/EntityPermissionResolver.js +1 -1
  76. package/dist/resolvers/EntityPermissionResolver.js.map +1 -1
  77. package/dist/resolvers/FileResolver.d.ts +34 -4
  78. package/dist/resolvers/FileResolver.d.ts.map +1 -1
  79. package/dist/resolvers/FileResolver.js +218 -28
  80. package/dist/resolvers/FileResolver.js.map +1 -1
  81. package/dist/resolvers/IntegrationDiscoveryResolver.d.ts +85 -3
  82. package/dist/resolvers/IntegrationDiscoveryResolver.d.ts.map +1 -1
  83. package/dist/resolvers/IntegrationDiscoveryResolver.js +302 -82
  84. package/dist/resolvers/IntegrationDiscoveryResolver.js.map +1 -1
  85. package/dist/resolvers/PotentialDuplicateRecordResolver.js +1 -1
  86. package/dist/resolvers/PotentialDuplicateRecordResolver.js.map +1 -1
  87. package/dist/resolvers/QueryResolver.d.ts +5 -4
  88. package/dist/resolvers/QueryResolver.d.ts.map +1 -1
  89. package/dist/resolvers/QueryResolver.js +29 -14
  90. package/dist/resolvers/QueryResolver.js.map +1 -1
  91. package/dist/resolvers/QuerySystemUserResolver.d.ts +12 -3
  92. package/dist/resolvers/QuerySystemUserResolver.d.ts.map +1 -1
  93. package/dist/resolvers/QuerySystemUserResolver.js +33 -14
  94. package/dist/resolvers/QuerySystemUserResolver.js.map +1 -1
  95. package/dist/resolvers/RealtimeClientSessionResolver.d.ts +10 -0
  96. package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -1
  97. package/dist/resolvers/RealtimeClientSessionResolver.js +5 -0
  98. package/dist/resolvers/RealtimeClientSessionResolver.js.map +1 -1
  99. package/dist/resolvers/RunAIAgentResolver.d.ts +3 -1
  100. package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
  101. package/dist/resolvers/RunAIAgentResolver.js +20 -7
  102. package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
  103. package/dist/resolvers/SearchEntitiesResolver.js +1 -1
  104. package/dist/resolvers/SearchEntitiesResolver.js.map +1 -1
  105. package/dist/resolvers/SqlLoggingConfigResolver.d.ts.map +1 -1
  106. package/dist/resolvers/SqlLoggingConfigResolver.js +1 -1
  107. package/dist/resolvers/SqlLoggingConfigResolver.js.map +1 -1
  108. package/dist/resolvers/SyncRolesUsersResolver.js +2 -2
  109. package/dist/resolvers/SyncRolesUsersResolver.js.map +1 -1
  110. package/dist/resolvers/TaskGraphFrameResolver.d.ts +18 -0
  111. package/dist/resolvers/TaskGraphFrameResolver.d.ts.map +1 -1
  112. package/dist/resolvers/TaskGraphFrameResolver.js +75 -0
  113. package/dist/resolvers/TaskGraphFrameResolver.js.map +1 -1
  114. package/dist/resolvers/UserFavoriteResolver.js +1 -1
  115. package/dist/resolvers/UserFavoriteResolver.js.map +1 -1
  116. package/dist/resolvers/UserResolver.d.ts +19 -17
  117. package/dist/resolvers/UserResolver.d.ts.map +1 -1
  118. package/dist/resolvers/UserResolver.js +66 -67
  119. package/dist/resolvers/UserResolver.js.map +1 -1
  120. package/dist/resolvers/currentUserRoles.d.ts +19 -0
  121. package/dist/resolvers/currentUserRoles.d.ts.map +1 -0
  122. package/dist/resolvers/currentUserRoles.js +36 -0
  123. package/dist/resolvers/currentUserRoles.js.map +1 -0
  124. package/dist/rest/MediaAccessKeys.d.ts +17 -0
  125. package/dist/rest/MediaAccessKeys.d.ts.map +1 -1
  126. package/dist/rest/MediaAccessKeys.js +42 -1
  127. package/dist/rest/MediaAccessKeys.js.map +1 -1
  128. package/dist/rest/MediaStreamHandler.d.ts +3 -2
  129. package/dist/rest/MediaStreamHandler.d.ts.map +1 -1
  130. package/dist/rest/MediaStreamHandler.js +83 -2
  131. package/dist/rest/MediaStreamHandler.js.map +1 -1
  132. package/dist/rest/OAuthCallbackHandler.d.ts +49 -1
  133. package/dist/rest/OAuthCallbackHandler.d.ts.map +1 -1
  134. package/dist/rest/OAuthCallbackHandler.js +130 -44
  135. package/dist/rest/OAuthCallbackHandler.js.map +1 -1
  136. package/dist/rest/SignatureWebhookHandler.js +1 -1
  137. package/dist/rest/SignatureWebhookHandler.js.map +1 -1
  138. package/dist/rest/UploadTokenManager.d.ts +90 -0
  139. package/dist/rest/UploadTokenManager.d.ts.map +1 -0
  140. package/dist/rest/UploadTokenManager.js +156 -0
  141. package/dist/rest/UploadTokenManager.js.map +1 -0
  142. package/dist/services/DurableEntityActionTaskSubmitter.d.ts +26 -0
  143. package/dist/services/DurableEntityActionTaskSubmitter.d.ts.map +1 -0
  144. package/dist/services/DurableEntityActionTaskSubmitter.js +55 -0
  145. package/dist/services/DurableEntityActionTaskSubmitter.js.map +1 -0
  146. package/dist/services/IntegrationSyncWorkerService.d.ts +48 -0
  147. package/dist/services/IntegrationSyncWorkerService.d.ts.map +1 -0
  148. package/dist/services/IntegrationSyncWorkerService.js +132 -0
  149. package/dist/services/IntegrationSyncWorkerService.js.map +1 -0
  150. package/dist/services/ScheduledJobsService.js +1 -1
  151. package/dist/services/ScheduledJobsService.js.map +1 -1
  152. package/dist/services/StartTaskGraphDispatcher.d.ts +0 -10
  153. package/dist/services/StartTaskGraphDispatcher.d.ts.map +1 -1
  154. package/dist/services/StartTaskGraphDispatcher.js +37 -2
  155. package/dist/services/StartTaskGraphDispatcher.js.map +1 -1
  156. package/dist/services/TaskGraphActionRunner.d.ts +34 -0
  157. package/dist/services/TaskGraphActionRunner.d.ts.map +1 -0
  158. package/dist/services/TaskGraphActionRunner.js +95 -0
  159. package/dist/services/TaskGraphActionRunner.js.map +1 -0
  160. package/dist/services/TaskGraphAgentRunner.d.ts +20 -0
  161. package/dist/services/TaskGraphAgentRunner.d.ts.map +1 -1
  162. package/dist/services/TaskGraphAgentRunner.js +53 -0
  163. package/dist/services/TaskGraphAgentRunner.js.map +1 -1
  164. package/dist/services/TaskGraphContinuationDeliverer.d.ts.map +1 -1
  165. package/dist/services/TaskGraphContinuationDeliverer.js +12 -1
  166. package/dist/services/TaskGraphContinuationDeliverer.js.map +1 -1
  167. package/dist/services/TaskGraphPromptRunner.d.ts +31 -0
  168. package/dist/services/TaskGraphPromptRunner.d.ts.map +1 -0
  169. package/dist/services/TaskGraphPromptRunner.js +134 -0
  170. package/dist/services/TaskGraphPromptRunner.js.map +1 -0
  171. package/dist/telephony/RingCentralTelephonyService.js +1 -1
  172. package/dist/telephony/RingCentralTelephonyService.js.map +1 -1
  173. package/dist/telephony/TwilioTelephonyRouter.js +1 -1
  174. package/dist/telephony/TwilioTelephonyRouter.js.map +1 -1
  175. package/dist/telephony/VonageTelephonyRouter.js +1 -1
  176. package/dist/telephony/VonageTelephonyRouter.js.map +1 -1
  177. package/dist/types.d.ts +6 -0
  178. package/dist/types.d.ts.map +1 -1
  179. package/dist/types.js.map +1 -1
  180. package/package.json +91 -91
  181. package/src/__tests__/APIKeyScopeAuth.test.ts +475 -0
  182. package/src/__tests__/AuthProviderEngine.test.ts +278 -0
  183. package/src/__tests__/EntityCRUDHandler.test.ts +378 -0
  184. package/src/__tests__/IntegrationSyncWorkerService.test.ts +275 -0
  185. package/src/__tests__/OAuthCallbackHandler.openRedirect.test.ts +117 -0
  186. package/src/__tests__/OAuthCallbackHandler.xss.test.ts +4 -1
  187. package/src/__tests__/RESTEndpointHandler.test.ts +672 -0
  188. package/src/__tests__/RSUProgressBridge.test.ts +250 -0
  189. package/src/__tests__/RealtimeBridgeResolver.test.ts +10 -2
  190. package/src/__tests__/RealtimeClientSessionResolver.test.ts +1 -1
  191. package/src/__tests__/ResolverBase.frozenRecordMapping.test.ts +133 -0
  192. package/src/__tests__/ResolverBase.transportMapping.test.ts +289 -0
  193. package/src/__tests__/SchemaRefreshLaunch.test.ts +114 -0
  194. package/src/__tests__/SessionManager.test.ts +1 -1
  195. package/src/__tests__/UserResolver.currentUser.test.ts +87 -0
  196. package/src/__tests__/ViewOperationsHandler.test.ts +277 -0
  197. package/src/__tests__/WidgetSessionService.test.ts +1 -1
  198. package/src/__tests__/config-units.test.ts +74 -0
  199. package/src/__tests__/databaseAbstraction.test.ts +1 -1
  200. package/src/__tests__/initializeProviders.refresh.test.ts +146 -0
  201. package/src/__tests__/magicLinkService.provisionLog.test.ts +1 -1
  202. package/src/__tests__/mjapi-bootstrap.test.ts +0 -6
  203. package/src/__tests__/newUsers.test.ts +726 -0
  204. package/src/__tests__/resolverBase.rls.test.ts +3 -0
  205. package/src/__tests__/secretRedactor.failOpen.test.ts +135 -0
  206. package/src/__tests__/uploadTokenManager.test.ts +159 -0
  207. package/src/__tests__/widgetGuestElevation.test.ts +1 -1
  208. package/src/auth/AuthProviderCatalogRouter.ts +76 -0
  209. package/src/auth/AuthProviderEngine.ts +253 -0
  210. package/src/auth/exampleNewUserSubClass.ts +1 -1
  211. package/src/auth/index.ts +69 -21
  212. package/src/auth/initializeProviders.ts +128 -7
  213. package/src/auth/magicLink/MagicLinkService.ts +1 -1
  214. package/src/auth/newUsers.ts +1 -1
  215. package/src/config.ts +55 -36
  216. package/src/context.ts +2 -2
  217. package/src/generated/generated.ts +2038 -10529
  218. package/src/generic/ResolverBase.ts +63 -21
  219. package/src/generic/RunViewResolver.ts +31 -0
  220. package/src/index.ts +199 -56
  221. package/src/integration/RSUProgressBridge.ts +248 -0
  222. package/src/integration/SchemaRefreshLaunch.ts +77 -0
  223. package/src/logging/secretRedactor.ts +43 -0
  224. package/src/middleware/BaseServerMiddleware.ts +5 -0
  225. package/src/providerConfigUnits.ts +21 -0
  226. package/src/realtimeWidget/WidgetSessionService.ts +1 -1
  227. package/src/realtimeWidget/widgetGuestElevation.ts +1 -1
  228. package/src/resolvers/ActionResolver.ts +13 -0
  229. package/src/resolvers/ComponentRegistryResolver.ts +1 -1
  230. package/src/resolvers/EntityPermissionResolver.ts +1 -1
  231. package/src/resolvers/FileResolver.ts +200 -40
  232. package/src/resolvers/IntegrationDiscoveryResolver.ts +328 -70
  233. package/src/resolvers/PotentialDuplicateRecordResolver.ts +1 -1
  234. package/src/resolvers/QueryResolver.ts +25 -11
  235. package/src/resolvers/QuerySystemUserResolver.ts +28 -13
  236. package/src/resolvers/RealtimeClientSessionResolver.ts +13 -0
  237. package/src/resolvers/RunAIAgentResolver.ts +21 -6
  238. package/src/resolvers/SearchEntitiesResolver.ts +1 -1
  239. package/src/resolvers/SqlLoggingConfigResolver.ts +1 -2
  240. package/src/resolvers/SyncRolesUsersResolver.ts +2 -2
  241. package/src/resolvers/TaskGraphFrameResolver.ts +67 -0
  242. package/src/resolvers/UserFavoriteResolver.ts +1 -1
  243. package/src/resolvers/UserResolver.ts +99 -60
  244. package/src/resolvers/currentUserRoles.ts +47 -0
  245. package/src/rest/MediaAccessKeys.ts +47 -1
  246. package/src/rest/MediaStreamHandler.ts +98 -3
  247. package/src/rest/OAuthCallbackHandler.ts +150 -42
  248. package/src/rest/SignatureWebhookHandler.ts +1 -1
  249. package/src/rest/UploadTokenManager.ts +211 -0
  250. package/src/services/DurableEntityActionTaskSubmitter.ts +78 -0
  251. package/src/services/IntegrationSyncWorkerService.ts +141 -0
  252. package/src/services/ScheduledJobsService.ts +1 -1
  253. package/src/services/StartTaskGraphDispatcher.ts +38 -1
  254. package/src/services/TaskGraphActionRunner.ts +99 -0
  255. package/src/services/TaskGraphAgentRunner.ts +58 -1
  256. package/src/services/TaskGraphContinuationDeliverer.ts +11 -1
  257. package/src/services/TaskGraphPromptRunner.ts +144 -0
  258. package/src/telephony/RingCentralTelephonyService.ts +1 -1
  259. package/src/telephony/TwilioTelephonyRouter.ts +1 -1
  260. package/src/telephony/VonageTelephonyRouter.ts +1 -1
  261. package/src/types.ts +6 -0
@@ -20,7 +20,8 @@ import {
20
20
  UserInfo,
21
21
  } from '@memberjunction/core';
22
22
  import { MJAuditLogEntity, MJErrorLogEntity, MJUserViewEntityExtended } from '@memberjunction/core-entities';
23
- import { SQLServerDataProvider, UserCache } from '@memberjunction/sqlserver-dataprovider';
23
+ import { SQLServerDataProvider } from '@memberjunction/sqlserver-dataprovider';
24
+ import { UserCache } from '@memberjunction/generic-database-provider';
24
25
  import { PubSubEngine, AuthorizationError } from 'type-graphql';
25
26
  import { GraphQLError } from 'graphql';
26
27
  import { GetAPIKeyEngine } from '@memberjunction/api-keys';
@@ -62,16 +63,28 @@ export class ResolverBase {
62
63
  * - AllowDecryptInAPI=false + SendEncryptedValue=true: Keep encrypted ciphertext
63
64
  * - AllowDecryptInAPI=false + SendEncryptedValue=false: Replace with sentinel
64
65
  *
66
+ * Returns a COPY — `dataObject` is never written to. Callers routinely pass rows straight
67
+ * from `findBy`/`RunView`, which are the server cache's own objects held by reference, and
68
+ * `LocalCacheManager` deep-freezes them. Renaming in place therefore threw
69
+ * `Cannot add property _mj__CreatedAt, object is not extensible` on every `UserByEmail` /
70
+ * `UserByID` / `UserByEmployeeID` call and on every generated single-record resolver whose
71
+ * entity has caching enabled. (Before the freeze it did something worse but quieter: it
72
+ * rewrote the cached row's keys, so later readers were served transport-shaped rows that
73
+ * `BaseEntity.SetMany` rejects.) Copying here fixes every call site at once and makes the
74
+ * hazard unreachable for future ones.
75
+ *
65
76
  * @param entityName - The entity name
66
- * @param dataObject - The data object with field values
77
+ * @param dataObject - The data object with field values. Not modified.
67
78
  * @param contextUser - Optional user context for decryption (required for encrypted fields)
68
- * @returns The processed data object
79
+ * @returns A new object in transport shape, or null when there is nothing to map
69
80
  */
70
81
  protected async MapFieldNamesToCodeNames(entityName: string, dataObject: any, contextUser?: UserInfo, provider?: IMetadataProvider): Promise<any> {
71
82
  // Return null for empty objects (e.g. when no rows found due to RLS filtering)
72
83
  if (!dataObject || Object.keys(dataObject).length === 0) {
73
84
  return null;
74
85
  }
86
+ // Shallow copy up front so every write below lands on our object, never the caller's.
87
+ dataObject = { ...dataObject };
75
88
 
76
89
  // for the given entity name provided, check to see if there are any fields
77
90
  // where the code name is different from the field name, and for just those
@@ -184,14 +197,21 @@ export class ResolverBase {
184
197
  return true;
185
198
  }
186
199
 
187
- protected async ArrayMapFieldNamesToCodeNames(entityName: string, dataObjectArray: any[], contextUser?: UserInfo): Promise<any[]> {
188
- // iterate through the array and call MapFieldNamesToCodeNames for each element
189
- if (dataObjectArray && dataObjectArray.length > 0) {
190
- for (const element of dataObjectArray) {
191
- await this.MapFieldNamesToCodeNames(entityName, element, contextUser);
192
- }
200
+ /**
201
+ * Array form of {@link MapFieldNamesToCodeNames}. Returns a NEW array of NEW objects; neither
202
+ * the input array nor its rows are modified. Both matter: the cache freezes the array as well
203
+ * as the rows it contains, so collecting the mapped copies (rather than mapping in place and
204
+ * returning the original) is what makes this safe on cache-served input.
205
+ */
206
+ protected async ArrayMapFieldNamesToCodeNames(entityName: string, dataObjectArray: any[], contextUser?: UserInfo, provider?: IMetadataProvider): Promise<any[]> {
207
+ if (!dataObjectArray || dataObjectArray.length === 0) {
208
+ return dataObjectArray;
193
209
  }
194
- return dataObjectArray;
210
+ const mapped: any[] = [];
211
+ for (const element of dataObjectArray) {
212
+ mapped.push(await this.MapFieldNamesToCodeNames(entityName, element, contextUser, provider));
213
+ }
214
+ return mapped;
195
215
  }
196
216
 
197
217
  /**
@@ -388,7 +408,8 @@ export class ResolverBase {
388
408
  viewInput.AfterKey
389
409
  ? CompositeKey.FromKeyValuePairs((viewInput.AfterKey as { KeyValuePairs: { FieldName: string; Value: string }[] }).KeyValuePairs)
390
410
  : undefined,
391
- viewInput.BypassCache
411
+ viewInput.BypassCache,
412
+ viewInput.DataSource
392
413
  );
393
414
  }
394
415
  else {
@@ -431,7 +452,8 @@ export class ResolverBase {
431
452
  viewInput.StartRow,
432
453
  viewInput.Aggregates,
433
454
  undefined,
434
- viewInput.BypassCache
455
+ viewInput.BypassCache,
456
+ viewInput.DataSource
435
457
  );
436
458
  } catch (err) {
437
459
  console.log(err);
@@ -477,7 +499,8 @@ export class ResolverBase {
477
499
  viewInput.StartRow,
478
500
  viewInput.Aggregates,
479
501
  undefined,
480
- viewInput.BypassCache
502
+ viewInput.BypassCache,
503
+ viewInput.DataSource
481
504
  );
482
505
  } catch (err) {
483
506
  console.log(err);
@@ -551,6 +574,7 @@ export class ResolverBase {
551
574
  userPayload,
552
575
  aggregates: viewInput.Aggregates,
553
576
  bypassCache: viewInput.BypassCache,
577
+ dataSource: viewInput.DataSource,
554
578
  });
555
579
  } catch (err) {
556
580
  LogError(err);
@@ -747,7 +771,8 @@ export class ResolverBase {
747
771
  startRow: number | undefined,
748
772
  aggregates?: AggregateExpression[],
749
773
  afterKey?: CompositeKey,
750
- bypassCache?: boolean
774
+ bypassCache?: boolean,
775
+ dataSource?: 'Live' | 'Materialized'
751
776
  ) {
752
777
  try {
753
778
  if (!viewInfo || !userPayload) return null;
@@ -817,6 +842,7 @@ export class ResolverBase {
817
842
  ResultType: rt,
818
843
  Aggregates: aggregates,
819
844
  BypassCache: bypassCache,
845
+ DataSource: dataSource,
820
846
  },
821
847
  user
822
848
  );
@@ -826,12 +852,27 @@ export class ResolverBase {
826
852
  LogStatus(`[ResolverBase] RunView result aggregate info: entityName=${viewInfo.Entity}, hasAggregateResults=${!!result?.AggregateResults}, aggregateResultCount=${result?.AggregateResults?.length || 0}, aggregateExecutionTime=${result?.AggregateExecutionTime}, aggregateResults=${JSON.stringify(result?.AggregateResults)}`);
827
853
  }
828
854
 
829
- // Process results for GraphQL transport
855
+ // Process results for GraphQL transport.
856
+ //
857
+ // Map onto COPIES, never in place. `FieldMapper.MapFields` renames keys by
858
+ // mutating (`obj[mapped] = obj[k]; delete obj[k]`), and these rows are the
859
+ // provider's own result objects — which the server cache holds BY REFERENCE
860
+ // under a reference-sharing storage provider. Mapping them in place rewrote
861
+ // `__mj_CreatedAt` to the transport alias `_mj__CreatedAt` inside the live
862
+ // cache, and because that cache is process-wide, one GraphQL response made
863
+ // every later read hand back transport-shaped rows that `BaseEntity.SetMany`
864
+ // rejects. `ArrayFilterEncryptedFieldsForAPI` mutates too, so it must also
865
+ // see the copies. (FileResolver already maps a spread copy.)
866
+ //
867
+ // These copies are LOAD-BEARING, not merely defensive: LocalCacheManager now
868
+ // deep-freezes cached rows (see ILocalStorageProvider.SharesReferences), so
869
+ // mapping in place would throw rather than silently corrupt. `{ ...r }` is a
870
+ // SHALLOW copy, which is sufficient here because the only post-map mutators
871
+ // rename top-level keys and redact scalar fields — and the cache's freeze is
872
+ // deep, so a nested value can no longer be reached through the copy either.
830
873
  const mapper = new FieldMapper();
831
874
  if (result?.Success && result.Results?.length) {
832
- for (const r of result.Results) {
833
- mapper.MapFields(r);
834
- }
875
+ result.Results = result.Results.map(r => mapper.MapFields({ ...r }));
835
876
  // Filter encrypted fields before sending to API client
836
877
  await this.ArrayFilterEncryptedFieldsForAPI(
837
878
  viewInfo.Entity,
@@ -937,6 +978,7 @@ export class ResolverBase {
937
978
  ResultType: rt,
938
979
  Aggregates: param.aggregates,
939
980
  BypassCache: param.bypassCache,
981
+ DataSource: param.dataSource,
940
982
  });
941
983
  }
942
984
 
@@ -948,9 +990,9 @@ export class ResolverBase {
948
990
  for (let i = 0; i < runViewResults.length; i++) {
949
991
  const runViewResult = runViewResults[i];
950
992
  if (runViewResult?.Success && runViewResult.Results?.length) {
951
- for (const result of runViewResult.Results) {
952
- mapper.MapFields(result);
953
- }
993
+ // Copy-then-map, same reason as the single-view path above: these rows
994
+ // are cache-held references and MapFields renames keys in place.
995
+ runViewResult.Results = runViewResult.Results.map(r => mapper.MapFields({ ...r }));
954
996
  // Filter encrypted fields before sending to API client
955
997
  // Use the corresponding param's entity name
956
998
  const entityName = params[i]?.viewInfo?.Entity;
@@ -177,6 +177,13 @@ export class RunViewByIDInput {
177
177
  'Optional, when true bypasses ALL server-side caching for this view run — the pre-check cache lookup is skipped and the result is not stored in the cache. Use for maintenance/audit queries that must see true database state, or to force-refresh views whose filters reference rows the server cache invalidator cannot follow (e.g., cross-entity subqueries against vwListDetails).',
178
178
  })
179
179
  BypassCache?: boolean;
180
+
181
+ @Field(() => String, {
182
+ nullable: true,
183
+ description:
184
+ "Optional source-of-truth selector for a base-view materialization: 'Live' (default) reads the entity's live base view; 'Materialized' reads its materialized snapshot wrapper view (materialized_vw<CodeName>). Only request 'Materialized' for an entity that actually has a base-view materialization: it resolves to the wrapper view by naming convention with NO existence check or fallback, so requesting it for a non-materialized entity targets a view that does not exist and the query errors.",
185
+ })
186
+ DataSource?: 'Live' | 'Materialized';
180
187
  }
181
188
 
182
189
  @InputType()
@@ -291,6 +298,13 @@ export class RunViewByNameInput {
291
298
  'Optional, when true bypasses ALL server-side caching for this view run — the pre-check cache lookup is skipped and the result is not stored in the cache. Use for maintenance/audit queries that must see true database state, or to force-refresh views whose filters reference rows the server cache invalidator cannot follow (e.g., cross-entity subqueries against vwListDetails).',
292
299
  })
293
300
  BypassCache?: boolean;
301
+
302
+ @Field(() => String, {
303
+ nullable: true,
304
+ description:
305
+ "Optional source-of-truth selector for a base-view materialization: 'Live' (default) reads the entity's live base view; 'Materialized' reads its materialized snapshot wrapper view (materialized_vw<CodeName>). Only request 'Materialized' for an entity that actually has a base-view materialization: it resolves to the wrapper view by naming convention with NO existence check or fallback, so requesting it for a non-materialized entity targets a view that does not exist and the query errors.",
306
+ })
307
+ DataSource?: 'Live' | 'Materialized';
294
308
  }
295
309
 
296
310
  @InputType()
@@ -391,6 +405,13 @@ export class RunDynamicViewInput {
391
405
  'Optional, when true bypasses ALL server-side caching for this view run — the pre-check cache lookup is skipped and the result is not stored in the cache. Use for maintenance/audit queries that must see true database state, or to force-refresh views whose filters reference rows the server cache invalidator cannot follow (e.g., cross-entity subqueries against vwListDetails).',
392
406
  })
393
407
  BypassCache?: boolean;
408
+
409
+ @Field(() => String, {
410
+ nullable: true,
411
+ description:
412
+ "Optional source-of-truth selector for a base-view materialization: 'Live' (default) reads the entity's live base view; 'Materialized' reads its materialized snapshot wrapper view (materialized_vw<CodeName>). Only request 'Materialized' for an entity that actually has a base-view materialization: it resolves to the wrapper view by naming convention with NO existence check or fallback, so requesting it for a non-materialized entity targets a view that does not exist and the query errors.",
413
+ })
414
+ DataSource?: 'Live' | 'Materialized';
394
415
  }
395
416
 
396
417
  @InputType()
@@ -520,6 +541,13 @@ export class RunViewGenericInput {
520
541
  'Optional, when true bypasses ALL server-side caching for this view run — the pre-check cache lookup is skipped and the result is not stored in the cache. Use for maintenance/audit queries that must see true database state, or to force-refresh views whose filters reference rows the server cache invalidator cannot follow (e.g., cross-entity subqueries against vwListDetails).',
521
542
  })
522
543
  BypassCache?: boolean;
544
+
545
+ @Field(() => String, {
546
+ nullable: true,
547
+ description:
548
+ "Optional source-of-truth selector for a base-view materialization: 'Live' (default) reads the entity's live base view; 'Materialized' reads its materialized snapshot wrapper view (materialized_vw<CodeName>). Only request 'Materialized' for an entity that actually has a base-view materialization: it resolves to the wrapper view by naming convention with NO existence check or fallback, so requesting it for a non-materialized entity targets a view that does not exist and the query errors.",
549
+ })
550
+ DataSource?: 'Live' | 'Materialized';
523
551
  }
524
552
 
525
553
  //****************************************************************************
@@ -1082,6 +1110,9 @@ export class RunViewResolver extends ResolverBase {
1082
1110
  // Forward the aggregate request to the engine (B40) — omitted here as well as in the
1083
1111
  // client's input map, so aggregates never reached InternalRunView on this transport.
1084
1112
  Aggregates: item.params.Aggregates,
1113
+ // DataSource must be forwarded on this transport too (same as the InternalRunView/RunViews maps):
1114
+ // otherwise a CacheLocal batch's DataSource:'Materialized' request silently reads the live view.
1115
+ DataSource: item.params.DataSource,
1085
1116
  AfterKey: item.params.AfterKey
1086
1117
  ? CompositeKey.FromKeyValuePairs(item.params.AfterKey.KeyValuePairs)
1087
1118
  : undefined,