@memberjunction/core-entities-server 6.2.0-edge.5 → 6.2.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 (214) hide show
  1. package/README.md +155 -100
  2. package/dist/custom/MJAIAgentExampleEntityServer.server.d.ts +2 -0
  3. package/dist/custom/MJAIAgentExampleEntityServer.server.d.ts.map +1 -1
  4. package/dist/custom/MJAIAgentExampleEntityServer.server.js +8 -5
  5. package/dist/custom/MJAIAgentExampleEntityServer.server.js.map +1 -1
  6. package/dist/custom/MJAIAgentNoteEntityServer.server.d.ts +2 -0
  7. package/dist/custom/MJAIAgentNoteEntityServer.server.d.ts.map +1 -1
  8. package/dist/custom/MJAIAgentNoteEntityServer.server.js +8 -5
  9. package/dist/custom/MJAIAgentNoteEntityServer.server.js.map +1 -1
  10. package/dist/custom/MJAIAgentRelationshipEntityServer.server.d.ts +17 -0
  11. package/dist/custom/MJAIAgentRelationshipEntityServer.server.d.ts.map +1 -0
  12. package/dist/custom/MJAIAgentRelationshipEntityServer.server.js +71 -0
  13. package/dist/custom/MJAIAgentRelationshipEntityServer.server.js.map +1 -0
  14. package/dist/custom/MJAIAgentSessionEntityServer.server.d.ts +8 -39
  15. package/dist/custom/MJAIAgentSessionEntityServer.server.d.ts.map +1 -1
  16. package/dist/custom/MJAIAgentSessionEntityServer.server.js +44 -44
  17. package/dist/custom/MJAIAgentSessionEntityServer.server.js.map +1 -1
  18. package/dist/custom/MJAIAgentUserPolicyEntityServer.server.d.ts +23 -0
  19. package/dist/custom/MJAIAgentUserPolicyEntityServer.server.d.ts.map +1 -0
  20. package/dist/custom/MJAIAgentUserPolicyEntityServer.server.js +53 -0
  21. package/dist/custom/MJAIAgentUserPolicyEntityServer.server.js.map +1 -0
  22. package/dist/custom/MJAIPromptEntityServer.server.d.ts.map +1 -1
  23. package/dist/custom/MJAIPromptEntityServer.server.js +2 -1
  24. package/dist/custom/MJAIPromptEntityServer.server.js.map +1 -1
  25. package/dist/custom/MJBrandKitEntityServer.server.d.ts +14 -0
  26. package/dist/custom/MJBrandKitEntityServer.server.d.ts.map +1 -0
  27. package/dist/custom/MJBrandKitEntityServer.server.js +75 -0
  28. package/dist/custom/MJBrandKitEntityServer.server.js.map +1 -0
  29. package/dist/custom/MJCompanyIntegrationEntityServer.server.d.ts.map +1 -1
  30. package/dist/custom/MJCompanyIntegrationEntityServer.server.js +1 -0
  31. package/dist/custom/MJCompanyIntegrationEntityServer.server.js.map +1 -1
  32. package/dist/custom/MJComponentEntityServer.server.d.ts +11 -0
  33. package/dist/custom/MJComponentEntityServer.server.d.ts.map +1 -1
  34. package/dist/custom/MJComponentEntityServer.server.js +30 -2
  35. package/dist/custom/MJComponentEntityServer.server.js.map +1 -1
  36. package/dist/custom/MJConversationEntityServer.server.d.ts +13 -0
  37. package/dist/custom/MJConversationEntityServer.server.d.ts.map +1 -0
  38. package/dist/custom/MJConversationEntityServer.server.js +33 -0
  39. package/dist/custom/MJConversationEntityServer.server.js.map +1 -0
  40. package/dist/custom/MJDashboardEntityServer.server.d.ts +14 -0
  41. package/dist/custom/MJDashboardEntityServer.server.d.ts.map +1 -0
  42. package/dist/custom/MJDashboardEntityServer.server.js +38 -0
  43. package/dist/custom/MJDashboardEntityServer.server.js.map +1 -0
  44. package/dist/custom/MJDuplicateRunEntityServer.server.d.ts +34 -2
  45. package/dist/custom/MJDuplicateRunEntityServer.server.d.ts.map +1 -1
  46. package/dist/custom/MJDuplicateRunEntityServer.server.js +103 -23
  47. package/dist/custom/MJDuplicateRunEntityServer.server.js.map +1 -1
  48. package/dist/custom/MJEntityAlternateBaseViewEntityServer.server.d.ts +73 -0
  49. package/dist/custom/MJEntityAlternateBaseViewEntityServer.server.d.ts.map +1 -0
  50. package/dist/custom/MJEntityAlternateBaseViewEntityServer.server.js +391 -0
  51. package/dist/custom/MJEntityAlternateBaseViewEntityServer.server.js.map +1 -0
  52. package/dist/custom/MJEntityDocumentEntityServer.server.d.ts.map +1 -1
  53. package/dist/custom/MJEntityDocumentEntityServer.server.js +1 -0
  54. package/dist/custom/MJEntityDocumentEntityServer.server.js.map +1 -1
  55. package/dist/custom/MJEntityFieldPermissionEntityServer.server.d.ts +96 -22
  56. package/dist/custom/MJEntityFieldPermissionEntityServer.server.d.ts.map +1 -1
  57. package/dist/custom/MJEntityFieldPermissionEntityServer.server.js +239 -55
  58. package/dist/custom/MJEntityFieldPermissionEntityServer.server.js.map +1 -1
  59. package/dist/custom/MJListDetailEntityServer.server.d.ts.map +1 -1
  60. package/dist/custom/MJListDetailEntityServer.server.js +1 -0
  61. package/dist/custom/MJListDetailEntityServer.server.js.map +1 -1
  62. package/dist/custom/MJQueryEntityServer.server.d.ts +184 -46
  63. package/dist/custom/MJQueryEntityServer.server.d.ts.map +1 -1
  64. package/dist/custom/MJQueryEntityServer.server.js +505 -159
  65. package/dist/custom/MJQueryEntityServer.server.js.map +1 -1
  66. package/dist/custom/MJQuerySQLEntityServer.server.d.ts +15 -18
  67. package/dist/custom/MJQuerySQLEntityServer.server.d.ts.map +1 -1
  68. package/dist/custom/MJQuerySQLEntityServer.server.js +34 -42
  69. package/dist/custom/MJQuerySQLEntityServer.server.js.map +1 -1
  70. package/dist/custom/MJTagEntityServer.server.d.ts +2 -0
  71. package/dist/custom/MJTagEntityServer.server.d.ts.map +1 -1
  72. package/dist/custom/MJTagEntityServer.server.js +13 -7
  73. package/dist/custom/MJTagEntityServer.server.js.map +1 -1
  74. package/dist/custom/MJUserRoleEntityServer.server.d.ts +74 -7
  75. package/dist/custom/MJUserRoleEntityServer.server.d.ts.map +1 -1
  76. package/dist/custom/MJUserRoleEntityServer.server.js +206 -20
  77. package/dist/custom/MJUserRoleEntityServer.server.js.map +1 -1
  78. package/dist/custom/MJUserRoutineEntityServer.server.d.ts.map +1 -1
  79. package/dist/custom/MJUserRoutineEntityServer.server.js +8 -1
  80. package/dist/custom/MJUserRoutineEntityServer.server.js.map +1 -1
  81. package/dist/custom/MJUserViewEntityServer.server.d.ts +17 -1
  82. package/dist/custom/MJUserViewEntityServer.server.d.ts.map +1 -1
  83. package/dist/custom/MJUserViewEntityServer.server.js +95 -7
  84. package/dist/custom/MJUserViewEntityServer.server.js.map +1 -1
  85. package/dist/custom/MJVectorIndexEntityServer.server.d.ts +2 -0
  86. package/dist/custom/MJVectorIndexEntityServer.server.d.ts.map +1 -1
  87. package/dist/custom/MJVectorIndexEntityServer.server.js +33 -0
  88. package/dist/custom/MJVectorIndexEntityServer.server.js.map +1 -1
  89. package/dist/custom/anonymousOwnershipKeyGuard.d.ts +42 -0
  90. package/dist/custom/anonymousOwnershipKeyGuard.d.ts.map +1 -0
  91. package/dist/custom/anonymousOwnershipKeyGuard.js +36 -0
  92. package/dist/custom/anonymousOwnershipKeyGuard.js.map +1 -0
  93. package/dist/custom/approvalStamp.d.ts +37 -0
  94. package/dist/custom/approvalStamp.d.ts.map +1 -0
  95. package/dist/custom/approvalStamp.js +67 -0
  96. package/dist/custom/approvalStamp.js.map +1 -0
  97. package/dist/custom/fieldPermissionAccess.d.ts +31 -0
  98. package/dist/custom/fieldPermissionAccess.d.ts.map +1 -0
  99. package/dist/custom/fieldPermissionAccess.js +38 -0
  100. package/dist/custom/fieldPermissionAccess.js.map +1 -0
  101. package/dist/custom/fieldPermissionReconciler.d.ts.map +1 -1
  102. package/dist/custom/fieldPermissionReconciler.js +12 -2
  103. package/dist/custom/fieldPermissionReconciler.js.map +1 -1
  104. package/dist/custom/query-extraction/childRowWriter.d.ts +77 -0
  105. package/dist/custom/query-extraction/childRowWriter.d.ts.map +1 -0
  106. package/dist/custom/query-extraction/childRowWriter.js +277 -0
  107. package/dist/custom/query-extraction/childRowWriter.js.map +1 -0
  108. package/dist/custom/query-extraction/compositionTargets.d.ts +40 -0
  109. package/dist/custom/query-extraction/compositionTargets.d.ts.map +1 -0
  110. package/dist/custom/query-extraction/compositionTargets.js +99 -0
  111. package/dist/custom/query-extraction/compositionTargets.js.map +1 -0
  112. package/dist/custom/query-extraction/declared.d.ts +79 -0
  113. package/dist/custom/query-extraction/declared.d.ts.map +1 -0
  114. package/dist/custom/query-extraction/declared.js +91 -0
  115. package/dist/custom/query-extraction/declared.js.map +1 -0
  116. package/dist/custom/query-extraction/describeFacts.d.ts +40 -0
  117. package/dist/custom/query-extraction/describeFacts.d.ts.map +1 -0
  118. package/dist/custom/query-extraction/describeFacts.js +225 -0
  119. package/dist/custom/query-extraction/describeFacts.js.map +1 -0
  120. package/dist/custom/query-extraction/descriptionPass.d.ts +23 -0
  121. package/dist/custom/query-extraction/descriptionPass.d.ts.map +1 -0
  122. package/dist/custom/query-extraction/descriptionPass.js +95 -0
  123. package/dist/custom/query-extraction/descriptionPass.js.map +1 -0
  124. package/dist/custom/query-extraction/extractQuery.d.ts +25 -0
  125. package/dist/custom/query-extraction/extractQuery.d.ts.map +1 -0
  126. package/dist/custom/query-extraction/extractQuery.js +146 -0
  127. package/dist/custom/query-extraction/extractQuery.js.map +1 -0
  128. package/dist/custom/query-extraction/extractionTypes.d.ts +133 -0
  129. package/dist/custom/query-extraction/extractionTypes.d.ts.map +1 -0
  130. package/dist/custom/query-extraction/extractionTypes.js +2 -0
  131. package/dist/custom/query-extraction/extractionTypes.js.map +1 -0
  132. package/dist/custom/query-extraction/index.d.ts +16 -0
  133. package/dist/custom/query-extraction/index.d.ts.map +1 -1
  134. package/dist/custom/query-extraction/index.js +11 -1
  135. package/dist/custom/query-extraction/index.js.map +1 -1
  136. package/dist/custom/query-extraction/pipeline.d.ts +6 -2
  137. package/dist/custom/query-extraction/pipeline.d.ts.map +1 -1
  138. package/dist/custom/query-extraction/pipeline.js +21 -14
  139. package/dist/custom/query-extraction/pipeline.js.map +1 -1
  140. package/dist/custom/query-extraction/resolve.d.ts +7 -2
  141. package/dist/custom/query-extraction/resolve.d.ts.map +1 -1
  142. package/dist/custom/query-extraction/resolve.js +41 -20
  143. package/dist/custom/query-extraction/resolve.js.map +1 -1
  144. package/dist/custom/query-extraction/sourceBinding.d.ts +57 -0
  145. package/dist/custom/query-extraction/sourceBinding.d.ts.map +1 -0
  146. package/dist/custom/query-extraction/sourceBinding.js +104 -0
  147. package/dist/custom/query-extraction/sourceBinding.js.map +1 -0
  148. package/dist/custom/query-extraction/sync.d.ts +28 -4
  149. package/dist/custom/query-extraction/sync.d.ts.map +1 -1
  150. package/dist/custom/query-extraction/sync.js +147 -59
  151. package/dist/custom/query-extraction/sync.js.map +1 -1
  152. package/dist/custom/query-extraction/templateFacts.d.ts +62 -0
  153. package/dist/custom/query-extraction/templateFacts.d.ts.map +1 -0
  154. package/dist/custom/query-extraction/templateFacts.js +229 -0
  155. package/dist/custom/query-extraction/templateFacts.js.map +1 -0
  156. package/dist/custom/query-extraction/types.d.ts +8 -0
  157. package/dist/custom/query-extraction/types.d.ts.map +1 -1
  158. package/dist/custom/query-refresh/QueryExtractionService.d.ts +54 -0
  159. package/dist/custom/query-refresh/QueryExtractionService.d.ts.map +1 -0
  160. package/dist/custom/query-refresh/QueryExtractionService.js +207 -0
  161. package/dist/custom/query-refresh/QueryExtractionService.js.map +1 -0
  162. package/dist/custom/query-refresh/QueryRefreshExtractionServerOperation.d.ts +28 -0
  163. package/dist/custom/query-refresh/QueryRefreshExtractionServerOperation.d.ts.map +1 -0
  164. package/dist/custom/query-refresh/QueryRefreshExtractionServerOperation.js +106 -0
  165. package/dist/custom/query-refresh/QueryRefreshExtractionServerOperation.js.map +1 -0
  166. package/dist/custom/query-refresh/index.d.ts +14 -0
  167. package/dist/custom/query-refresh/index.d.ts.map +1 -0
  168. package/dist/custom/query-refresh/index.js +10 -0
  169. package/dist/custom/query-refresh/index.js.map +1 -0
  170. package/dist/custom/query-refresh/queryRebind.d.ts +33 -0
  171. package/dist/custom/query-refresh/queryRebind.d.ts.map +1 -0
  172. package/dist/custom/query-refresh/queryRebind.js +85 -0
  173. package/dist/custom/query-refresh/queryRebind.js.map +1 -0
  174. package/dist/custom/query-refresh/refreshAuthorization.d.ts +29 -0
  175. package/dist/custom/query-refresh/refreshAuthorization.d.ts.map +1 -0
  176. package/dist/custom/query-refresh/refreshAuthorization.js +66 -0
  177. package/dist/custom/query-refresh/refreshAuthorization.js.map +1 -0
  178. package/dist/custom/query-refresh/refreshFormat.d.ts +16 -0
  179. package/dist/custom/query-refresh/refreshFormat.d.ts.map +1 -0
  180. package/dist/custom/query-refresh/refreshFormat.js +67 -0
  181. package/dist/custom/query-refresh/refreshFormat.js.map +1 -0
  182. package/dist/custom/query-refresh/refreshOrder.d.ts +25 -0
  183. package/dist/custom/query-refresh/refreshOrder.d.ts.map +1 -0
  184. package/dist/custom/query-refresh/refreshOrder.js +95 -0
  185. package/dist/custom/query-refresh/refreshOrder.js.map +1 -0
  186. package/dist/custom/query-refresh/refreshSelection.d.ts +15 -0
  187. package/dist/custom/query-refresh/refreshSelection.d.ts.map +1 -0
  188. package/dist/custom/query-refresh/refreshSelection.js +99 -0
  189. package/dist/custom/query-refresh/refreshSelection.js.map +1 -0
  190. package/dist/custom/query-refresh/refreshTypes.d.ts +81 -0
  191. package/dist/custom/query-refresh/refreshTypes.d.ts.map +1 -0
  192. package/dist/custom/query-refresh/refreshTypes.js +5 -0
  193. package/dist/custom/query-refresh/refreshTypes.js.map +1 -0
  194. package/dist/custom/systemUserGuardState.d.ts +73 -0
  195. package/dist/custom/systemUserGuardState.d.ts.map +1 -0
  196. package/dist/custom/systemUserGuardState.js +161 -0
  197. package/dist/custom/systemUserGuardState.js.map +1 -0
  198. package/dist/custom/template-extraction/parser.js +5 -2
  199. package/dist/custom/template-extraction/parser.js.map +1 -1
  200. package/dist/custom/template-extraction/types.d.ts +1 -1
  201. package/dist/custom/template-extraction/types.d.ts.map +1 -1
  202. package/dist/custom/util.d.ts +8 -0
  203. package/dist/custom/util.d.ts.map +1 -1
  204. package/dist/custom/util.js +18 -1
  205. package/dist/custom/util.js.map +1 -1
  206. package/dist/engines/QueryEngineServer.d.ts +4 -1
  207. package/dist/engines/QueryEngineServer.d.ts.map +1 -1
  208. package/dist/engines/QueryEngineServer.js +10 -7
  209. package/dist/engines/QueryEngineServer.js.map +1 -1
  210. package/dist/index.d.ts +9 -0
  211. package/dist/index.d.ts.map +1 -1
  212. package/dist/index.js +9 -0
  213. package/dist/index.js.map +1 -1
  214. package/package.json +30 -30
@@ -0,0 +1,66 @@
1
+ import { NormalizeUUID } from "@memberjunction/global";
2
+ /** The child entities a refresh creates, updates and deletes rows of. */
3
+ export const QUERY_REFRESH_CHILD_ENTITIES = [
4
+ 'MJ: Query Fields',
5
+ 'MJ: Query Parameters',
6
+ 'MJ: Query Entities',
7
+ 'MJ: Query Dependencies',
8
+ ];
9
+ /**
10
+ * Whether `user` may refresh queries at all: Update on `MJ: Queries`, and Create, Update and
11
+ * Delete on each of its derived child entities. A dry run needs the same, since its report
12
+ * discloses the same metadata. Run permissions (`MJ: Query Permissions`) play no part: refreshing
13
+ * is an authoring act.
14
+ */
15
+ export function UserCanRefreshQueries(user, entities) {
16
+ const byName = new Map(entities.map(e => [e.Name.toLowerCase(), e]));
17
+ const query = byName.get('mj: queries');
18
+ if (!query || !query.GetUserPermisions(user)?.CanUpdate)
19
+ return false;
20
+ return QUERY_REFRESH_CHILD_ENTITIES.every(name => {
21
+ const entity = byName.get(name.toLowerCase());
22
+ if (!entity)
23
+ return false;
24
+ const permissions = entity.GetUserPermisions(user);
25
+ return !!permissions && permissions.CanCreate && permissions.CanUpdate && permissions.CanDelete;
26
+ });
27
+ }
28
+ /**
29
+ * Whether `user` may see what a refresh of one query reveals: the column names, types and
30
+ * sources of every entity the query reads. True only when the user can read every one of those
31
+ * entities and is denied no field on any of them.
32
+ *
33
+ * @param entityIDs - The entities the query reads: its stored links and field sources, and the ones
34
+ * the refresh found ({@link EntityIDsOfExtraction}).
35
+ */
36
+ export function UserCanSeeQuerySources(user, entityIDs, entities) {
37
+ const byID = new Map(entities.map(e => [NormalizeUUID(e.ID), e]));
38
+ for (const id of entityIDs) {
39
+ const entity = byID.get(NormalizeUUID(id));
40
+ if (!entity)
41
+ return false;
42
+ if (!entity.GetUserPermisions(user)?.CanRead)
43
+ return false;
44
+ if (entity.FieldLevelSecurityApplies && entity.GetDeniedReadFields(user).size > 0)
45
+ return false;
46
+ }
47
+ return true;
48
+ }
49
+ /** The entities a refresh's facts link the query to: the entities it reads and its fields' sources. */
50
+ export function EntityIDsOfExtraction(extraction) {
51
+ const ids = (extraction.Entities ?? []).map(e => e.EntityID);
52
+ for (const field of extraction.Fields ?? []) {
53
+ if (field.SourceEntityID)
54
+ ids.push(field.SourceEntityID);
55
+ }
56
+ return ids;
57
+ }
58
+ /**
59
+ * The message for a refresh of a query that does not exist or that the caller may not refresh.
60
+ * The two are worded identically, so a refusal does not confirm that the query, or anything it
61
+ * reads, exists. It echoes only the identifier the caller supplied.
62
+ */
63
+ export function QueryRefreshUnavailableMessage(label) {
64
+ return `Query '${label}' does not exist or you do not have permission to refresh it.`;
65
+ }
66
+ //# sourceMappingURL=refreshAuthorization.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refreshAuthorization.js","sourceRoot":"","sources":["../../../src/custom/query-refresh/refreshAuthorization.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAGvD,yEAAyE;AACzE,MAAM,CAAC,MAAM,4BAA4B,GAAsB;IAC3D,kBAAkB;IAClB,sBAAsB;IACtB,oBAAoB;IACpB,wBAAwB;CAC3B,CAAC;AAEF;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CAAC,IAAc,EAAE,QAA+B;IACjF,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IACrE,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;IACxC,IAAI,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,IAAI,CAAC,EAAE,SAAS;QAAE,OAAO,KAAK,CAAC;IACtE,OAAO,4BAA4B,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE;QAC7C,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;QAC9C,IAAI,CAAC,MAAM;YAAE,OAAO,KAAK,CAAC;QAC1B,MAAM,WAAW,GAAG,MAAM,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;QACnD,OAAO,CAAC,CAAC,WAAW,IAAI,WAAW,CAAC,SAAS,IAAI,WAAW,CAAC,SAAS,IAAI,WAAW,CAAC,SAAS,CAAC;IACpG,CAAC,CAAC,CAAC;AACP,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,sBAAsB,CAAC,IAAc,EAAE,SAA2B,EAAE,QAA+B;IAC/G,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAClE,KAAK,MAAM,EAAE,IAAI,SAAS,EAAE,CAAC;QACzB,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC,CAAC;QAC3C,IAAI,CAAC,MAAM;YAAE,OAAO,KAAK,CAAC;QAC1B,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC,IAAI,CAAC,EAAE,OAAO;YAAE,OAAO,KAAK,CAAC;QAC3D,IAAI,MAAM,CAAC,yBAAyB,IAAI,MAAM,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC,IAAI,GAAG,CAAC;YAAE,OAAO,KAAK,CAAC;IACpG,CAAC;IACD,OAAO,IAAI,CAAC;AAChB,CAAC;AAED,uGAAuG;AACvG,MAAM,UAAU,qBAAqB,CAAC,UAAiC;IACnE,MAAM,GAAG,GAAG,CAAC,UAAU,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;IAC7D,KAAK,MAAM,KAAK,IAAI,UAAU,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC;QAC1C,IAAI,KAAK,CAAC,cAAc;YAAE,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;IAC7D,CAAC;IACD,OAAO,GAAG,CAAC;AACf,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,8BAA8B,CAAC,KAAa;IACxD,OAAO,UAAU,KAAK,+DAA+D,CAAC;AAC1F,CAAC"}
@@ -0,0 +1,16 @@
1
+ import type { QueryRefreshOutcome, QueryRefreshReport, QueryRefreshRunReport } from "./refreshTypes.js";
2
+ /** How many queries of a run ended in each outcome. */
3
+ export type QueryRefreshTotals = Record<QueryRefreshOutcome, number>;
4
+ /** The number of reports per outcome. */
5
+ export declare function QueryRefreshTotalsOf(run: QueryRefreshRunReport): QueryRefreshTotals;
6
+ /** One line summing a run up: `12 refreshed, 19 unchanged, 0 failed (dry run)`. */
7
+ export declare function FormatQueryRefreshSummary(run: QueryRefreshRunReport): string;
8
+ /**
9
+ * The lines of a run's report: one heading per query, then its row changes, flagged parameters
10
+ * and diagnostics, then any composition cycles and the summary. With `changedOnly`, unchanged
11
+ * queries with no diagnostics are left out.
12
+ */
13
+ export declare function FormatQueryRefreshRun(run: QueryRefreshRunReport, changedOnly?: boolean): string[];
14
+ /** The lines of one query's report. */
15
+ export declare function FormatQueryRefreshReport(report: QueryRefreshReport): string[];
16
+ //# sourceMappingURL=refreshFormat.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refreshFormat.d.ts","sourceRoot":"","sources":["../../../src/custom/query-refresh/refreshFormat.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAErG,uDAAuD;AACvD,MAAM,MAAM,kBAAkB,GAAG,MAAM,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAAC;AAErE,yCAAyC;AACzC,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,qBAAqB,GAAG,kBAAkB,CAInF;AAED,mFAAmF;AACnF,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,qBAAqB,GAAG,MAAM,CAS5E;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,qBAAqB,EAAE,WAAW,UAAQ,GAAG,MAAM,EAAE,CAS/F;AAED,uCAAuC;AACvC,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM,EAAE,CAW7E"}
@@ -0,0 +1,67 @@
1
+ /** The number of reports per outcome. */
2
+ export function QueryRefreshTotalsOf(run) {
3
+ const totals = { Refreshed: 0, Unchanged: 0, NotPermitted: 0, Unavailable: 0, Failed: 0 };
4
+ for (const report of run.Reports)
5
+ totals[report.Outcome]++;
6
+ return totals;
7
+ }
8
+ /** One line summing a run up: `12 refreshed, 19 unchanged, 0 failed (dry run)`. */
9
+ export function FormatQueryRefreshSummary(run) {
10
+ const t = QueryRefreshTotalsOf(run);
11
+ const verb = run.DryRun ? 'would be refreshed' : 'refreshed';
12
+ const parts = [`${t.Refreshed} ${verb}`, `${t.Unchanged} unchanged`, `${t.Failed} failed`];
13
+ if (t.NotPermitted > 0)
14
+ parts.push(`${t.NotPermitted} skipped (not permitted)`);
15
+ if (t.Unavailable > 0)
16
+ parts.push(`${t.Unavailable} unavailable`);
17
+ const withErrors = run.Reports.filter(r => r.ExtractionStatus === 'Error').length;
18
+ if (withErrors > 0)
19
+ parts.push(`${withErrors} with extraction errors`);
20
+ return `${run.Reports.length} queries: ${parts.join(', ')}${run.DryRun ? ' (dry run: nothing was written)' : ''}.`;
21
+ }
22
+ /**
23
+ * The lines of a run's report: one heading per query, then its row changes, flagged parameters
24
+ * and diagnostics, then any composition cycles and the summary. With `changedOnly`, unchanged
25
+ * queries with no diagnostics are left out.
26
+ */
27
+ export function FormatQueryRefreshRun(run, changedOnly = false) {
28
+ const lines = [];
29
+ for (const report of run.Reports) {
30
+ if (changedOnly && report.Outcome === 'Unchanged' && report.Diagnostics.length === 0)
31
+ continue;
32
+ lines.push(...FormatQueryRefreshReport(report));
33
+ }
34
+ for (const cycle of run.Cycles)
35
+ lines.push(`Composition cycle (each refreshed once): ${cycle.join(' -> ')}`);
36
+ lines.push(FormatQueryRefreshSummary(run));
37
+ return lines;
38
+ }
39
+ /** The lines of one query's report. */
40
+ export function FormatQueryRefreshReport(report) {
41
+ const name = report.QueryName ? `"${report.QueryName}" (${report.QueryID})` : report.QueryID;
42
+ const status = report.ExtractionStatus ? ` [${report.ExtractionStatus}]` : '';
43
+ const lines = [`${outcomeLabel(report)} ${name}${status}`];
44
+ if (report.QueryColumns.length > 0)
45
+ lines.push(` ~ query (${report.QueryColumns.join(', ')})`);
46
+ for (const change of report.Added)
47
+ lines.push(` + ${changeLabel(change)}`);
48
+ for (const change of report.Updated)
49
+ lines.push(` ~ ${changeLabel(change)}${change.ChangedColumns.length > 0 ? ` (${change.ChangedColumns.join(', ')})` : ''}`);
50
+ for (const change of report.Removed)
51
+ lines.push(` - ${changeLabel(change)}`);
52
+ for (const name of report.Flagged)
53
+ lines.push(` ! parameter ${name}: authored, no longer used by the SQL (kept)`);
54
+ for (const line of report.Diagnostics)
55
+ lines.push(` ${line}`);
56
+ return lines;
57
+ }
58
+ function outcomeLabel(report) {
59
+ if (report.Outcome === 'Refreshed')
60
+ return report.DryRun ? 'WOULD REFRESH' : 'REFRESHED';
61
+ return report.Outcome.toUpperCase();
62
+ }
63
+ function changeLabel(change) {
64
+ const kind = { Fields: 'field', Parameters: 'parameter', Entities: 'entity', Dependencies: 'dependency' }[change.Collection];
65
+ return `${kind} ${change.Name}`;
66
+ }
67
+ //# sourceMappingURL=refreshFormat.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refreshFormat.js","sourceRoot":"","sources":["../../../src/custom/query-refresh/refreshFormat.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /** A query to order, by ID, with a name for reporting. */
2
+ export interface OrderableQuery {
3
+ readonly ID: string;
4
+ readonly Name: string;
5
+ }
6
+ /** A composition edge: `QueryID` composes `DependsOnQueryID`. */
7
+ export interface CompositionEdge {
8
+ readonly QueryID: string;
9
+ readonly DependsOnQueryID: string;
10
+ }
11
+ /** The order to refresh in, and the composition cycles found. */
12
+ export interface RefreshOrder {
13
+ readonly Order: readonly OrderableQuery[];
14
+ /** Each cycle's members, by name. */
15
+ readonly Cycles: readonly (readonly string[])[];
16
+ }
17
+ /**
18
+ * Orders `queries` so each comes after every selected query it composes (dependencies first),
19
+ * keeping the given order among queries that do not depend on each other. Queries in a
20
+ * composition cycle (stored by an older version that allowed one), and those that depend on
21
+ * them, come last in their given order; each is listed once, so a cycle is never followed round.
22
+ * Edges to queries outside the selection are ignored.
23
+ */
24
+ export declare function OrderForRefresh(queries: readonly OrderableQuery[], edges: readonly CompositionEdge[]): RefreshOrder;
25
+ //# sourceMappingURL=refreshOrder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refreshOrder.d.ts","sourceRoot":"","sources":["../../../src/custom/query-refresh/refreshOrder.ts"],"names":[],"mappings":"AAEA,0DAA0D;AAC1D,MAAM,WAAW,cAAc;IAC3B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACzB;AAED,iEAAiE;AACjE,MAAM,WAAW,eAAe;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;CACrC;AAED,iEAAiE;AACjE,MAAM,WAAW,YAAY;IACzB,QAAQ,CAAC,KAAK,EAAE,SAAS,cAAc,EAAE,CAAC;IAC1C,qCAAqC;IACrC,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,CAAC;CACnD;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,SAAS,cAAc,EAAE,EAAE,KAAK,EAAE,SAAS,eAAe,EAAE,GAAG,YAAY,CAmBnH"}
@@ -0,0 +1,95 @@
1
+ import { NormalizeUUID } from "@memberjunction/global";
2
+ /**
3
+ * Orders `queries` so each comes after every selected query it composes (dependencies first),
4
+ * keeping the given order among queries that do not depend on each other. Queries in a
5
+ * composition cycle (stored by an older version that allowed one), and those that depend on
6
+ * them, come last in their given order; each is listed once, so a cycle is never followed round.
7
+ * Edges to queries outside the selection are ignored.
8
+ */
9
+ export function OrderForRefresh(queries, edges) {
10
+ const ids = queries.map(q => NormalizeUUID(q.ID));
11
+ const selected = new Set(ids);
12
+ const dependsOn = new Map(ids.map(id => [id, new Set()]));
13
+ for (const edge of edges) {
14
+ const from = NormalizeUUID(edge.QueryID);
15
+ const to = NormalizeUUID(edge.DependsOnQueryID);
16
+ if (selected.has(from) && selected.has(to) && from !== to)
17
+ dependsOn.get(from)?.add(to);
18
+ if (from === to && selected.has(from))
19
+ dependsOn.get(from)?.add(to);
20
+ }
21
+ const ordered = topologicalOrder(ids, dependsOn);
22
+ const placed = new Set(ordered);
23
+ const rest = ids.filter(id => !placed.has(id));
24
+ const byID = new Map(queries.map(q => [NormalizeUUID(q.ID), q]));
25
+ const nameOf = (id) => byID.get(id)?.Name ?? id;
26
+ return {
27
+ Order: [...ordered, ...rest].map(id => byID.get(id)).filter((q) => q !== undefined),
28
+ Cycles: findCycles(rest, dependsOn).map(cycle => cycle.map(nameOf)),
29
+ };
30
+ }
31
+ /** Kahn's algorithm, stable in the given order; nodes on or behind a cycle are left out. */
32
+ function topologicalOrder(ids, dependsOn) {
33
+ const done = new Set();
34
+ const ordered = [];
35
+ let progressed = true;
36
+ while (progressed) {
37
+ progressed = false;
38
+ for (const id of ids) {
39
+ if (done.has(id))
40
+ continue;
41
+ const deps = dependsOn.get(id) ?? new Set();
42
+ if ([...deps].every(d => done.has(d))) {
43
+ done.add(id);
44
+ ordered.push(id);
45
+ progressed = true;
46
+ }
47
+ }
48
+ }
49
+ return ordered;
50
+ }
51
+ /** The strongly connected components among `ids` that are cycles (more than one member, or a self-reference). */
52
+ function findCycles(ids, dependsOn) {
53
+ const within = new Set(ids);
54
+ const index = new Map();
55
+ const low = new Map();
56
+ const stack = [];
57
+ const onStack = new Set();
58
+ const cycles = [];
59
+ let counter = 0;
60
+ const visit = (id) => {
61
+ index.set(id, counter);
62
+ low.set(id, counter++);
63
+ stack.push(id);
64
+ onStack.add(id);
65
+ for (const dep of dependsOn.get(id) ?? []) {
66
+ if (!within.has(dep))
67
+ continue;
68
+ if (!index.has(dep)) {
69
+ visit(dep);
70
+ low.set(id, Math.min(low.get(id) ?? 0, low.get(dep) ?? 0));
71
+ }
72
+ else if (onStack.has(dep)) {
73
+ low.set(id, Math.min(low.get(id) ?? 0, index.get(dep) ?? 0));
74
+ }
75
+ }
76
+ if (low.get(id) !== index.get(id))
77
+ return;
78
+ const component = [];
79
+ let member;
80
+ do {
81
+ member = stack.pop();
82
+ if (member === undefined)
83
+ break;
84
+ onStack.delete(member);
85
+ component.push(member);
86
+ } while (member !== id);
87
+ if (component.length > 1 || dependsOn.get(id)?.has(id))
88
+ cycles.push(component.reverse());
89
+ };
90
+ for (const id of ids)
91
+ if (!index.has(id))
92
+ visit(id);
93
+ return cycles;
94
+ }
95
+ //# sourceMappingURL=refreshOrder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refreshOrder.js","sourceRoot":"","sources":["../../../src/custom/query-refresh/refreshOrder.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { CompositionEdge, OrderableQuery } from "./refreshOrder.js";
2
+ import type { QueryRefreshContext, QueryRefreshFilter } from "./refreshTypes.js";
3
+ /** The queries a filter selects, the selectors that matched nothing, and the composition edges among them. */
4
+ export interface RefreshSelection {
5
+ readonly Queries: readonly OrderableQuery[];
6
+ /** Each ID or name the filter gave that matched no query the caller can read, as given. */
7
+ readonly Unmatched: readonly string[];
8
+ readonly Edges: readonly CompositionEdge[];
9
+ }
10
+ /**
11
+ * The queries `filter` selects, read as the caller from the database (never a cache), so a
12
+ * caller only ever selects queries it can read. Selectors combine as a union.
13
+ */
14
+ export declare function SelectQueriesForRefresh(context: QueryRefreshContext, filter: QueryRefreshFilter): Promise<RefreshSelection>;
15
+ //# sourceMappingURL=refreshSelection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refreshSelection.d.ts","sourceRoot":"","sources":["../../../src/custom/query-refresh/refreshSelection.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACtE,OAAO,KAAK,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAoB9E,8GAA8G;AAC9G,MAAM,WAAW,gBAAgB;IAC7B,QAAQ,CAAC,OAAO,EAAE,SAAS,cAAc,EAAE,CAAC;IAC5C,2FAA2F;IAC3F,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,QAAQ,CAAC,KAAK,EAAE,SAAS,eAAe,EAAE,CAAC;CAC9C;AAED;;;GAGG;AACH,wBAAsB,uBAAuB,CAAC,OAAO,EAAE,mBAAmB,EAAE,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAiBjI"}
@@ -0,0 +1,99 @@
1
+ import { NormalizeUUID } from "@memberjunction/global";
2
+ /**
3
+ * The queries `filter` selects, read as the caller from the database (never a cache), so a
4
+ * caller only ever selects queries it can read. Selectors combine as a union.
5
+ */
6
+ export async function SelectQueriesForRefresh(context, filter) {
7
+ const entityIDs = entityIDsOf(context, filter);
8
+ const [queries, categories, links, edges] = await readAll(context, entityIDs.length > 0);
9
+ const paths = categoryPaths(categories);
10
+ const selected = new Map();
11
+ const unmatched = [];
12
+ const add = (rows) => rows.forEach(r => selected.set(NormalizeUUID(r.ID), r));
13
+ if (filter.All)
14
+ add(queries);
15
+ for (const id of filter.QueryIDs ?? [])
16
+ collect(queries.filter(q => NormalizeUUID(q.ID) === NormalizeUUID(id)), id, add, unmatched);
17
+ for (const name of filter.QueryNames ?? [])
18
+ collect(queries.filter(q => nameMatches(q, name, paths)), name, add, unmatched);
19
+ if (filter.CategoryPath)
20
+ add(queries.filter(q => inCategory(q, filter.CategoryPath ?? '', paths)));
21
+ if (entityIDs.length > 0) {
22
+ const wanted = new Set(entityIDs.map(NormalizeUUID));
23
+ const reading = new Set(links.filter(l => wanted.has(NormalizeUUID(l.EntityID))).map(l => NormalizeUUID(l.QueryID)));
24
+ add(queries.filter(q => reading.has(NormalizeUUID(q.ID))));
25
+ }
26
+ return { Queries: [...selected.values()].map(q => ({ ID: q.ID, Name: q.Name })), Unmatched: unmatched, Edges: edges };
27
+ }
28
+ function collect(rows, given, add, unmatched) {
29
+ if (rows.length === 0)
30
+ unmatched.push(given);
31
+ else
32
+ add(rows);
33
+ }
34
+ /** The entity IDs the filter names, by ID or by name (names the catalog does not hold select nothing). */
35
+ function entityIDsOf(context, filter) {
36
+ const ids = [...(filter.EntityIDs ?? [])];
37
+ for (const name of filter.EntityNames ?? []) {
38
+ const entity = context.Provider.Entities.find(e => e.Name.trim().toLowerCase() === name.trim().toLowerCase());
39
+ if (entity)
40
+ ids.push(entity.ID);
41
+ }
42
+ return ids;
43
+ }
44
+ async function readAll(context, withLinks) {
45
+ const views = [
46
+ { EntityName: 'MJ: Queries', Fields: ['ID', 'Name', 'CategoryID'], OrderBy: 'Name', ResultType: 'simple', BypassCache: true, IgnoreMaxRows: true },
47
+ { EntityName: 'MJ: Query Categories', Fields: ['ID', 'Name', 'ParentID'], ResultType: 'simple', BypassCache: true, IgnoreMaxRows: true },
48
+ { EntityName: 'MJ: Query Dependencies', Fields: ['QueryID', 'DependsOnQueryID'], ResultType: 'simple', BypassCache: true, IgnoreMaxRows: true },
49
+ ];
50
+ if (withLinks)
51
+ views.push({ EntityName: 'MJ: Query Entities', Fields: ['QueryID', 'EntityID'], ResultType: 'simple', BypassCache: true, IgnoreMaxRows: true });
52
+ const results = await context.Provider.RunViews(views, context.User);
53
+ const failed = results.find(r => !r?.Success);
54
+ if (failed)
55
+ throw new Error(`the queries to refresh could not be read: ${failed.ErrorMessage}`);
56
+ const rows = (i) => (results[i]?.Results ?? []);
57
+ return [rows(0), rows(1), withLinks ? rows(3) : [], rows(2)];
58
+ }
59
+ /** Each category's full path (`Parent/Child`), lower-cased, by normalized ID. */
60
+ function categoryPaths(categories) {
61
+ const byID = new Map(categories.map(c => [NormalizeUUID(c.ID), c]));
62
+ const paths = new Map();
63
+ const pathOf = (id, depth) => {
64
+ const known = paths.get(id);
65
+ if (known !== undefined)
66
+ return known;
67
+ const category = byID.get(id);
68
+ if (!category)
69
+ return '';
70
+ const parent = category.ParentID && depth < 64 ? pathOf(NormalizeUUID(category.ParentID), depth + 1) : '';
71
+ const path = (parent ? `${parent}/${category.Name.trim()}` : category.Name.trim()).toLowerCase();
72
+ paths.set(id, path);
73
+ return path;
74
+ };
75
+ for (const id of byID.keys())
76
+ pathOf(id, 0);
77
+ return paths;
78
+ }
79
+ function normalizePath(path) {
80
+ return path.split('/').map(s => s.trim()).filter(s => s.length > 0).join('/').toLowerCase();
81
+ }
82
+ function pathOfQuery(query, paths) {
83
+ return query.CategoryID ? paths.get(NormalizeUUID(query.CategoryID)) ?? '' : '';
84
+ }
85
+ /** A bare name matches every query with that name; a path matches the query at that full path. */
86
+ function nameMatches(query, given, paths) {
87
+ const wanted = normalizePath(given);
88
+ const name = query.Name.trim().toLowerCase();
89
+ if (!given.includes('/'))
90
+ return name === wanted;
91
+ const category = pathOfQuery(query, paths);
92
+ return (category ? `${category}/${name}` : name) === wanted;
93
+ }
94
+ function inCategory(query, categoryPath, paths) {
95
+ const wanted = normalizePath(categoryPath);
96
+ const category = pathOfQuery(query, paths);
97
+ return wanted.length > 0 && (category === wanted || category.startsWith(`${wanted}/`));
98
+ }
99
+ //# sourceMappingURL=refreshSelection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refreshSelection.js","sourceRoot":"","sources":["../../../src/custom/query-refresh/refreshSelection.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,81 @@
1
+ import type { IMetadataProvider, IRunViewProvider, UserInfo } from "@memberjunction/core";
2
+ import type { ChildRowChange, QueryExtractionStatus } from "../query-extraction/index.js";
3
+ /** The provider a refresh reads and writes through: the caller's, so its transaction and permissions apply. */
4
+ export type QueryRefreshProvider = IMetadataProvider & IRunViewProvider;
5
+ /**
6
+ * Which saved queries a bulk refresh covers. Selectors combine as a union; `All` selects every
7
+ * query the caller can read. A refresh with no selector refreshes nothing.
8
+ */
9
+ export interface QueryRefreshFilter {
10
+ /** Query IDs. */
11
+ readonly QueryIDs?: readonly string[];
12
+ /** Query names, or full paths (`Category/Sub/Name`); a bare name selects every query with that name. */
13
+ readonly QueryNames?: readonly string[];
14
+ /** A category path (`Category/Sub`): the queries in that category and every category below it. */
15
+ readonly CategoryPath?: string;
16
+ /** Entity names: the queries that read any of these entities (their `MJ: Query Entities` rows). */
17
+ readonly EntityNames?: readonly string[];
18
+ /** Entity IDs, as {@link EntityNames}. */
19
+ readonly EntityIDs?: readonly string[];
20
+ /** Every query. */
21
+ readonly All?: boolean;
22
+ }
23
+ /** How a refresh runs. */
24
+ export interface QueryRefreshOptions {
25
+ /** Work out and report the changes without writing anything. */
26
+ readonly DryRun?: boolean;
27
+ /** Queue the LLM description pass for rows still without a description (off by default). */
28
+ readonly WithAI?: boolean;
29
+ /** Called once per query as a bulk refresh finishes it. */
30
+ readonly OnProgress?: (progress: QueryRefreshProgress) => void;
31
+ }
32
+ /** A bulk refresh's progress after one query. */
33
+ export interface QueryRefreshProgress {
34
+ readonly Processed: number;
35
+ readonly Total: number;
36
+ readonly Report: QueryRefreshReport;
37
+ }
38
+ /**
39
+ * What happened to one query:
40
+ * - `Refreshed`: the child rows or the query's extraction columns changed (with `DryRun`: would change);
41
+ * - `Unchanged`: the stored rows already match the SQL;
42
+ * - `NotPermitted`: a bulk refresh skipped a query the caller may not refresh (no reason is given);
43
+ * - `Unavailable`: the query does not exist or the caller may not refresh it (the two read alike);
44
+ * - `Failed`: the refresh could not be completed, and nothing was written.
45
+ */
46
+ export type QueryRefreshOutcome = 'Refreshed' | 'Unchanged' | 'NotPermitted' | 'Unavailable' | 'Failed';
47
+ /** One query's refresh. */
48
+ export interface QueryRefreshReport {
49
+ /** The query's ID; for `Unavailable`, the identifier the caller supplied. */
50
+ readonly QueryID: string;
51
+ /** The query's name; empty for `Unavailable`. */
52
+ readonly QueryName: string;
53
+ readonly Outcome: QueryRefreshOutcome;
54
+ readonly DryRun: boolean;
55
+ /** The extraction status the run records (or, with `DryRun`, would record); `null` when it did not run. */
56
+ readonly ExtractionStatus: QueryExtractionStatus | null;
57
+ /** The columns of the query row itself the run changes (`UsesTemplate`, `ExtractedSQLHash`, `ExtractionStatus`, `ExtractionMessage`). */
58
+ readonly QueryColumns: readonly string[];
59
+ readonly Added: readonly ChildRowChange[];
60
+ readonly Updated: readonly ChildRowChange[];
61
+ readonly Removed: readonly ChildRowChange[];
62
+ /** Authored parameters the SQL no longer uses: kept, and flagged on the query. */
63
+ readonly Flagged: readonly string[];
64
+ /** The run's diagnostics (`ExtractionMessage` lines), or why it was not completed. */
65
+ readonly Diagnostics: readonly string[];
66
+ }
67
+ /** A bulk refresh: one report per query, in the order the queries were refreshed. */
68
+ export interface QueryRefreshRunReport {
69
+ readonly DryRun: boolean;
70
+ readonly Reports: readonly QueryRefreshReport[];
71
+ /** Queries that compose each other in a cycle, by name; each was refreshed once, after the rest. */
72
+ readonly Cycles: readonly (readonly string[])[];
73
+ }
74
+ /** Who a refresh runs as and through which provider. */
75
+ export interface QueryRefreshContext {
76
+ readonly Provider: QueryRefreshProvider;
77
+ readonly User: UserInfo;
78
+ }
79
+ /** Whether `provider` can read views and create entity objects, which is everything a refresh needs. */
80
+ export declare function IsQueryRefreshProvider(provider: IMetadataProvider): provider is QueryRefreshProvider;
81
+ //# sourceMappingURL=refreshTypes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refreshTypes.d.ts","sourceRoot":"","sources":["../../../src/custom/query-refresh/refreshTypes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /** Whether `provider` can read views and create entity objects, which is everything a refresh needs. */
2
+ export function IsQueryRefreshProvider(provider) {
3
+ return typeof Reflect.get(provider, 'RunViews') === 'function' && typeof Reflect.get(provider, 'RunView') === 'function';
4
+ }
5
+ //# sourceMappingURL=refreshTypes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refreshTypes.js","sourceRoot":"","sources":["../../../src/custom/query-refresh/refreshTypes.ts"],"names":[],"mappings":"AAuFA,wGAAwG;AACxG,MAAM,UAAU,sBAAsB,CAAC,QAA2B;IAC9D,OAAO,OAAO,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,UAAU,CAAC,KAAK,UAAU,IAAI,OAAO,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,SAAS,CAAC,KAAK,UAAU,CAAC;AAC7H,CAAC"}
@@ -0,0 +1,73 @@
1
+ /**
2
+ * @fileoverview What the system-user field-access guards read from the DATABASE, and the rule that
3
+ * keeps two guarded writes out of one transaction group.
4
+ *
5
+ * The guards on `MJ: User Roles` and `MJ: Entity Field Permissions` judge one write against the
6
+ * state before it. Two cached inputs can make that state stale:
7
+ *
8
+ * - the system user's roles in `UserCache`, which refreshes on a debounce after a role save;
9
+ * - field rules on loaded metadata, which refreshes after the rule save.
10
+ *
11
+ * Two quick saves (assign a role to the system user, then put a `Deny` on that role) each passed
12
+ * against the cached state the other had not reached yet. So the guards read the system user's
13
+ * roles and the relevant rules here, straight from the database, with the cache bypassed. Inside a
14
+ * database transaction those reads see the transaction's own earlier writes.
15
+ *
16
+ * A transaction group is different: its items are checked when they enrol and written together at
17
+ * submit, so no read can see the group's other items. Two guarded writes in one group that concern
18
+ * the system user are therefore refused ({@link GuardedTransactionGroupRefusal}); each one saved on
19
+ * its own is checked against the state the other left.
20
+ */
21
+ import { IMetadataProvider, IRunViewProvider, UserInfo } from '@memberjunction/core';
22
+ import { SystemUserFieldAccessViolation } from '@memberjunction/generic-database-provider';
23
+ /** The cached system user, or null when the cache is cold. */
24
+ export declare function CachedSystemUser(): UserInfo | null;
25
+ /** Whether `userID` names the system user. False on a cold cache or a missing ID. */
26
+ export declare function IsSystemUserID(userID: string | null | undefined): boolean;
27
+ /**
28
+ * The system user with its role set as stored in the database now, not as cached. Null when no
29
+ * system user resolves (a cold cache, where the guards stand down and the startup sweep is the
30
+ * backstop). Throws when the read fails, so a guard can refuse rather than permit unchecked.
31
+ */
32
+ export declare function LoadStoredSystemUser(runViewProvider: IRunViewProvider | null | undefined): Promise<UserInfo | null>;
33
+ /** Whether `user` holds `roleID`. False for a null user or a missing role. */
34
+ export declare function UserHoldsRole(user: UserInfo | null, roleID: string | null | undefined): boolean;
35
+ /**
36
+ * Every field the system user would lose by giving up `roleID`, judged on the role set and the
37
+ * rules as stored now. Empty when no system user resolves. Throws when a read fails.
38
+ */
39
+ export declare function StoredRoleRemovalViolations(runViewProvider: IRunViewProvider | null | undefined, provider: IMetadataProvider, roleID: string): Promise<SystemUserFieldAccessViolation[]>;
40
+ /**
41
+ * The `EntityFieldID`s on which `roleID` carries a restricting rule (a `Deny` on any verb, any
42
+ * spelling), as stored now. Throws when the read fails.
43
+ */
44
+ export declare function StoredRestrictingFieldIDsForRole(runViewProvider: IRunViewProvider | null | undefined, roleID: string): Promise<string[]>;
45
+ /** A guarded write's place in its transaction group, reserved when it passed the group check. */
46
+ export type PendingGuardedGroupWrite = {
47
+ Group: object;
48
+ ConcernsSystemUser: boolean;
49
+ };
50
+ /**
51
+ * Why a guarded write may not enrol in `group`, or null. Refused when the group already holds a
52
+ * guarded write and this one or any earlier one concerns the system user: each is checked against
53
+ * the database before any is written, so none can see what the others change. A write outside a
54
+ * group is never refused here.
55
+ *
56
+ * Earlier writes are those holding a place reserved by {@link ReserveGuardedTransactionGroupWrite}.
57
+ * The place is taken when the check passes, not when the write enrols, so two guarded saves in
58
+ * flight at once (a `Promise.all`) cannot both pass; a write that is then refused, returns false
59
+ * or throws gives its place back ({@link ReleaseGuardedTransactionGroupWrite}), so a retry is not
60
+ * mistaken for a second write.
61
+ *
62
+ * @param group the write's transaction group, if any
63
+ * @param concernsSystemUser whether this write touches the system user's roles or a rule bound to one of them
64
+ */
65
+ export declare function GuardedTransactionGroupRefusal(group: object | null | undefined, concernsSystemUser: boolean): string | null;
66
+ /**
67
+ * Reserves a guarded write's place in `group` when it passes the group check, in the same
68
+ * synchronous step as the check. Null outside a group, where nothing is reserved.
69
+ */
70
+ export declare function ReserveGuardedTransactionGroupWrite(group: object | null | undefined, concernsSystemUser: boolean): PendingGuardedGroupWrite | null;
71
+ /** Gives back a reserved place for a write that did not enrol. A null `write` releases nothing. */
72
+ export declare function ReleaseGuardedTransactionGroupWrite(write: PendingGuardedGroupWrite | null): void;
73
+ //# sourceMappingURL=systemUserGuardState.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"systemUserGuardState.d.ts","sourceRoot":"","sources":["../../src/custom/systemUserGuardState.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,EAEH,iBAAiB,EACjB,gBAAgB,EAEhB,QAAQ,EAEX,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAEH,8BAA8B,EAEjC,MAAM,2CAA2C,CAAC;AAMnD,8DAA8D;AAC9D,wBAAgB,gBAAgB,IAAI,QAAQ,GAAG,IAAI,CAElD;AAED,qFAAqF;AACrF,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAGzE;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,CAAC,eAAe,EAAE,gBAAgB,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAYzH;AAED,8EAA8E;AAC9E,wBAAgB,aAAa,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAE/F;AAED;;;GAGG;AACH,wBAAsB,2BAA2B,CAC7C,eAAe,EAAE,gBAAgB,GAAG,IAAI,GAAG,SAAS,EACpD,QAAQ,EAAE,iBAAiB,EAC3B,MAAM,EAAE,MAAM,GACf,OAAO,CAAC,8BAA8B,EAAE,CAAC,CAQ3C;AAED;;;GAGG;AACH,wBAAsB,gCAAgC,CAClD,eAAe,EAAE,gBAAgB,GAAG,IAAI,GAAG,SAAS,EACpD,MAAM,EAAE,MAAM,GACf,OAAO,CAAC,MAAM,EAAE,CAAC,CAUnB;AA2CD,iGAAiG;AACjG,MAAM,MAAM,wBAAwB,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,kBAAkB,EAAE,OAAO,CAAA;CAAE,CAAC;AAEtF;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,kBAAkB,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAW3H;AAED;;;GAGG;AACH,wBAAgB,mCAAmC,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,kBAAkB,EAAE,OAAO,GAAG,wBAAwB,GAAG,IAAI,CAOlJ;AAED,mGAAmG;AACnG,wBAAgB,mCAAmC,CAAC,KAAK,EAAE,wBAAwB,GAAG,IAAI,GAAG,IAAI,CAMhG"}