@memberjunction/server 5.37.0 → 5.39.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 (121) hide show
  1. package/README.md +79 -12
  2. package/dist/agents/skip-sdk.d.ts.map +1 -1
  3. package/dist/agents/skip-sdk.js +16 -12
  4. package/dist/agents/skip-sdk.js.map +1 -1
  5. package/dist/apolloServer/index.d.ts +0 -8
  6. package/dist/apolloServer/index.d.ts.map +1 -1
  7. package/dist/apolloServer/index.js +61 -0
  8. package/dist/apolloServer/index.js.map +1 -1
  9. package/dist/config.d.ts +68 -0
  10. package/dist/config.d.ts.map +1 -1
  11. package/dist/config.js +24 -0
  12. package/dist/config.js.map +1 -1
  13. package/dist/context.d.ts.map +1 -1
  14. package/dist/context.js +2 -21
  15. package/dist/context.js.map +1 -1
  16. package/dist/generated/generated.d.ts +172 -5
  17. package/dist/generated/generated.d.ts.map +1 -1
  18. package/dist/generated/generated.js +867 -14
  19. package/dist/generated/generated.js.map +1 -1
  20. package/dist/generic/FireAndForgetHeartbeat.d.ts +51 -0
  21. package/dist/generic/FireAndForgetHeartbeat.d.ts.map +1 -0
  22. package/dist/generic/FireAndForgetHeartbeat.js +44 -0
  23. package/dist/generic/FireAndForgetHeartbeat.js.map +1 -0
  24. package/dist/index.d.ts +3 -0
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +177 -16
  27. package/dist/index.js.map +1 -1
  28. package/dist/logging/NoLog.d.ts +50 -0
  29. package/dist/logging/NoLog.d.ts.map +1 -0
  30. package/dist/logging/NoLog.js +80 -0
  31. package/dist/logging/NoLog.js.map +1 -0
  32. package/dist/logging/bootAudit.d.ts +43 -0
  33. package/dist/logging/bootAudit.d.ts.map +1 -0
  34. package/dist/logging/bootAudit.js +83 -0
  35. package/dist/logging/bootAudit.js.map +1 -0
  36. package/dist/logging/boundaryLogPayload.d.ts +18 -0
  37. package/dist/logging/boundaryLogPayload.d.ts.map +1 -0
  38. package/dist/logging/boundaryLogPayload.js +18 -0
  39. package/dist/logging/boundaryLogPayload.js.map +1 -0
  40. package/dist/logging/secretRedactor.d.ts +23 -0
  41. package/dist/logging/secretRedactor.d.ts.map +1 -0
  42. package/dist/logging/secretRedactor.js +53 -0
  43. package/dist/logging/secretRedactor.js.map +1 -0
  44. package/dist/logging/shortenForLog.d.ts +8 -0
  45. package/dist/logging/shortenForLog.d.ts.map +1 -0
  46. package/dist/logging/shortenForLog.js +21 -0
  47. package/dist/logging/shortenForLog.js.map +1 -0
  48. package/dist/logging/variablesLoggingMiddleware.d.ts +22 -0
  49. package/dist/logging/variablesLoggingMiddleware.d.ts.map +1 -0
  50. package/dist/logging/variablesLoggingMiddleware.js +127 -0
  51. package/dist/logging/variablesLoggingMiddleware.js.map +1 -0
  52. package/dist/resolvers/GetDataResolver.d.ts.map +1 -1
  53. package/dist/resolvers/GetDataResolver.js +8 -4
  54. package/dist/resolvers/GetDataResolver.js.map +1 -1
  55. package/dist/resolvers/IntegrationDiscoveryResolver.d.ts +259 -2
  56. package/dist/resolvers/IntegrationDiscoveryResolver.d.ts.map +1 -1
  57. package/dist/resolvers/IntegrationDiscoveryResolver.js +1276 -117
  58. package/dist/resolvers/IntegrationDiscoveryResolver.js.map +1 -1
  59. package/dist/resolvers/IntegrationProgressResolver.d.ts +90 -0
  60. package/dist/resolvers/IntegrationProgressResolver.d.ts.map +1 -0
  61. package/dist/resolvers/IntegrationProgressResolver.js +196 -0
  62. package/dist/resolvers/IntegrationProgressResolver.js.map +1 -0
  63. package/dist/resolvers/MCPResolver.d.ts.map +1 -1
  64. package/dist/resolvers/MCPResolver.js +19 -14
  65. package/dist/resolvers/MCPResolver.js.map +1 -1
  66. package/dist/resolvers/QueryResolver.d.ts.map +1 -1
  67. package/dist/resolvers/QueryResolver.js +5 -4
  68. package/dist/resolvers/QueryResolver.js.map +1 -1
  69. package/dist/resolvers/QuerySystemUserResolver.d.ts.map +1 -1
  70. package/dist/resolvers/QuerySystemUserResolver.js +6 -18
  71. package/dist/resolvers/QuerySystemUserResolver.js.map +1 -1
  72. package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
  73. package/dist/resolvers/RunAIAgentResolver.js +32 -64
  74. package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
  75. package/dist/resolvers/RunTestResolver.d.ts.map +1 -1
  76. package/dist/resolvers/RunTestResolver.js +12 -2
  77. package/dist/resolvers/RunTestResolver.js.map +1 -1
  78. package/dist/resolvers/SearchEntitiesResolver.d.ts +46 -0
  79. package/dist/resolvers/SearchEntitiesResolver.d.ts.map +1 -0
  80. package/dist/resolvers/SearchEntitiesResolver.js +216 -0
  81. package/dist/resolvers/SearchEntitiesResolver.js.map +1 -0
  82. package/dist/resolvers/VectorizeEntityResolver.d.ts.map +1 -1
  83. package/dist/resolvers/VectorizeEntityResolver.js +14 -1
  84. package/dist/resolvers/VectorizeEntityResolver.js.map +1 -1
  85. package/dist/services/ScheduledJobsService.d.ts.map +1 -1
  86. package/dist/services/ScheduledJobsService.js +14 -2
  87. package/dist/services/ScheduledJobsService.js.map +1 -1
  88. package/package.json +75 -74
  89. package/src/__tests__/NoLog.test.ts +76 -0
  90. package/src/__tests__/bootAudit.test.ts +188 -0
  91. package/src/__tests__/boundaryLogPayload.test.ts +31 -0
  92. package/src/__tests__/getDataTokenRedaction.test.ts +84 -0
  93. package/src/__tests__/resolverBase.rls.test.ts +224 -0
  94. package/src/__tests__/secretRedactor.test.ts +163 -0
  95. package/src/__tests__/subscriptionRedaction.test.ts +217 -0
  96. package/src/agents/skip-sdk.ts +16 -13
  97. package/src/apolloServer/index.ts +58 -0
  98. package/src/config.ts +27 -0
  99. package/src/context.ts +2 -19
  100. package/src/generated/generated.ts +627 -15
  101. package/src/generic/FireAndForgetHeartbeat.ts +85 -0
  102. package/src/generic/__tests__/FireAndForgetHeartbeat.test.ts +99 -0
  103. package/src/index.ts +199 -19
  104. package/src/logging/NoLog.ts +88 -0
  105. package/src/logging/bootAudit.ts +117 -0
  106. package/src/logging/boundaryLogPayload.ts +19 -0
  107. package/src/logging/secretRedactor.ts +82 -0
  108. package/src/logging/shortenForLog.ts +17 -0
  109. package/src/logging/variablesLoggingMiddleware.ts +191 -0
  110. package/src/resolvers/GetDataResolver.ts +9 -5
  111. package/src/resolvers/IntegrationDiscoveryResolver.ts +1111 -120
  112. package/src/resolvers/IntegrationProgressResolver.ts +220 -0
  113. package/src/resolvers/MCPResolver.ts +18 -14
  114. package/src/resolvers/QueryResolver.ts +7 -6
  115. package/src/resolvers/QuerySystemUserResolver.ts +11 -25
  116. package/src/resolvers/RunAIAgentResolver.ts +34 -71
  117. package/src/resolvers/RunTestResolver.ts +14 -2
  118. package/src/resolvers/SearchEntitiesResolver.ts +173 -0
  119. package/src/resolvers/VectorizeEntityResolver.ts +14 -1
  120. package/src/resolvers/__tests__/IntegrationProgressResolver.test.ts +170 -0
  121. package/src/services/ScheduledJobsService.ts +15 -2
@@ -0,0 +1,51 @@
1
+ import { PubSubEngine } from 'type-graphql';
2
+ /** Default cadence for fire-and-forget liveness pulses (5 minutes). */
3
+ export declare const DEFAULT_PULSE_INTERVAL_MS: number;
4
+ /** The `type` discriminator carried by liveness pulse messages. */
5
+ export declare const HEARTBEAT_MESSAGE_TYPE = "Heartbeat";
6
+ /**
7
+ * Lightweight status snapshot included in an enriched pulse so the client can
8
+ * correlate the pulse with a run record and surface a coarse "still running"
9
+ * signal. Read from the in-memory run entity — never a DB query.
10
+ */
11
+ export interface PulseStatus {
12
+ /** Primary key of the persisted run record (AIAgentRun, TestRun, TestSuiteRun). */
13
+ runId?: string;
14
+ /** Current run status, e.g. 'Running'. */
15
+ status?: string;
16
+ /** Optional human-readable current step for UI display. */
17
+ currentStep?: string;
18
+ }
19
+ /** Handle returned by {@link startLivenessPulse}; call `stop()` when the work settles. */
20
+ export interface LivenessPulseHandle {
21
+ stop(): void;
22
+ }
23
+ export interface LivenessPulseOptions {
24
+ /** PubSub engine used to publish on the shared push-status topic. */
25
+ pubSub: PubSubEngine;
26
+ /** Session the client is subscribed on (used by the subscription filter). */
27
+ sessionId: string;
28
+ /** Resolver label echoed in the message envelope (e.g. 'RunAIAgentResolver'). */
29
+ resolver: string;
30
+ /** Pulse cadence in ms. Defaults to {@link DEFAULT_PULSE_INTERVAL_MS}. */
31
+ intervalMs?: number;
32
+ /**
33
+ * Optional cheap status reader invoked on each tick. Should read from an
34
+ * in-memory ref, not the database. Errors are swallowed so a transient read
35
+ * never kills the pulse loop.
36
+ */
37
+ readStatus?: () => PulseStatus | undefined;
38
+ }
39
+ /**
40
+ * Publishes a periodic liveness pulse on {@link PUSH_STATUS_UPDATES_TOPIC} while a
41
+ * fire-and-forget background operation runs. Each pulse resets the client's idle
42
+ * timer, so a long-but-active operation never spuriously times out. The pulse
43
+ * stops when the caller invokes `stop()` (typically in the background promise's
44
+ * `finally` block).
45
+ *
46
+ * The envelope matches the resolvers' existing push-status messages
47
+ * (`{ message: JSON.stringify({ resolver, type, status, data }), sessionId }`),
48
+ * so the client receives it through the same subscription with no special parsing.
49
+ */
50
+ export declare function startLivenessPulse(options: LivenessPulseOptions): LivenessPulseHandle;
51
+ //# sourceMappingURL=FireAndForgetHeartbeat.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FireAndForgetHeartbeat.d.ts","sourceRoot":"","sources":["../../src/generic/FireAndForgetHeartbeat.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAI5C,uEAAuE;AACvE,eAAO,MAAM,yBAAyB,QAAgB,CAAC;AAEvD,mEAAmE;AACnE,eAAO,MAAM,sBAAsB,cAAc,CAAC;AAElD;;;;GAIG;AACH,MAAM,WAAW,WAAW;IACxB,mFAAmF;IACnF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,0CAA0C;IAC1C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2DAA2D;IAC3D,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,0FAA0F;AAC1F,MAAM,WAAW,mBAAmB;IAChC,IAAI,IAAI,IAAI,CAAC;CAChB;AAED,MAAM,WAAW,oBAAoB;IACjC,qEAAqE;IACrE,MAAM,EAAE,YAAY,CAAC;IACrB,6EAA6E;IAC7E,SAAS,EAAE,MAAM,CAAC;IAClB,iFAAiF;IACjF,QAAQ,EAAE,MAAM,CAAC;IACjB,0EAA0E;IAC1E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,WAAW,GAAG,SAAS,CAAC;CAC9C;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,oBAAoB,GAAG,mBAAmB,CA2BrF"}
@@ -0,0 +1,44 @@
1
+ import { LogError } from '@memberjunction/core';
2
+ import { PUSH_STATUS_UPDATES_TOPIC } from './PushStatusResolver.js';
3
+ /** Default cadence for fire-and-forget liveness pulses (5 minutes). */
4
+ export const DEFAULT_PULSE_INTERVAL_MS = 5 * 60 * 1000;
5
+ /** The `type` discriminator carried by liveness pulse messages. */
6
+ export const HEARTBEAT_MESSAGE_TYPE = 'Heartbeat';
7
+ /**
8
+ * Publishes a periodic liveness pulse on {@link PUSH_STATUS_UPDATES_TOPIC} while a
9
+ * fire-and-forget background operation runs. Each pulse resets the client's idle
10
+ * timer, so a long-but-active operation never spuriously times out. The pulse
11
+ * stops when the caller invokes `stop()` (typically in the background promise's
12
+ * `finally` block).
13
+ *
14
+ * The envelope matches the resolvers' existing push-status messages
15
+ * (`{ message: JSON.stringify({ resolver, type, status, data }), sessionId }`),
16
+ * so the client receives it through the same subscription with no special parsing.
17
+ */
18
+ export function startLivenessPulse(options) {
19
+ const { pubSub, sessionId, resolver, readStatus } = options;
20
+ const intervalMs = options.intervalMs ?? DEFAULT_PULSE_INTERVAL_MS;
21
+ const timer = setInterval(() => {
22
+ let data;
23
+ try {
24
+ data = readStatus?.();
25
+ }
26
+ catch (e) {
27
+ // A status read failure must not break the liveness loop.
28
+ LogError(`[LivenessPulse:${resolver}] readStatus failed: ${e.message}`);
29
+ }
30
+ pubSub.publish(PUSH_STATUS_UPDATES_TOPIC, {
31
+ message: JSON.stringify({
32
+ resolver,
33
+ type: HEARTBEAT_MESSAGE_TYPE,
34
+ status: 'ok',
35
+ data: data ?? {},
36
+ }),
37
+ sessionId,
38
+ });
39
+ }, intervalMs);
40
+ return {
41
+ stop: () => clearInterval(timer),
42
+ };
43
+ }
44
+ //# sourceMappingURL=FireAndForgetHeartbeat.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FireAndForgetHeartbeat.js","sourceRoot":"","sources":["../../src/generic/FireAndForgetHeartbeat.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAChD,OAAO,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAC;AAEpE,uEAAuE;AACvE,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;AAEvD,mEAAmE;AACnE,MAAM,CAAC,MAAM,sBAAsB,GAAG,WAAW,CAAC;AAsClD;;;;;;;;;;GAUG;AACH,MAAM,UAAU,kBAAkB,CAAC,OAA6B;IAC5D,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC;IAC5D,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,yBAAyB,CAAC;IAEnE,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE;QAC3B,IAAI,IAA6B,CAAC;QAClC,IAAI,CAAC;YACD,IAAI,GAAG,UAAU,EAAE,EAAE,CAAC;QAC1B,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACT,0DAA0D;YAC1D,QAAQ,CAAC,kBAAkB,QAAQ,wBAAyB,CAAW,CAAC,OAAO,EAAE,CAAC,CAAC;QACvF,CAAC;QAED,MAAM,CAAC,OAAO,CAAC,yBAAyB,EAAE;YACtC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC;gBACpB,QAAQ;gBACR,IAAI,EAAE,sBAAsB;gBAC5B,MAAM,EAAE,IAAI;gBACZ,IAAI,EAAE,IAAI,IAAI,EAAE;aACnB,CAAC;YACF,SAAS;SACZ,CAAC,CAAC;IACP,CAAC,EAAE,UAAU,CAAC,CAAC;IAEf,OAAO;QACH,IAAI,EAAE,GAAG,EAAE,CAAC,aAAa,CAAC,KAAK,CAAC;KACnC,CAAC;AACN,CAAC"}
package/dist/index.d.ts CHANGED
@@ -22,6 +22,7 @@ export { configInfo, DEFAULT_SERVER_CONFIG } from './config.js';
22
22
  export { ServerExtensionLoader, BaseServerExtension } from '@memberjunction/server-extensions-core';
23
23
  export type { ServerExtensionConfig, ExtensionInitResult, ExtensionHealthResult } from '@memberjunction/server-extensions-core';
24
24
  export * from './directives/index.js';
25
+ export { NoLog, hasNoLogParameter, getNoLogFields } from './logging/NoLog.js';
25
26
  export * from './entitySubclasses/MJEntityPermissionEntityServer.server.js';
26
27
  export * from './types.js';
27
28
  export { getSystemUser, getSigningKeys, extractUserInfoFromPayload, verifyUserRecord, } from './auth/index.js';
@@ -40,6 +41,7 @@ export * from './resolvers/SearchKnowledgeStreamResolver.js';
40
41
  export * from './resolvers/AvailableSearchProvidersResolver.js';
41
42
  export * from './resolvers/FetchEntityVectorsResolver.js';
42
43
  export * from './resolvers/PipelineProgressResolver.js';
44
+ export * from './resolvers/IntegrationProgressResolver.js';
43
45
  export * from './resolvers/ClientToolRequestResolver.js';
44
46
  export * from './resolvers/AutotagPipelineResolver.js';
45
47
  export * from './resolvers/TagGovernanceResolver.js';
@@ -81,6 +83,7 @@ export * from './resolvers/FileResolver.js';
81
83
  export * from './resolvers/InfoResolver.js';
82
84
  export * from './resolvers/PotentialDuplicateRecordResolver.js';
83
85
  export * from './resolvers/RunTestResolver.js';
86
+ export * from './resolvers/SearchEntitiesResolver.js';
84
87
  export * from './resolvers/UserFavoriteResolver.js';
85
88
  export * from './resolvers/UserResolver.js';
86
89
  export * from './resolvers/UserViewResolver.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,OAAO,EAAY,gBAAgB,EAAmH,MAAM,sBAAsB,CAAC;AAQnL,OAAgB,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAM/C,OAAO,kBAAkB,CAAC;AAO1B,OAAO,EAA2H,cAAc,EAAE,MAAM,aAAa,CAAC;AAuCtK;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,IAAI,gBAAgB,CAE5C;AAED,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,cAAc,cAAc,CAAC;AAC7B,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAChE,OAAO,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,wCAAwC,CAAC;AACpG,YAAY,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,wCAAwC,CAAC;AAChI,cAAc,uBAAuB,CAAC;AACtC,OAAO,EAAE,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAC9E,cAAc,6DAA6D,CAAC;AAC5E,cAAc,YAAY,CAAC;AAC3B,OAAO,EACH,aAAa,EACb,cAAc,EACd,0BAA0B,EAC1B,gBAAgB,GACnB,MAAM,iBAAiB,CAAC;AACzB,cAAc,2BAA2B,CAAC;AAE1C,cAAc,iCAAiC,CAAC;AAChD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,wCAAwC,CAAC;AACvD,cAAc,2BAA2B,CAAC;AAC1C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,oCAAoC,CAAC;AACnD,cAAc,oCAAoC,CAAC;AACnD,cAAc,mCAAmC,CAAC;AAClD,cAAc,wCAAwC,CAAC;AACvD,cAAc,wCAAwC,CAAC;AACvD,cAAc,8CAA8C,CAAC;AAC7D,cAAc,iDAAiD,CAAC;AAChE,cAAc,2CAA2C,CAAC;AAC1D,cAAc,yCAAyC,CAAC;AACxD,cAAc,4CAA4C,CAAC;AAC3D,cAAc,0CAA0C,CAAC;AACzD,cAAc,wCAAwC,CAAC;AACvD,cAAc,sCAAsC,CAAC;AACrD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,kCAAkC,CAAC;AACjD,cAAc,iCAAiC,CAAC;AAChD,cAAc,kCAAkC,CAAC;AAEjD,cAAc,wBAAwB,CAAC;AACvC,cAAc,sBAAsB,CAAC;AAErC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,0CAA0C,CAAC;AACzD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,yCAAyC,CAAC;AACxD,cAAc,qCAAqC,CAAC;AACpD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,qCAAqC,CAAC;AACpD,cAAc,yCAAyC,CAAC;AACxD,cAAc,uCAAuC,CAAC;AACtD,cAAc,iCAAiC,CAAC;AAChD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,2CAA2C,CAAC;AAC1D,cAAc,yCAAyC,CAAC;AACxD,cAAc,wCAAwC,CAAC;AACvD,cAAc,kCAAkC,CAAC;AACjD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,mCAAmC,CAAC;AAClD,cAAc,6CAA6C,CAAC;AAC5D,cAAc,iCAAiC,CAAC;AAChD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,kCAAkC,CAAC;AACjD,cAAc,qCAAqC,CAAC;AACpD,cAAc,qCAAqC,CAAC;AACpD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,iDAAiD,CAAC;AAChE,cAAc,gCAAgC,CAAC;AAC/C,cAAc,uCAAuC,CAAC;AACtD,cAAc,qCAAqC,CAAC;AACpD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,iCAAiC,CAAC;AAChD,cAAc,uCAAuC,CAAC;AACtD,cAAc,2CAA2C,CAAC;AAC1D,cAAc,4BAA4B,CAAC;AAC3C,OAAO,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAErH,cAAc,0BAA0B,CAAC;AACzC,cAAc,yBAAyB,CAAC;AACxC,cAAc,uBAAuB,CAAC;AAQtC,MAAM,MAAM,eAAe,GAAG;IAC5B,aAAa,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,cAAc,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC;CAC1C,CAAC;AAUF,eAAO,MAAM,SAAS,QAAO,WAAwB,CAAC;AAEtD,eAAO,MAAM,KAAK,GAAU,eAAe,KAAK,CAAC,MAAM,CAAC,EAAE,MAAK,WAAyB,EAAE,UAAU,eAAe,KAAG,OAAO,CAAC,IAAI,CAs6BjI,CAAC"}
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@ import dotenv from 'dotenv';
2
2
  dotenv.config({ quiet: true });
3
3
  import { expressMiddleware } from '@as-integrations/express5';
4
4
  import { mergeSchemas } from '@graphql-tools/schema';
5
- import { Metadata, SetProvider, StartupManager as StartupManagerImport, BaseEntity, RunView } from '@memberjunction/core';
5
+ import { Metadata, SetProvider, StartupManager as StartupManagerImport, BaseEntity, RunView, DatabaseProviderBase } from '@memberjunction/core';
6
6
  import { resolveDbPlatformFromEnv } from '@memberjunction/generic-database-provider';
7
7
  import { MJGlobal, MJEventType, UUIDsEqual, ShutdownRegistry } from '@memberjunction/global';
8
8
  import { setupSQLServerClient, SQLServerDataProvider, SQLServerProviderConfigData, UserCache } from '@memberjunction/sqlserver-dataprovider';
@@ -24,8 +24,11 @@ import sql from 'mssql';
24
24
  import { WebSocketServer } from 'ws';
25
25
  import buildApolloServer from './apolloServer/index.js';
26
26
  import { configInfo, dbDatabase, dbHost, dbPort, dbUsername, graphqlPort, graphqlRootPath, mj_core_schema, websiteRunFromPackage } from './config.js';
27
+ import { default as jwt } from 'jsonwebtoken';
27
28
  import { contextFunction, createUnifiedAuthMiddleware, getUserPayload } from './context.js';
28
29
  import { requireSystemUserDirective, publicDirective } from './directives/index.js';
30
+ import { variablesLoggingMiddleware } from './logging/variablesLoggingMiddleware.js';
31
+ import { auditResolversForUndecoratedArgs } from './logging/bootAudit.js';
29
32
  import createMSSQLConfig from './orm.js';
30
33
  import { setupRESTEndpoints } from './rest/setupRESTEndpoints.js';
31
34
  import { createOAuthCallbackHandler } from './rest/OAuthCallbackHandler.js';
@@ -36,7 +39,9 @@ import { LocalCacheManager, TelemetryManager } from '@memberjunction/core';
36
39
  import { GetAPIKeyEngine } from '@memberjunction/api-keys';
37
40
  import { RedisLocalStorageProvider } from '@memberjunction/redis-provider';
38
41
  import { PubSubManager } from './generic/PubSubManager.js';
39
- import { ClientToolRequestManager } from '@memberjunction/ai-agents';
42
+ import { IntegrationProgressEmitter } from '@memberjunction/integration-progress-artifacts';
43
+ import { PublishIntegrationProgress } from './resolvers/IntegrationProgressResolver.js';
44
+ import { ClientToolRequestManager, AgentRunWatchdog } from '@memberjunction/ai-agents';
40
45
  import { CACHE_INVALIDATION_TOPIC } from './generic/CacheInvalidationResolver.js';
41
46
  import { ConnectorFactory, IntegrationEngine } from '@memberjunction/integration-engine';
42
47
  import { CronExpressionHelper } from '@memberjunction/scheduling-engine';
@@ -63,6 +68,7 @@ export { NewUserBase } from './auth/newUsers.js';
63
68
  export { configInfo, DEFAULT_SERVER_CONFIG } from './config.js';
64
69
  export { ServerExtensionLoader, BaseServerExtension } from '@memberjunction/server-extensions-core';
65
70
  export * from './directives/index.js';
71
+ export { NoLog, hasNoLogParameter, getNoLogFields } from './logging/NoLog.js';
66
72
  export * from './entitySubclasses/MJEntityPermissionEntityServer.server.js';
67
73
  export * from './types.js';
68
74
  export { getSystemUser, getSigningKeys, extractUserInfoFromPayload, verifyUserRecord, } from './auth/index.js';
@@ -81,6 +87,7 @@ export * from './resolvers/SearchKnowledgeStreamResolver.js';
81
87
  export * from './resolvers/AvailableSearchProvidersResolver.js';
82
88
  export * from './resolvers/FetchEntityVectorsResolver.js';
83
89
  export * from './resolvers/PipelineProgressResolver.js';
90
+ export * from './resolvers/IntegrationProgressResolver.js';
84
91
  export * from './resolvers/ClientToolRequestResolver.js';
85
92
  export * from './resolvers/AutotagPipelineResolver.js';
86
93
  export * from './resolvers/TagGovernanceResolver.js';
@@ -122,6 +129,7 @@ export * from './resolvers/FileResolver.js';
122
129
  export * from './resolvers/InfoResolver.js';
123
130
  export * from './resolvers/PotentialDuplicateRecordResolver.js';
124
131
  export * from './resolvers/RunTestResolver.js';
132
+ export * from './resolvers/SearchEntitiesResolver.js';
125
133
  export * from './resolvers/UserFavoriteResolver.js';
126
134
  export * from './resolvers/UserResolver.js';
127
135
  export * from './resolvers/UserViewResolver.js';
@@ -233,6 +241,75 @@ export const serve = async (resolverPaths, app = createApp(), options) => {
233
241
  }
234
242
  return origExecuteSQLWithPool.call(this, pool, query, parameters, contextUser);
235
243
  };
244
+ // Set up CodeGen-credentialed provider + in-process CodeGen runner for RSU (PostgreSQL).
245
+ // Without this, RuntimeSchemaManager has no injected runner and falls back to spawning a
246
+ // child-process CodeGen. On Postgres that child path is slow, its output is buffered (so
247
+ // progress is unobservable), and a non-responding advancedGen AI pass hangs it indefinitely
248
+ // — which leaves association/junction CRUD functions un-regenerated and blocks the sync.
249
+ // Running in-process keeps CodeGen inside the MJAPI event loop where it is observable and
250
+ // bounded by advanced_generation's per-call timeout + circuit breaker. Mirrors the SQL
251
+ // Server path below (the runner is required for BOTH platforms).
252
+ const pgCodegenUser = process.env.CODEGEN_DB_USERNAME;
253
+ const pgCodegenPass = process.env.CODEGEN_DB_PASSWORD;
254
+ if (pgCodegenUser && pgCodegenPass) {
255
+ try {
256
+ const codegenPgPool = new pg.default.Pool({
257
+ host: pgHost,
258
+ port: pgPort,
259
+ user: pgCodegenUser,
260
+ password: pgCodegenPass,
261
+ database: pgDatabase,
262
+ max: 10,
263
+ });
264
+ const codegenTestClient = await codegenPgPool.connect();
265
+ await codegenTestClient.query('SELECT 1');
266
+ codegenTestClient.release();
267
+ const { RuntimeSchemaManager } = await import('@memberjunction/schema-engine');
268
+ const codegenPgConfigData = new PostgreSQLProviderConfigData({ Host: pgHost, Port: pgPort, Database: pgDatabase, User: pgCodegenUser, Password: pgCodegenPass }, mj_core_schema, cacheRefreshInterval / 1000);
269
+ const codegenPgProvider = new PostgreSQLDataProvider();
270
+ await codegenPgProvider.Config(codegenPgConfigData); // separate pool (per-instance manager); does NOT touch the global API provider
271
+ RuntimeSchemaManager.Instance.SetDDLProvider(codegenPgProvider);
272
+ console.log('RSU DDL provider initialized with CodeGen credentials (PostgreSQL).');
273
+ // Set up in-process CodeGen runner for RSU
274
+ try {
275
+ const { RunCodeGenBase } = await import('@memberjunction/codegen-lib');
276
+ const { PostgreSQLCodeGenConnection } = await import('@memberjunction/codegen-lib/dist/Database/providers/postgresql/PostgreSQLCodeGenConnection.js');
277
+ const codegenConnection = new PostgreSQLCodeGenConnection(codegenPgPool);
278
+ const codegenCurrentUser = UserCache.Instance.Users.find(u => u.Type?.trim().toLowerCase() === 'owner') ?? UserCache.Instance.Users[0];
279
+ const codegenDataSource = {
280
+ provider: codegenPgProvider,
281
+ connection: codegenConnection,
282
+ currentUser: codegenCurrentUser,
283
+ connectionInfo: `${pgHost}:${pgPort}/${pgDatabase} (CodeGen)`,
284
+ };
285
+ const runObject = MJGlobal.Instance.ClassFactory.CreateInstance(RunCodeGenBase);
286
+ const rsuWorkDir = process.env.RSU_WORK_DIR || process.cwd();
287
+ RuntimeSchemaManager.Instance.SetCodeGenRunner({
288
+ RunInProcess: (skipDB) => runObject.RunInProcess(codegenDataSource, skipDB, rsuWorkDir),
289
+ });
290
+ console.log('RSU in-process CodeGen runner initialized (PostgreSQL).');
291
+ // Inject CodeGen output paths for targeted git staging
292
+ const { initializeConfig } = await import('@memberjunction/codegen-lib');
293
+ const cgConfig = initializeConfig(rsuWorkDir);
294
+ const outputPaths = (cgConfig.output ?? []).map((o) => o.directory);
295
+ RuntimeSchemaManager.Instance.SetCodeGenOutputPaths(outputPaths);
296
+ console.log(`RSU CodeGen output paths: ${outputPaths.length} directories configured.`);
297
+ // Point RSU's soft PK/FK writer at the SAME file CodeGen reads (mj.config.cjs
298
+ // `additionalSchemaInfo`), or RSU writes soft PKs to its own default path while
299
+ // CodeGen reads a different one and skips integration tables with "No primary key found".
300
+ if (cgConfig.additionalSchemaInfo) {
301
+ RuntimeSchemaManager.Instance.SetAdditionalSchemaInfoPath(cgConfig.additionalSchemaInfo);
302
+ console.log(`RSU additionalSchemaInfo path: ${cgConfig.additionalSchemaInfo}`);
303
+ }
304
+ }
305
+ catch (codegenErr) {
306
+ console.warn(`RSU in-process CodeGen runner setup failed (will fall back to child process): ${codegenErr.message}`);
307
+ }
308
+ }
309
+ catch (err) {
310
+ console.warn(`RSU DDL provider setup failed (PostgreSQL; RSU will fall back to default provider): ${err.message}`);
311
+ }
312
+ }
236
313
  const md = new Metadata(); // global-provider-ok: bootstrap
237
314
  console.log(`Data Source has been initialized. ${md?.Entities ? md.Entities.length : 0} entities loaded.`);
238
315
  }
@@ -279,6 +356,13 @@ export const serve = async (resolverPaths, app = createApp(), options) => {
279
356
  ...createMSSQLConfig(),
280
357
  user: codegenUser,
281
358
  password: codegenPass,
359
+ // CodeGen's metadata management ("manage entity fields", schema refresh) runs
360
+ // long-running queries across ALL entities. The default API requestTimeout (30s)
361
+ // is far too short at scale — e.g. a large integration Create-Tables pushing the
362
+ // schema to 400+ entities times out mid-refresh and leaves entity-field metadata
363
+ // only partially applied. This pool is used ONLY for CodeGen/DDL (never to serve
364
+ // API requests), so a generous timeout is safe. Mirrors MJCLI's baseline connection.
365
+ requestTimeout: 600000,
282
366
  });
283
367
  codegenPool.on('error', (err) => {
284
368
  console.error('[ConnectionPool] CodeGen pool connection error:', err.message);
@@ -314,6 +398,14 @@ export const serve = async (resolverPaths, app = createApp(), options) => {
314
398
  const outputPaths = (codegenConfig.output ?? []).map((o) => o.directory);
315
399
  RuntimeSchemaManager.Instance.SetCodeGenOutputPaths(outputPaths);
316
400
  console.log(`RSU CodeGen output paths: ${outputPaths.length} directories configured.`);
401
+ // Point RSU's soft PK/FK writer at the SAME file CodeGen reads (mj.config.cjs
402
+ // `additionalSchemaInfo`). Without this, RSU writes soft PKs to its own default
403
+ // path while CodeGen reads a different one and skips every integration table
404
+ // with "No primary key found".
405
+ if (codegenConfig.additionalSchemaInfo) {
406
+ RuntimeSchemaManager.Instance.SetAdditionalSchemaInfoPath(codegenConfig.additionalSchemaInfo);
407
+ console.log(`RSU additionalSchemaInfo path: ${codegenConfig.additionalSchemaInfo}`);
408
+ }
317
409
  }
318
410
  catch (codegenErr) {
319
411
  console.warn(`RSU in-process CodeGen runner setup failed (will fall back to child process): ${codegenErr.message}`);
@@ -511,6 +603,21 @@ export const serve = async (resolverPaths, app = createApp(), options) => {
511
603
  // Wire the ClientToolRequestManager so BaseAgent can publish client tool requests
512
604
  // via the same PubSub infrastructure used for pipeline progress and cache invalidation.
513
605
  ClientToolRequestManager.Instance.SetPublishFunction((topic, payload) => PubSubManager.Instance.Publish(topic, payload));
606
+ // §11: fan every integration progress event onto the live GraphQL subscription topic. The emitter
607
+ // (in @memberjunction/integration-progress-artifacts) stays server-agnostic; we inject the publish
608
+ // here so integration runs/refreshes/syncs are subscribable in addition to the durable JSONL
609
+ // artifact + the pollable IntegrationTailRunEvents query.
610
+ IntegrationProgressEmitter.SetPublishHook((manifest, event) => PublishIntegrationProgress({
611
+ RunID: manifest.runID,
612
+ Kind: manifest.runKind,
613
+ CompanyIntegrationID: manifest.companyIntegrationID,
614
+ EventType: event.eventType,
615
+ Seq: event.seq,
616
+ Message: event.message,
617
+ Stage: event.stage,
618
+ Level: event.level,
619
+ Data: event.data,
620
+ }));
514
621
  // Global listener: broadcast CACHE_INVALIDATION to all browser clients whenever
515
622
  // ANY BaseEntity save/delete occurs on this server — regardless of whether it
516
623
  // originated from a GraphQL mutation or internal server-side code (agents, actions,
@@ -541,10 +648,14 @@ export const serve = async (resolverPaths, app = createApp(), options) => {
541
648
  scalarsMap: [{ type: Date, scalar: GraphQLTimestamp }],
542
649
  emitSchemaFile: websiteRunFromPackage !== 1,
543
650
  pubSub,
651
+ globalMiddlewares: [variablesLoggingMiddleware],
544
652
  }),
545
653
  ],
546
654
  typeDefs: [requireSystemUserDirective.typeDefs, publicDirective.typeDefs],
547
655
  });
656
+ // Verbose-mode-only diagnostic: name custom-resolver args that aren't metadata-bound
657
+ // and aren't @NoLog-marked. No-op in default config (logVariables=false).
658
+ auditResolversForUndecoratedArgs();
548
659
  schema = requireSystemUserDirective.transformer(schema);
549
660
  schema = publicDirective.transformer(schema);
550
661
  // Apply middleware-contributed schema transformers (after built-in directive transformers)
@@ -554,25 +665,67 @@ export const serve = async (resolverPaths, app = createApp(), options) => {
554
665
  tServe = lap('Schema Build', tServe);
555
666
  const httpServer = createServer(app);
556
667
  const webSocketServer = new WebSocketServer({ server: httpServer, path: graphqlRootPath });
668
+ // Track per-connection expiry timers so we can clean them up on close
669
+ const expiryTimers = new WeakMap();
557
670
  const serverCleanup = useServer({
558
671
  schema,
559
- context: async ({ connectionParams }) => {
560
- const userPayload = await getUserPayload(String(connectionParams?.Authorization), undefined, dataSources);
561
- return { userPayload };
562
- },
563
- onError: (ctx, message, errors) => {
564
- // Check if error is token expiration (expected behavior)
565
- const isTokenExpired = errors.some(err => err.extensions?.code === 'JWT_EXPIRED' ||
566
- err.message?.includes('token has expired'));
567
- if (isTokenExpired) {
568
- // Log at warn level - this is expected from long-lived browser sessions
569
- console.warn('WebSocket connection token expired - client should reconnect with refreshed token');
672
+ // RAII: validate the token once at connection time and cache the result.
673
+ // This prevents re-validating (and re-logging) on every subscription message.
674
+ onConnect: async (ctx) => {
675
+ try {
676
+ const token = String(ctx.connectionParams?.Authorization);
677
+ const userPayload = await getUserPayload(token, undefined, dataSources);
678
+ // Store validated payload on the connection for use in context()
679
+ ctx.extra.userPayload = userPayload;
680
+ // Schedule proactive socket close at token expiry so the client
681
+ // reconnects with a fresh token instead of spamming errors.
682
+ const decoded = jwt.decode(token.replace('Bearer ', ''));
683
+ if (decoded && typeof decoded !== 'string' && decoded.exp) {
684
+ const msUntilExpiry = decoded.exp * 1000 - Date.now();
685
+ if (msUntilExpiry > 0) {
686
+ const timer = setTimeout(() => {
687
+ console.log(`WebSocket token expiring — closing connection for ${userPayload.email}`);
688
+ // 4403 = Forbidden — retriable in graphql-ws, signals "get a new token"
689
+ // (4401 is reserved as fatal/"Unauthorized" in the graphql-ws protocol)
690
+ try {
691
+ ctx.extra.socket.close(4403, 'Token expired');
692
+ }
693
+ catch {
694
+ // Socket may already be closed; benign
695
+ }
696
+ }, msUntilExpiry);
697
+ // Prevent the timer from keeping the process alive during shutdown
698
+ timer.unref();
699
+ expiryTimers.set(ctx, timer);
700
+ }
701
+ }
702
+ return true;
570
703
  }
571
- else {
572
- // Log actual errors at error level
573
- console.error('WebSocket error:', errors);
704
+ catch (error) {
705
+ // Return false instead of throwing — graphql-ws sends 4403 Forbidden
706
+ // (retriable) rather than letting the throw produce 1011 (fatal).
707
+ const msg = error instanceof Error ? error.message : 'Authentication failed';
708
+ console.warn(`WebSocket connection rejected: ${msg}`);
709
+ return false;
574
710
  }
575
711
  },
712
+ // context() now just reads the already-validated payload — no I/O, no logging
713
+ context: async (ctx) => {
714
+ return { userPayload: ctx.extra.userPayload };
715
+ },
716
+ onClose: (ctx) => {
717
+ // Clear the expiry timer if the connection closes before the token expires
718
+ const timer = expiryTimers.get(ctx);
719
+ if (timer) {
720
+ clearTimeout(timer);
721
+ expiryTimers.delete(ctx);
722
+ }
723
+ },
724
+ onError: (_ctx, _message, errors) => {
725
+ // Token expiry errors can't happen anymore (handled in onConnect + timer),
726
+ // so all errors here are genuine and worth logging.
727
+ console.error('WebSocket error:', errors);
728
+ },
576
729
  }, webSocketServer);
577
730
  const apolloServer = buildApolloServer({ schema }, { httpServer, serverCleanup }, mwApolloPlugins.length > 0 ? mwApolloPlugins : undefined);
578
731
  await apolloServer.start();
@@ -729,6 +882,14 @@ export const serve = async (resolverPaths, app = createApp(), options) => {
729
882
  IntegrationEngine.Instance.ResumeOrphanedSyncs(resumeUser)
730
883
  .catch(err => console.warn(`[IntegrationEngine] Orphaned sync resume failed: ${err}`));
731
884
  }
885
+ // Force-fail any agent runs left 'Running' by a process that died (restart/crash/OOM) or whose
886
+ // terminal-state write never landed. Staleness-based, so it never touches runs another healthy
887
+ // instance is still heart-beating. The watchdog also self-registers for graceful-shutdown
888
+ // cancellation (via ShutdownRegistry) once it begins tracking this process's first live run.
889
+ if (resumeUser && Metadata.Provider instanceof DatabaseProviderBase) { // global-provider-ok: server startup recovery — one-shot orphaned-run sweep at boot
890
+ AgentRunWatchdog.SweepOrphanedRuns(Metadata.Provider, resumeUser) // global-provider-ok: server startup recovery — one-shot orphaned-run sweep at boot
891
+ .catch(err => console.warn(`[AgentRunWatchdog] Startup sweep failed: ${err}`));
892
+ }
732
893
  // Set up graceful shutdown handlers
733
894
  const gracefulShutdown = async (signal) => {
734
895
  console.log(`\n${signal} received, shutting down gracefully...`);