@memberjunction/server 5.51.0 → 6.1.0-edge.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/LICENSE +7 -0
  2. package/dist/agentSessions/SessionManager.d.ts +22 -1
  3. package/dist/agentSessions/SessionManager.d.ts.map +1 -1
  4. package/dist/agentSessions/SessionManager.js +13 -3
  5. package/dist/agentSessions/SessionManager.js.map +1 -1
  6. package/dist/auth/actingContextResolver.d.ts +44 -0
  7. package/dist/auth/actingContextResolver.d.ts.map +1 -0
  8. package/dist/auth/actingContextResolver.js +18 -0
  9. package/dist/auth/actingContextResolver.js.map +1 -0
  10. package/dist/auth/index.d.ts +1 -0
  11. package/dist/auth/index.d.ts.map +1 -1
  12. package/dist/auth/index.js +1 -0
  13. package/dist/auth/index.js.map +1 -1
  14. package/dist/auth/sessionUserClone.d.ts +32 -0
  15. package/dist/auth/sessionUserClone.d.ts.map +1 -0
  16. package/dist/auth/sessionUserClone.js +50 -0
  17. package/dist/auth/sessionUserClone.js.map +1 -0
  18. package/dist/context.d.ts +1 -1
  19. package/dist/context.d.ts.map +1 -1
  20. package/dist/context.js +43 -6
  21. package/dist/context.js.map +1 -1
  22. package/dist/generated/generated.d.ts +71 -0
  23. package/dist/generated/generated.d.ts.map +1 -1
  24. package/dist/generated/generated.js +383 -3
  25. package/dist/generated/generated.js.map +1 -1
  26. package/dist/generic/FireAndForgetHeartbeat.d.ts +5 -0
  27. package/dist/generic/FireAndForgetHeartbeat.d.ts.map +1 -1
  28. package/dist/generic/FireAndForgetHeartbeat.js +5 -4
  29. package/dist/generic/FireAndForgetHeartbeat.js.map +1 -1
  30. package/dist/generic/PushStatusResolver.d.ts +58 -0
  31. package/dist/generic/PushStatusResolver.d.ts.map +1 -1
  32. package/dist/generic/PushStatusResolver.js +42 -3
  33. package/dist/generic/PushStatusResolver.js.map +1 -1
  34. package/dist/generic/ResolverBase.d.ts +9 -0
  35. package/dist/generic/ResolverBase.d.ts.map +1 -1
  36. package/dist/generic/ResolverBase.js +31 -13
  37. package/dist/generic/ResolverBase.js.map +1 -1
  38. package/dist/index.d.ts +2 -0
  39. package/dist/index.d.ts.map +1 -1
  40. package/dist/index.js +4 -2
  41. package/dist/index.js.map +1 -1
  42. package/dist/multiTenancy/index.d.ts +19 -0
  43. package/dist/multiTenancy/index.d.ts.map +1 -1
  44. package/dist/multiTenancy/index.js +82 -5
  45. package/dist/multiTenancy/index.js.map +1 -1
  46. package/dist/resolvers/ActionResolver.d.ts.map +1 -1
  47. package/dist/resolvers/ActionResolver.js +12 -5
  48. package/dist/resolvers/ActionResolver.js.map +1 -1
  49. package/dist/resolvers/MCPResolver.d.ts.map +1 -1
  50. package/dist/resolvers/MCPResolver.js +13 -16
  51. package/dist/resolvers/MCPResolver.js.map +1 -1
  52. package/dist/resolvers/RealtimeClientSessionResolver.d.ts +1 -1
  53. package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -1
  54. package/dist/resolvers/RealtimeClientSessionResolver.js +15 -14
  55. package/dist/resolvers/RealtimeClientSessionResolver.js.map +1 -1
  56. package/dist/resolvers/RemoteBrowserActionResolver.d.ts.map +1 -1
  57. package/dist/resolvers/RemoteBrowserActionResolver.js +19 -26
  58. package/dist/resolvers/RemoteBrowserActionResolver.js.map +1 -1
  59. package/dist/resolvers/ReportResolver.d.ts.map +1 -1
  60. package/dist/resolvers/ReportResolver.js +11 -3
  61. package/dist/resolvers/ReportResolver.js.map +1 -1
  62. package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
  63. package/dist/resolvers/RunAIAgentResolver.js +30 -40
  64. package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
  65. package/dist/resolvers/RunTestResolver.d.ts.map +1 -1
  66. package/dist/resolvers/RunTestResolver.js +90 -112
  67. package/dist/resolvers/RunTestResolver.js.map +1 -1
  68. package/dist/resolvers/SearchKnowledgeResolver.js +3 -3
  69. package/dist/resolvers/SearchKnowledgeResolver.js.map +1 -1
  70. package/dist/resolvers/SearchKnowledgeStreamResolver.js +2 -2
  71. package/dist/resolvers/SearchKnowledgeStreamResolver.js.map +1 -1
  72. package/dist/resolvers/VersionHistoryResolver.d.ts.map +1 -1
  73. package/dist/resolvers/VersionHistoryResolver.js +8 -12
  74. package/dist/resolvers/VersionHistoryResolver.js.map +1 -1
  75. package/dist/services/TaskOrchestrator.d.ts.map +1 -1
  76. package/dist/services/TaskOrchestrator.js +17 -15
  77. package/dist/services/TaskOrchestrator.js.map +1 -1
  78. package/package.json +100 -100
  79. package/src/__tests__/RealtimeBridgeResolver.test.ts +6 -0
  80. package/src/__tests__/SessionManager.test.ts +44 -0
  81. package/src/__tests__/multiTenancy.security.test.ts +254 -22
  82. package/src/__tests__/multiTenancy.test.ts +22 -0
  83. package/src/__tests__/pushStatusResolver.filter.test.ts +56 -0
  84. package/src/__tests__/resolverBase.rls.test.ts +4 -0
  85. package/src/__tests__/search-knowledge-tags.test.ts +74 -6
  86. package/src/__tests__/sessionUserClone.test.ts +161 -0
  87. package/src/agentSessions/SessionManager.ts +26 -2
  88. package/src/auth/actingContextResolver.ts +56 -0
  89. package/src/auth/index.ts +1 -0
  90. package/src/auth/sessionUserClone.ts +50 -0
  91. package/src/context.ts +48 -6
  92. package/src/generated/generated.ts +276 -3
  93. package/src/generic/FireAndForgetHeartbeat.ts +10 -4
  94. package/src/generic/PushStatusResolver.ts +79 -4
  95. package/src/generic/ResolverBase.ts +33 -13
  96. package/src/index.ts +4 -2
  97. package/src/multiTenancy/index.ts +92 -5
  98. package/src/resolvers/ActionResolver.ts +15 -7
  99. package/src/resolvers/MCPResolver.ts +13 -15
  100. package/src/resolvers/RealtimeClientSessionResolver.ts +15 -13
  101. package/src/resolvers/RemoteBrowserActionResolver.ts +19 -25
  102. package/src/resolvers/ReportResolver.ts +11 -3
  103. package/src/resolvers/RunAIAgentResolver.ts +30 -40
  104. package/src/resolvers/RunTestResolver.ts +90 -112
  105. package/src/resolvers/SearchKnowledgeResolver.ts +3 -3
  106. package/src/resolvers/SearchKnowledgeStreamResolver.ts +2 -2
  107. package/src/resolvers/VersionHistoryResolver.ts +10 -13
  108. package/src/resolvers/__tests__/ReportResolver.test.ts +279 -0
  109. package/src/services/TaskOrchestrator.ts +17 -17
@@ -1000,7 +1000,7 @@ export class MJActionExecutionLog_ {
1000
1000
  @Field({nullable: true, description: `Timestamp when the action execution ended (timezone-aware)`})
1001
1001
  EndedAt?: Date;
1002
1002
 
1003
- @Field({nullable: true, description: `JSON-formatted input parameters passed to the action during execution, storing the exact values used for this specific run.`})
1003
+ @Field({nullable: true, description: `JSON-formatted input parameters AS THE ACTION WAS CALLED, captured once when execution starts and never overwritten. Custom and Generated actions mutate their parameter array in place, so this is the only durable record of the values actually passed in; the final state lives in ResultParams. Parameter values may be redacted per ActionParam.LogValue / EntityActionParam.LogValue, and whole-record value types are never written - see the parameter's own documentation.`})
1004
1004
  Params?: string;
1005
1005
 
1006
1006
  @Field({nullable: true, description: `The outcome code returned by the action execution, indicating success, failure, or specific error conditions as defined in Action Result Codes.`})
@@ -1020,9 +1020,28 @@ export class MJActionExecutionLog_ {
1020
1020
  @Field()
1021
1021
  _mj__UpdatedAt: Date;
1022
1022
 
1023
- @Field({nullable: true, description: `JSON-formatted output data or response from the action execution`})
1023
+ @Field({nullable: true, description: `Human-readable summary message returned by the action - the reason for a refusal, or a short description of what was done. Not the action's output data: parameter values live in Params and ResultParams, and the outcome code in ResultCode.`})
1024
1024
  Message?: string;
1025
1025
 
1026
+ @Field({nullable: true, description: `Optional. The Entity Action binding that caused this run. NULL when the action was invoked directly - from a resolver, a script, an agent step or a scheduled action.`})
1027
+ @MaxLength(36)
1028
+ EntityActionID?: string;
1029
+
1030
+ @Field({nullable: true, description: `Optional. Which lifecycle event fired the binding - AfterUpdate, Validate, List and so on. Recorded separately from EntityActionID because one binding may be attached to several invocation types, and telling a Validate refusal apart from an AfterUpdate side effect is the first question anyone asks of this log.`})
1031
+ @MaxLength(36)
1032
+ EntityActionInvocationTypeID?: string;
1033
+
1034
+ @Field({nullable: true, description: `Optional. The entity of the record this run operated on. Deliberately denormalized rather than joined through EntityActionID: it survives the binding being deleted or retargeted, and it lets the log be queried by record with no join. Kept generic because every invoker has a subject - not only Entity Actions.`})
1035
+ @MaxLength(36)
1036
+ TargetEntityID?: string;
1037
+
1038
+ @Field({nullable: true, description: `Optional. The primary key of the record this run operated on, as text, paired with TargetEntityID. For multi-record invocation types (List, View) one log row is written per record, so this is always a single record.`})
1039
+ @MaxLength(450)
1040
+ TargetRecordID?: string;
1041
+
1042
+ @Field({nullable: true, description: `JSON-formatted FINAL parameter set captured when the action finished - the inputs as the action left them, plus any output parameters it produced. Written on FAILURE exactly as on success, under the same redaction rules: a failed run's partially-mutated inputs are usually the most diagnostic thing available, and an audit trail that records only successes is not an audit trail. Distinct from Params, which holds the values the action was called with and is never overwritten. NULL means one thing only - the run never finished (process died, host killed) - so it is a signal rather than an absence, and must not be backfilled.`})
1043
+ ResultParams?: string;
1044
+
1026
1045
  @Field()
1027
1046
  @MaxLength(425)
1028
1047
  Action: string;
@@ -1031,6 +1050,18 @@ export class MJActionExecutionLog_ {
1031
1050
  @MaxLength(100)
1032
1051
  User: string;
1033
1052
 
1053
+ @Field({nullable: true})
1054
+ @MaxLength(425)
1055
+ EntityAction?: string;
1056
+
1057
+ @Field({nullable: true})
1058
+ @MaxLength(255)
1059
+ EntityActionInvocationType?: string;
1060
+
1061
+ @Field({nullable: true})
1062
+ @MaxLength(255)
1063
+ TargetEntity?: string;
1064
+
1034
1065
  @Field(() => [MJProcessRunDetail_])
1035
1066
  MJProcessRunDetails_ActionExecutionLogIDArray: MJProcessRunDetail_[]; // Link to MJProcessRunDetails
1036
1067
 
@@ -1071,6 +1102,21 @@ export class CreateMJActionExecutionLogInput {
1071
1102
  @Field({ nullable: true })
1072
1103
  Message: string | null;
1073
1104
 
1105
+ @Field({ nullable: true })
1106
+ EntityActionID: string | null;
1107
+
1108
+ @Field({ nullable: true })
1109
+ EntityActionInvocationTypeID: string | null;
1110
+
1111
+ @Field({ nullable: true })
1112
+ TargetEntityID: string | null;
1113
+
1114
+ @Field({ nullable: true })
1115
+ TargetRecordID: string | null;
1116
+
1117
+ @Field({ nullable: true })
1118
+ ResultParams: string | null;
1119
+
1074
1120
  @Field(() => RestoreContextInput, { nullable: true })
1075
1121
  RestoreContext___?: RestoreContextInput;
1076
1122
  }
@@ -1108,6 +1154,21 @@ export class UpdateMJActionExecutionLogInput {
1108
1154
  @Field({ nullable: true })
1109
1155
  Message?: string | null;
1110
1156
 
1157
+ @Field({ nullable: true })
1158
+ EntityActionID?: string | null;
1159
+
1160
+ @Field({ nullable: true })
1161
+ EntityActionInvocationTypeID?: string | null;
1162
+
1163
+ @Field({ nullable: true })
1164
+ TargetEntityID?: string | null;
1165
+
1166
+ @Field({ nullable: true })
1167
+ TargetRecordID?: string | null;
1168
+
1169
+ @Field({ nullable: true })
1170
+ ResultParams?: string | null;
1171
+
1111
1172
  @Field(() => [KeyValuePairInput], { nullable: true })
1112
1173
  OldValues___?: KeyValuePairInput[];
1113
1174
 
@@ -1616,6 +1677,9 @@ export class MJActionParam_ {
1616
1677
  @MaxLength(20)
1617
1678
  MediaModality?: string;
1618
1679
 
1680
+ @Field(() => Boolean, {description: `Whether this parameter's VALUE may be written to ActionExecutionLog.Params. Default 1. Set to 0 for parameters that carry records, credentials or personal data - for example the Data payload of Execute Agent. Independent of the hard rule that Entity Action params of ValueType 'Entity Object' or 'Entity Object Data' are never logged regardless of this flag. When logging is suppressed the log records the parameter name, its type and a redaction marker, never the value.`})
1681
+ LogValue: boolean;
1682
+
1619
1683
  @Field()
1620
1684
  @MaxLength(425)
1621
1685
  Action: string;
@@ -1663,6 +1727,9 @@ export class CreateMJActionParamInput {
1663
1727
  @Field({ nullable: true })
1664
1728
  MediaModality: string | null;
1665
1729
 
1730
+ @Field(() => Boolean, { nullable: true })
1731
+ LogValue?: boolean;
1732
+
1666
1733
  @Field(() => RestoreContextInput, { nullable: true })
1667
1734
  RestoreContext___?: RestoreContextInput;
1668
1735
  }
@@ -1703,6 +1770,9 @@ export class UpdateMJActionParamInput {
1703
1770
  @Field({ nullable: true })
1704
1771
  MediaModality?: string | null;
1705
1772
 
1773
+ @Field(() => Boolean, { nullable: true })
1774
+ LogValue?: boolean;
1775
+
1706
1776
  @Field(() => [KeyValuePairInput], { nullable: true })
1707
1777
  OldValues___?: KeyValuePairInput[];
1708
1778
 
@@ -22278,6 +22348,10 @@ export class MJAPIApplicationScope_ {
22278
22348
  @Field()
22279
22349
  _mj__UpdatedAt: Date;
22280
22350
 
22351
+ @Field({nullable: true, description: `Optional row-level filter acting as a CEILING for every API key operating under this application — a restriction keys inherit and cannot widen. Composes with the per-key filter (APIKeyScope.RowFilterID) and with role-based RLS using AND, never OR, so no layer can broaden another. References the same RowLevelSecurityFilter catalog used by role-based RLS. NULL (the default) means the application imposes no row ceiling. The same authoring constraints as APIKeyScope.RowFilterID apply: exact single-entity resource pattern, all referenced columns must exist on that entity, and all referrers of the filter record must resolve to the same entity.`})
22352
+ @MaxLength(36)
22353
+ RowFilterID?: string;
22354
+
22281
22355
  @Field()
22282
22356
  @MaxLength(100)
22283
22357
  Application: string;
@@ -22286,6 +22360,10 @@ export class MJAPIApplicationScope_ {
22286
22360
  @MaxLength(100)
22287
22361
  Scope: string;
22288
22362
 
22363
+ @Field({nullable: true})
22364
+ @MaxLength(100)
22365
+ RowFilter?: string;
22366
+
22289
22367
  }
22290
22368
 
22291
22369
  //****************************************************************************
@@ -22314,6 +22392,9 @@ export class CreateMJAPIApplicationScopeInput {
22314
22392
  @Field(() => Int, { nullable: true })
22315
22393
  Priority?: number;
22316
22394
 
22395
+ @Field({ nullable: true })
22396
+ RowFilterID: string | null;
22397
+
22317
22398
  @Field(() => RestoreContextInput, { nullable: true })
22318
22399
  RestoreContext___?: RestoreContextInput;
22319
22400
  }
@@ -22345,6 +22426,9 @@ export class UpdateMJAPIApplicationScopeInput {
22345
22426
  @Field(() => Int, { nullable: true })
22346
22427
  Priority?: number;
22347
22428
 
22429
+ @Field({ nullable: true })
22430
+ RowFilterID?: string | null;
22431
+
22348
22432
  @Field(() => [KeyValuePairInput], { nullable: true })
22349
22433
  OldValues___?: KeyValuePairInput[];
22350
22434
 
@@ -22833,6 +22917,10 @@ export class MJAPIKeyScope_ {
22833
22917
  @Field(() => Int, {description: `Rule evaluation order. Higher priority rules are evaluated first. Within same priority, deny rules are evaluated before allow rules.`})
22834
22918
  Priority: number;
22835
22919
 
22920
+ @Field({nullable: true, description: `Optional row-level filter narrowing WHICH RECORDS this scope grant applies to, in addition to the resource pattern that governs which entities. References the same RowLevelSecurityFilter catalog used by role-based RLS, so the filter text flows through the standard {{Token}} substitution engine and every existing RLS enforcement point (RunView, Load by primary key, save, delete, search). NULL (the default) means no row restriction — behavior identical to before this column existed. When set, the rule's ResourcePattern must name a single exact entity (no wildcards, no comma-separated lists), every column the filter references must resolve to a real non-virtual field on that entity, and every other referrer of the same filter record must resolve to that same entity. Critically, this filter is evaluated INDEPENDENTLY of the role-RLS exemption: a user exempt from role RLS is still bound by their key's filter, because narrowing a principal below what their roles allow is the entire purpose of a key ceiling.`})
22921
+ @MaxLength(36)
22922
+ RowFilterID?: string;
22923
+
22836
22924
  @Field()
22837
22925
  @MaxLength(255)
22838
22926
  APIKey: string;
@@ -22841,6 +22929,10 @@ export class MJAPIKeyScope_ {
22841
22929
  @MaxLength(100)
22842
22930
  Scope: string;
22843
22931
 
22932
+ @Field({nullable: true})
22933
+ @MaxLength(100)
22934
+ RowFilter?: string;
22935
+
22844
22936
  }
22845
22937
 
22846
22938
  //****************************************************************************
@@ -22869,6 +22961,9 @@ export class CreateMJAPIKeyScopeInput {
22869
22961
  @Field(() => Int, { nullable: true })
22870
22962
  Priority?: number;
22871
22963
 
22964
+ @Field({ nullable: true })
22965
+ RowFilterID: string | null;
22966
+
22872
22967
  @Field(() => RestoreContextInput, { nullable: true })
22873
22968
  RestoreContext___?: RestoreContextInput;
22874
22969
  }
@@ -22900,6 +22995,9 @@ export class UpdateMJAPIKeyScopeInput {
22900
22995
  @Field(() => Int, { nullable: true })
22901
22996
  Priority?: number;
22902
22997
 
22998
+ @Field({ nullable: true })
22999
+ RowFilterID?: string | null;
23000
+
22903
23001
  @Field(() => [KeyValuePairInput], { nullable: true })
22904
23002
  OldValues___?: KeyValuePairInput[];
22905
23003
 
@@ -34383,6 +34481,10 @@ export class MJContentItemChunk_ {
34383
34481
  @MaxLength(250)
34384
34482
  ContentItem?: string;
34385
34483
 
34484
+ @Field({nullable: true})
34485
+ @MaxLength(500)
34486
+ ParentChunk?: string;
34487
+
34386
34488
  @Field({nullable: true})
34387
34489
  @MaxLength(36)
34388
34490
  RootParentChunkID?: string;
@@ -46499,6 +46601,19 @@ export class MJEntity_ {
46499
46601
  @MaxLength(255)
46500
46602
  ExternalObjectName?: string;
46501
46603
 
46604
+ @Field({nullable: true, description: `When set, CodeGen generates the entity's full base view under THIS name instead of BaseView, and the application owns BaseView — which is expected to wrap it (SELECT g.*, <extras> FROM <GeneratedBaseViewName> g). This gives an entity a custom base view WITHOUT inheriting the generated SQL: related-entity display joins, geo columns and recursive root-ID columns keep regenerating underneath, so a foreign key added later still appears. NULL (the default, and every pre-existing row) means the previous all-or-nothing behaviour: BaseViewGenerated alone decides whether CodeGen writes BaseView, and there is no second view. BaseView remains the public surface — entity field discovery, permissions and the generated CRUD procedures all target it. SQL SERVER ONLY: layering relies on sp_refreshview to re-resolve the application-owned outer view's SELECT * against a regenerated inner view. PostgreSQL freezes a view's column list at creation and has no refresh equivalent, so CodeGen rejects this column on PostgreSQL rather than let the outer view go silently stale.`})
46605
+ @MaxLength(255)
46606
+ GeneratedBaseViewName?: string;
46607
+
46608
+ @Field(() => Boolean, {description: `When 1, this entity may be populated by INSERT statements that do not go through BaseEntity.Save() — bulk loads, ETL/integration sync, or rows created as a side effect of a stored procedure. Default 0, meaning every insert is expected to flow through BaseEntity so that record-change tracking, entity actions, validation and cache invalidation all run. This column DECLARES intent for the code paths and tooling that consult it; it does not and cannot prevent anyone from executing SQL. Requires TrackRecordChanges = 0 and TrustServerCacheCompletely = 0, because a direct insert produces neither an audit row nor a cache-invalidation event.`})
46609
+ AllowDirectSQLInsert: boolean;
46610
+
46611
+ @Field(() => Boolean, {description: `When 1, this entity may be modified by UPDATE statements that do not go through BaseEntity.Save() — bulk backfills, integration sync, or maintenance routines. Default 0, meaning every update is expected to flow through BaseEntity so that record-change tracking, entity actions, validation and cache invalidation all run. This column DECLARES intent for the code paths and tooling that consult it; it does not and cannot prevent anyone from executing SQL. Requires TrackRecordChanges = 0 and TrustServerCacheCompletely = 0, because a direct update produces neither an audit row nor a cache-invalidation event.`})
46612
+ AllowDirectSQLUpdate: boolean;
46613
+
46614
+ @Field(() => Boolean, {description: `When 1, this entity may have rows removed by DELETE statements that do not go through BaseEntity.Delete() — purge and retention routines, or integration sync reconciling a remote source. Default 0, meaning every delete is expected to flow through BaseEntity so that record-change tracking, entity actions, cascade handling and cache invalidation all run. This column DECLARES intent for the code paths and tooling that consult it; it does not and cannot prevent anyone from executing SQL. Requires TrackRecordChanges = 0 and TrustServerCacheCompletely = 0, and additionally requires DeleteType = 'Hard' — a direct DELETE removes the row outright rather than setting DeletedAt, which would defeat soft delete.`})
46615
+ AllowDirectSQLDelete: boolean;
46616
+
46502
46617
  @Field({nullable: true, description: `Schema-based programmatic code name derived from the entity Name. Uses GetClassNameSchemaPrefix(SchemaName) as the prefix, then strips EntityNamePrefix from the Name and removes spaces. For "__mj" schema with entity "MJ: AI Models", this produces "MJAIModels". For entities in other schemas, the sanitized schema name is prepended. Used in GraphQL type generation and internal code references.`})
46503
46618
  CodeName?: string;
46504
46619
 
@@ -46737,6 +46852,12 @@ export class MJEntity_ {
46737
46852
  @Field(() => [MJScopedPromptConfig_])
46738
46853
  MJScopedPromptConfigs_PrimaryScopeEntityIDArray: MJScopedPromptConfig_[]; // Link to MJScopedPromptConfigs
46739
46854
 
46855
+ @Field(() => [MJActionExecutionLog_])
46856
+ MJActionExecutionLogs_TargetEntityIDArray: MJActionExecutionLog_[]; // Link to MJActionExecutionLogs
46857
+
46858
+ @Field(() => [MJEntityAction_])
46859
+ MJEntityActions_ScopeEntityIDArray: MJEntityAction_[]; // Link to MJEntityActions
46860
+
46740
46861
  }
46741
46862
 
46742
46863
  //****************************************************************************
@@ -46933,6 +47054,18 @@ export class CreateMJEntityInput {
46933
47054
  @Field({ nullable: true })
46934
47055
  ExternalObjectName: string | null;
46935
47056
 
47057
+ @Field({ nullable: true })
47058
+ GeneratedBaseViewName: string | null;
47059
+
47060
+ @Field(() => Boolean, { nullable: true })
47061
+ AllowDirectSQLInsert?: boolean;
47062
+
47063
+ @Field(() => Boolean, { nullable: true })
47064
+ AllowDirectSQLUpdate?: boolean;
47065
+
47066
+ @Field(() => Boolean, { nullable: true })
47067
+ AllowDirectSQLDelete?: boolean;
47068
+
46936
47069
  @Field(() => RestoreContextInput, { nullable: true })
46937
47070
  RestoreContext___?: RestoreContextInput;
46938
47071
  }
@@ -47132,6 +47265,18 @@ export class UpdateMJEntityInput {
47132
47265
  @Field({ nullable: true })
47133
47266
  ExternalObjectName?: string | null;
47134
47267
 
47268
+ @Field({ nullable: true })
47269
+ GeneratedBaseViewName?: string | null;
47270
+
47271
+ @Field(() => Boolean, { nullable: true })
47272
+ AllowDirectSQLInsert?: boolean;
47273
+
47274
+ @Field(() => Boolean, { nullable: true })
47275
+ AllowDirectSQLUpdate?: boolean;
47276
+
47277
+ @Field(() => Boolean, { nullable: true })
47278
+ AllowDirectSQLDelete?: boolean;
47279
+
47135
47280
  @Field(() => [KeyValuePairInput], { nullable: true })
47136
47281
  OldValues___?: KeyValuePairInput[];
47137
47282
 
@@ -47916,6 +48061,26 @@ export class MJEntityResolverBase extends ResolverBase {
47916
48061
  return result;
47917
48062
  }
47918
48063
 
48064
+ @FieldResolver(() => [MJActionExecutionLog_])
48065
+ async MJActionExecutionLogs_TargetEntityIDArray(@Root() mjentity_: MJEntity_, @Ctx() { userPayload, providers }: AppContext, @PubSub() pubSub: PubSubEngine) {
48066
+ this.CheckUserReadPermissions('MJ: Action Execution Logs', userPayload);
48067
+ const provider = GetReadOnlyProvider(providers, { allowFallbackToReadWrite: true });
48068
+ const sSQL = `SELECT * FROM ${provider.QuoteSchemaAndView(Metadata.Provider.ConfigData.MJCoreSchemaName, 'vwActionExecutionLogs')} WHERE ${provider.QuoteIdentifier('TargetEntityID')}=${provider.BuildParameterPlaceholder(0)} ` + this.getRowLevelSecurityWhereClause(provider, 'MJ: Action Execution Logs', userPayload, EntityPermissionType.Read, 'AND');
48069
+ const rows = await provider.ExecuteSQL(sSQL, [mjentity_.ID], undefined, this.GetUserFromPayload(userPayload));
48070
+ const result = await this.ArrayMapFieldNamesToCodeNames('MJ: Action Execution Logs', rows, this.GetUserFromPayload(userPayload));
48071
+ return result;
48072
+ }
48073
+
48074
+ @FieldResolver(() => [MJEntityAction_])
48075
+ async MJEntityActions_ScopeEntityIDArray(@Root() mjentity_: MJEntity_, @Ctx() { userPayload, providers }: AppContext, @PubSub() pubSub: PubSubEngine) {
48076
+ this.CheckUserReadPermissions('MJ: Entity Actions', userPayload);
48077
+ const provider = GetReadOnlyProvider(providers, { allowFallbackToReadWrite: true });
48078
+ const sSQL = `SELECT * FROM ${provider.QuoteSchemaAndView(Metadata.Provider.ConfigData.MJCoreSchemaName, 'vwEntityActions')} WHERE ${provider.QuoteIdentifier('ScopeEntityID')}=${provider.BuildParameterPlaceholder(0)} ` + this.getRowLevelSecurityWhereClause(provider, 'MJ: Entity Actions', userPayload, EntityPermissionType.Read, 'AND');
48079
+ const rows = await provider.ExecuteSQL(sSQL, [mjentity_.ID], undefined, this.GetUserFromPayload(userPayload));
48080
+ const result = await this.ArrayMapFieldNamesToCodeNames('MJ: Entity Actions', rows, this.GetUserFromPayload(userPayload));
48081
+ return result;
48082
+ }
48083
+
47919
48084
  @Mutation(() => MJEntity_)
47920
48085
  async CreateMJEntity(
47921
48086
  @Arg('input', () => CreateMJEntityInput) input: CreateMJEntityInput,
@@ -48150,6 +48315,9 @@ export class MJEntityActionInvocationType_ {
48150
48315
  @Field(() => [MJEntityActionInvocation_])
48151
48316
  MJEntityActionInvocations_InvocationTypeIDArray: MJEntityActionInvocation_[]; // Link to MJEntityActionInvocations
48152
48317
 
48318
+ @Field(() => [MJActionExecutionLog_])
48319
+ MJActionExecutionLogs_EntityActionInvocationTypeIDArray: MJActionExecutionLog_[]; // Link to MJActionExecutionLogs
48320
+
48153
48321
  }
48154
48322
 
48155
48323
  //****************************************************************************
@@ -48265,6 +48433,16 @@ export class MJEntityActionInvocationTypeResolver extends ResolverBase {
48265
48433
  return result;
48266
48434
  }
48267
48435
 
48436
+ @FieldResolver(() => [MJActionExecutionLog_])
48437
+ async MJActionExecutionLogs_EntityActionInvocationTypeIDArray(@Root() mjentityactioninvocationtype_: MJEntityActionInvocationType_, @Ctx() { userPayload, providers }: AppContext, @PubSub() pubSub: PubSubEngine) {
48438
+ this.CheckUserReadPermissions('MJ: Action Execution Logs', userPayload);
48439
+ const provider = GetReadOnlyProvider(providers, { allowFallbackToReadWrite: true });
48440
+ const sSQL = `SELECT * FROM ${provider.QuoteSchemaAndView(Metadata.Provider.ConfigData.MJCoreSchemaName, 'vwActionExecutionLogs')} WHERE ${provider.QuoteIdentifier('EntityActionInvocationTypeID')}=${provider.BuildParameterPlaceholder(0)} ` + this.getRowLevelSecurityWhereClause(provider, 'MJ: Action Execution Logs', userPayload, EntityPermissionType.Read, 'AND');
48441
+ const rows = await provider.ExecuteSQL(sSQL, [mjentityactioninvocationtype_.ID], undefined, this.GetUserFromPayload(userPayload));
48442
+ const result = await this.ArrayMapFieldNamesToCodeNames('MJ: Action Execution Logs', rows, this.GetUserFromPayload(userPayload));
48443
+ return result;
48444
+ }
48445
+
48268
48446
  @Mutation(() => MJEntityActionInvocationType_)
48269
48447
  async CreateMJEntityActionInvocationType(
48270
48448
  @Arg('input', () => CreateMJEntityActionInvocationTypeInput) input: CreateMJEntityActionInvocationTypeInput,
@@ -48490,7 +48668,7 @@ export class MJEntityActionParam_ {
48490
48668
  @MaxLength(36)
48491
48669
  ActionParamID: string;
48492
48670
 
48493
- @Field({description: `Type of the value, which can be Static, Entity Object, or Script.`})
48671
+ @Field({description: `How the parameter value is produced at invocation time. Static = the literal Value (parsed as JSON when it parses). Entity Object = the live BaseEntity instance, for actions that call entity methods. Entity Object Data = entity.GetAll(), a plain object - use this for any action that SERIALIZES the value, such as the Data payload of Execute Agent, because a BaseEntity serializes to {} (its fields are getters, not enumerable own properties). Entity Field = the named field's value. Script = evaluated expression with the entity in scope.`})
48494
48672
  @MaxLength(20)
48495
48673
  ValueType: string;
48496
48674
 
@@ -48506,6 +48684,9 @@ export class MJEntityActionParam_ {
48506
48684
  @Field()
48507
48685
  _mj__UpdatedAt: Date;
48508
48686
 
48687
+ @Field(() => Boolean, {nullable: true, description: `Optional per-binding override of ActionParam.LogValue. NULL (the default) inherits the parameter definition. Set to 0 when this particular binding passes something sensitive through a parameter that is ordinarily safe to log - a message body through a generic Text parameter, for instance. Cannot re-enable logging for a value type the hard rule suppresses.`})
48688
+ LogValue?: boolean;
48689
+
48509
48690
  @Field()
48510
48691
  @MaxLength(425)
48511
48692
  EntityAction: string;
@@ -48539,6 +48720,9 @@ export class CreateMJEntityActionParamInput {
48539
48720
  @Field({ nullable: true })
48540
48721
  Comments: string | null;
48541
48722
 
48723
+ @Field(() => Boolean, { nullable: true })
48724
+ LogValue: boolean | null;
48725
+
48542
48726
  @Field(() => RestoreContextInput, { nullable: true })
48543
48727
  RestoreContext___?: RestoreContextInput;
48544
48728
  }
@@ -48567,6 +48751,9 @@ export class UpdateMJEntityActionParamInput {
48567
48751
  @Field({ nullable: true })
48568
48752
  Comments?: string | null;
48569
48753
 
48754
+ @Field(() => Boolean, { nullable: true })
48755
+ LogValue?: boolean | null;
48756
+
48570
48757
  @Field(() => [KeyValuePairInput], { nullable: true })
48571
48758
  OldValues___?: KeyValuePairInput[];
48572
48759
 
@@ -48687,6 +48874,21 @@ export class MJEntityAction_ {
48687
48874
  @MaxLength(36)
48688
48875
  ID: string;
48689
48876
 
48877
+ @Field(() => Int, {description: `Execution order when multiple Entity Actions are bound to the same entity and invocation type. Lower runs first; ties fall back to creation order. Defaults to 0 so existing rows are unaffected.`})
48878
+ Sequence: number;
48879
+
48880
+ @Field({nullable: true, description: `Optional. Together with ScopeRecordID, narrows this Entity Action to records related to ONE specific record - for example a single Deal Type, Contract Type, Pipeline or Company - rather than every record of EntityID. NULL (the default) means the action applies to all records, which is the pre-existing behaviour. How a scope record relates to the subject record is resolved by the app that owns the scope entity; the framework only stores and filters on the pair.`})
48881
+ @MaxLength(36)
48882
+ ScopeEntityID?: string;
48883
+
48884
+ @Field({nullable: true, description: `Optional. The primary key of the scope record, as text, paired with ScopeEntityID. Both columns are NULL or both are set (CK_EntityAction_Scope). Lets a configuration record such as a Deal Type surface "the workflows bound to me" as a real relationship rather than something buried in filter code.`})
48885
+ @MaxLength(450)
48886
+ ScopeRecordID?: string;
48887
+
48888
+ @Field({description: `How much of this binding's activity reaches ActionExecutionLog. All (default) writes a row per invocation. FailuresOnly writes only runs that did not succeed - the right setting for a high-frequency binding on a busy entity, where the successful runs are noise. None disables logging for the binding entirely and should be rare, because it also removes the failure record.`})
48889
+ @MaxLength(20)
48890
+ LoggingMode: string;
48891
+
48690
48892
  @Field()
48691
48893
  @MaxLength(255)
48692
48894
  Entity: string;
@@ -48695,6 +48897,10 @@ export class MJEntityAction_ {
48695
48897
  @MaxLength(425)
48696
48898
  Action: string;
48697
48899
 
48900
+ @Field({nullable: true})
48901
+ @MaxLength(255)
48902
+ ScopeEntity?: string;
48903
+
48698
48904
  @Field(() => [MJEntityActionFilter_])
48699
48905
  MJEntityActionFilters_EntityActionIDArray: MJEntityActionFilter_[]; // Link to MJEntityActionFilters
48700
48906
 
@@ -48704,6 +48910,9 @@ export class MJEntityAction_ {
48704
48910
  @Field(() => [MJEntityActionParam_])
48705
48911
  MJEntityActionParams_EntityActionIDArray: MJEntityActionParam_[]; // Link to MJEntityActionParams
48706
48912
 
48913
+ @Field(() => [MJActionExecutionLog_])
48914
+ MJActionExecutionLogs_EntityActionIDArray: MJActionExecutionLog_[]; // Link to MJActionExecutionLogs
48915
+
48707
48916
  }
48708
48917
 
48709
48918
  //****************************************************************************
@@ -48723,6 +48932,18 @@ export class CreateMJEntityActionInput {
48723
48932
  @Field({ nullable: true })
48724
48933
  ID?: string;
48725
48934
 
48935
+ @Field(() => Int, { nullable: true })
48936
+ Sequence?: number;
48937
+
48938
+ @Field({ nullable: true })
48939
+ ScopeEntityID: string | null;
48940
+
48941
+ @Field({ nullable: true })
48942
+ ScopeRecordID: string | null;
48943
+
48944
+ @Field({ nullable: true })
48945
+ LoggingMode?: string;
48946
+
48726
48947
  @Field(() => RestoreContextInput, { nullable: true })
48727
48948
  RestoreContext___?: RestoreContextInput;
48728
48949
  }
@@ -48745,6 +48966,18 @@ export class UpdateMJEntityActionInput {
48745
48966
  @Field()
48746
48967
  ID: string;
48747
48968
 
48969
+ @Field(() => Int, { nullable: true })
48970
+ Sequence?: number;
48971
+
48972
+ @Field({ nullable: true })
48973
+ ScopeEntityID?: string | null;
48974
+
48975
+ @Field({ nullable: true })
48976
+ ScopeRecordID?: string | null;
48977
+
48978
+ @Field({ nullable: true })
48979
+ LoggingMode?: string;
48980
+
48748
48981
  @Field(() => [KeyValuePairInput], { nullable: true })
48749
48982
  OldValues___?: KeyValuePairInput[];
48750
48983
 
@@ -48839,6 +49072,16 @@ export class MJEntityActionResolver extends ResolverBase {
48839
49072
  return result;
48840
49073
  }
48841
49074
 
49075
+ @FieldResolver(() => [MJActionExecutionLog_])
49076
+ async MJActionExecutionLogs_EntityActionIDArray(@Root() mjentityaction_: MJEntityAction_, @Ctx() { userPayload, providers }: AppContext, @PubSub() pubSub: PubSubEngine) {
49077
+ this.CheckUserReadPermissions('MJ: Action Execution Logs', userPayload);
49078
+ const provider = GetReadOnlyProvider(providers, { allowFallbackToReadWrite: true });
49079
+ const sSQL = `SELECT * FROM ${provider.QuoteSchemaAndView(Metadata.Provider.ConfigData.MJCoreSchemaName, 'vwActionExecutionLogs')} WHERE ${provider.QuoteIdentifier('EntityActionID')}=${provider.BuildParameterPlaceholder(0)} ` + this.getRowLevelSecurityWhereClause(provider, 'MJ: Action Execution Logs', userPayload, EntityPermissionType.Read, 'AND');
49080
+ const rows = await provider.ExecuteSQL(sSQL, [mjentityaction_.ID], undefined, this.GetUserFromPayload(userPayload));
49081
+ const result = await this.ArrayMapFieldNamesToCodeNames('MJ: Action Execution Logs', rows, this.GetUserFromPayload(userPayload));
49082
+ return result;
49083
+ }
49084
+
48842
49085
  @Mutation(() => MJEntityAction_)
48843
49086
  async CreateMJEntityAction(
48844
49087
  @Arg('input', () => CreateMJEntityActionInput) input: CreateMJEntityActionInput,
@@ -77389,6 +77632,12 @@ export class MJRowLevelSecurityFilter_ {
77389
77632
  @Field(() => [MJEntityPermission_])
77390
77633
  MJEntityPermissions_UpdateRLSFilterIDArray: MJEntityPermission_[]; // Link to MJEntityPermissions
77391
77634
 
77635
+ @Field(() => [MJAPIKeyScope_])
77636
+ MJAPIKeyScopes_RowFilterIDArray: MJAPIKeyScope_[]; // Link to MJAPIKeyScopes
77637
+
77638
+ @Field(() => [MJAPIApplicationScope_])
77639
+ MJAPIApplicationScopes_RowFilterIDArray: MJAPIApplicationScope_[]; // Link to MJAPIApplicationScopes
77640
+
77392
77641
  }
77393
77642
 
77394
77643
  //****************************************************************************
@@ -77544,6 +77793,26 @@ export class MJRowLevelSecurityFilterResolver extends ResolverBase {
77544
77793
  return result;
77545
77794
  }
77546
77795
 
77796
+ @FieldResolver(() => [MJAPIKeyScope_])
77797
+ async MJAPIKeyScopes_RowFilterIDArray(@Root() mjrowlevelsecurityfilter_: MJRowLevelSecurityFilter_, @Ctx() { userPayload, providers }: AppContext, @PubSub() pubSub: PubSubEngine) {
77798
+ this.CheckUserReadPermissions('MJ: API Key Scopes', userPayload);
77799
+ const provider = GetReadOnlyProvider(providers, { allowFallbackToReadWrite: true });
77800
+ const sSQL = `SELECT * FROM ${provider.QuoteSchemaAndView(Metadata.Provider.ConfigData.MJCoreSchemaName, 'vwAPIKeyScopes')} WHERE ${provider.QuoteIdentifier('RowFilterID')}=${provider.BuildParameterPlaceholder(0)} ` + this.getRowLevelSecurityWhereClause(provider, 'MJ: API Key Scopes', userPayload, EntityPermissionType.Read, 'AND');
77801
+ const rows = await provider.ExecuteSQL(sSQL, [mjrowlevelsecurityfilter_.ID], undefined, this.GetUserFromPayload(userPayload));
77802
+ const result = await this.ArrayMapFieldNamesToCodeNames('MJ: API Key Scopes', rows, this.GetUserFromPayload(userPayload));
77803
+ return result;
77804
+ }
77805
+
77806
+ @FieldResolver(() => [MJAPIApplicationScope_])
77807
+ async MJAPIApplicationScopes_RowFilterIDArray(@Root() mjrowlevelsecurityfilter_: MJRowLevelSecurityFilter_, @Ctx() { userPayload, providers }: AppContext, @PubSub() pubSub: PubSubEngine) {
77808
+ this.CheckUserReadPermissions('MJ: API Application Scopes', userPayload);
77809
+ const provider = GetReadOnlyProvider(providers, { allowFallbackToReadWrite: true });
77810
+ const sSQL = `SELECT * FROM ${provider.QuoteSchemaAndView(Metadata.Provider.ConfigData.MJCoreSchemaName, 'vwAPIApplicationScopes')} WHERE ${provider.QuoteIdentifier('RowFilterID')}=${provider.BuildParameterPlaceholder(0)} ` + this.getRowLevelSecurityWhereClause(provider, 'MJ: API Application Scopes', userPayload, EntityPermissionType.Read, 'AND');
77811
+ const rows = await provider.ExecuteSQL(sSQL, [mjrowlevelsecurityfilter_.ID], undefined, this.GetUserFromPayload(userPayload));
77812
+ const result = await this.ArrayMapFieldNamesToCodeNames('MJ: API Application Scopes', rows, this.GetUserFromPayload(userPayload));
77813
+ return result;
77814
+ }
77815
+
77547
77816
  @Mutation(() => MJRowLevelSecurityFilter_)
77548
77817
  async CreateMJRowLevelSecurityFilter(
77549
77818
  @Arg('input', () => CreateMJRowLevelSecurityFilterInput) input: CreateMJRowLevelSecurityFilterInput,
@@ -93056,6 +93325,10 @@ export class MJUserViewRunDetail_ {
93056
93325
  @Field()
93057
93326
  _mj__UpdatedAt: Date;
93058
93327
 
93328
+ @Field()
93329
+ @MaxLength(100)
93330
+ UserViewRun: string;
93331
+
93059
93332
  @Field()
93060
93333
  @MaxLength(36)
93061
93334
  UserViewID: string;
@@ -1,6 +1,6 @@
1
1
  import { PubSubEngine } from 'type-graphql';
2
2
  import { LogError } from '@memberjunction/core';
3
- import { PUSH_STATUS_UPDATES_TOPIC } from './PushStatusResolver.js';
3
+ import { publishStatusUpdate } from './PushStatusResolver.js';
4
4
 
5
5
  /** Default cadence for fire-and-forget liveness pulses (5 minutes). */
6
6
  export const DEFAULT_PULSE_INTERVAL_MS = 5 * 60 * 1000;
@@ -32,6 +32,11 @@ export interface LivenessPulseOptions {
32
32
  pubSub: PubSubEngine;
33
33
  /** Session the client is subscribed on (used by the subscription filter). */
34
34
  sessionId: string;
35
+ /**
36
+ * Authenticated user the operation belongs to (B49). Stamped on every pulse so the
37
+ * subscription filter binds delivery to identity, not just the client-chosen sessionId.
38
+ */
39
+ ownerUserId: string;
35
40
  /** Resolver label echoed in the message envelope (e.g. 'RunAIAgentResolver'). */
36
41
  resolver: string;
37
42
  /** Pulse cadence in ms. Defaults to {@link DEFAULT_PULSE_INTERVAL_MS}. */
@@ -56,7 +61,7 @@ export interface LivenessPulseOptions {
56
61
  * so the client receives it through the same subscription with no special parsing.
57
62
  */
58
63
  export function startLivenessPulse(options: LivenessPulseOptions): LivenessPulseHandle {
59
- const { pubSub, sessionId, resolver, readStatus } = options;
64
+ const { pubSub, sessionId, ownerUserId, resolver, readStatus } = options;
60
65
  const intervalMs = options.intervalMs ?? DEFAULT_PULSE_INTERVAL_MS;
61
66
 
62
67
  const timer = setInterval(() => {
@@ -68,14 +73,15 @@ export function startLivenessPulse(options: LivenessPulseOptions): LivenessPulse
68
73
  LogError(`[LivenessPulse:${resolver}] readStatus failed: ${(e as Error).message}`);
69
74
  }
70
75
 
71
- pubSub.publish(PUSH_STATUS_UPDATES_TOPIC, {
76
+ publishStatusUpdate(pubSub, {
77
+ sessionId,
78
+ ownerUserId,
72
79
  message: JSON.stringify({
73
80
  resolver,
74
81
  type: HEARTBEAT_MESSAGE_TYPE,
75
82
  status: 'ok',
76
83
  data: data ?? {},
77
84
  }),
78
- sessionId,
79
85
  });
80
86
  }, intervalMs);
81
87