@memberjunction/server 5.40.2 → 5.42.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.
- package/dist/agentSessions/HostInstance.d.ts +19 -0
- package/dist/agentSessions/HostInstance.d.ts.map +1 -0
- package/dist/agentSessions/HostInstance.js +48 -0
- package/dist/agentSessions/HostInstance.js.map +1 -0
- package/dist/agentSessions/SessionJanitor.d.ts +97 -0
- package/dist/agentSessions/SessionJanitor.d.ts.map +1 -0
- package/dist/agentSessions/SessionJanitor.js +222 -0
- package/dist/agentSessions/SessionJanitor.js.map +1 -0
- package/dist/agentSessions/SessionManager.d.ts +142 -0
- package/dist/agentSessions/SessionManager.d.ts.map +1 -0
- package/dist/agentSessions/SessionManager.js +308 -0
- package/dist/agentSessions/SessionManager.js.map +1 -0
- package/dist/agentSessions/index.d.ts +5 -0
- package/dist/agentSessions/index.d.ts.map +1 -0
- package/dist/agentSessions/index.js +31 -0
- package/dist/agentSessions/index.js.map +1 -0
- package/dist/agentSessions/remoteBrowserGoalEngine.d.ts +102 -0
- package/dist/agentSessions/remoteBrowserGoalEngine.d.ts.map +1 -0
- package/dist/agentSessions/remoteBrowserGoalEngine.js +164 -0
- package/dist/agentSessions/remoteBrowserGoalEngine.js.map +1 -0
- package/dist/agentSessions/remoteBrowserGoalRegistry.d.ts +66 -0
- package/dist/agentSessions/remoteBrowserGoalRegistry.d.ts.map +1 -0
- package/dist/agentSessions/remoteBrowserGoalRegistry.js +86 -0
- package/dist/agentSessions/remoteBrowserGoalRegistry.js.map +1 -0
- package/dist/auth/initializeProviders.d.ts.map +1 -1
- package/dist/auth/initializeProviders.js +4 -2
- package/dist/auth/initializeProviders.js.map +1 -1
- package/dist/config.d.ts +7 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +12 -2
- package/dist/config.js.map +1 -1
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +41 -7
- package/dist/context.js.map +1 -1
- package/dist/generated/generated.d.ts +1296 -49
- package/dist/generated/generated.d.ts.map +1 -1
- package/dist/generated/generated.js +50343 -43421
- package/dist/generated/generated.js.map +1 -1
- package/dist/generic/ResolverBase.js +1 -1
- package/dist/generic/ResolverBase.js.map +1 -1
- package/dist/generic/RunViewResolver.js +9 -10
- package/dist/generic/RunViewResolver.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +127 -54
- package/dist/index.js.map +1 -1
- package/dist/integration/CustomColumnPromoter.d.ts +108 -0
- package/dist/integration/CustomColumnPromoter.d.ts.map +1 -0
- package/dist/integration/CustomColumnPromoter.js +508 -0
- package/dist/integration/CustomColumnPromoter.js.map +1 -0
- package/dist/logging/StartupLogger.d.ts +177 -0
- package/dist/logging/StartupLogger.d.ts.map +1 -0
- package/dist/logging/StartupLogger.js +354 -0
- package/dist/logging/StartupLogger.js.map +1 -0
- package/dist/logging/variablesLoggingMiddleware.d.ts.map +1 -1
- package/dist/logging/variablesLoggingMiddleware.js +21 -2
- package/dist/logging/variablesLoggingMiddleware.js.map +1 -1
- package/dist/resolvers/AgentSessionResolver.d.ts +42 -0
- package/dist/resolvers/AgentSessionResolver.d.ts.map +1 -0
- package/dist/resolvers/AgentSessionResolver.js +152 -0
- package/dist/resolvers/AgentSessionResolver.js.map +1 -0
- package/dist/resolvers/EntityPermissionResolver.d.ts +16 -0
- package/dist/resolvers/EntityPermissionResolver.d.ts.map +1 -0
- package/dist/resolvers/EntityPermissionResolver.js +95 -0
- package/dist/resolvers/EntityPermissionResolver.js.map +1 -0
- package/dist/resolvers/ExecuteRemoteOperationResolver.d.ts +27 -0
- package/dist/resolvers/ExecuteRemoteOperationResolver.d.ts.map +1 -0
- package/dist/resolvers/ExecuteRemoteOperationResolver.js +136 -0
- package/dist/resolvers/ExecuteRemoteOperationResolver.js.map +1 -0
- package/dist/resolvers/IntegrationDiscoveryResolver.d.ts +101 -1
- package/dist/resolvers/IntegrationDiscoveryResolver.d.ts.map +1 -1
- package/dist/resolvers/IntegrationDiscoveryResolver.js +544 -42
- package/dist/resolvers/IntegrationDiscoveryResolver.js.map +1 -1
- package/dist/resolvers/RealtimeBridgeResolver.d.ts +121 -0
- package/dist/resolvers/RealtimeBridgeResolver.d.ts.map +1 -0
- package/dist/resolvers/RealtimeBridgeResolver.js +533 -0
- package/dist/resolvers/RealtimeBridgeResolver.js.map +1 -0
- package/dist/resolvers/RealtimeClientSessionResolver.d.ts +688 -0
- package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -0
- package/dist/resolvers/RealtimeClientSessionResolver.js +1774 -0
- package/dist/resolvers/RealtimeClientSessionResolver.js.map +1 -0
- package/dist/resolvers/RemoteBrowserActionResolver.d.ts +432 -0
- package/dist/resolvers/RemoteBrowserActionResolver.d.ts.map +1 -0
- package/dist/resolvers/RemoteBrowserActionResolver.js +1108 -0
- package/dist/resolvers/RemoteBrowserActionResolver.js.map +1 -0
- package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.js +0 -7
- package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
- package/dist/rest/setupRESTEndpoints.d.ts.map +1 -1
- package/dist/rest/setupRESTEndpoints.js +5 -3
- package/dist/rest/setupRESTEndpoints.js.map +1 -1
- package/dist/services/ScheduledJobsService.d.ts.map +1 -1
- package/dist/services/ScheduledJobsService.js +6 -5
- package/dist/services/ScheduledJobsService.js.map +1 -1
- package/package.json +83 -74
- package/src/__tests__/RealtimeBridgeResolver.test.ts +100 -0
- package/src/__tests__/RealtimeClientSessionResolver.test.ts +2605 -0
- package/src/__tests__/RemoteBrowserAudioStream.test.ts +181 -0
- package/src/__tests__/RemoteBrowserGoalEngine.test.ts +144 -0
- package/src/__tests__/SessionJanitor.test.ts +234 -0
- package/src/__tests__/SessionManager.test.ts +465 -0
- package/src/__tests__/remoteBrowserGoalRegistry.test.ts +81 -0
- package/src/__tests__/subscriptionRedaction.test.ts +5 -0
- package/src/agentSessions/HostInstance.ts +53 -0
- package/src/agentSessions/SessionJanitor.ts +267 -0
- package/src/agentSessions/SessionManager.ts +446 -0
- package/src/agentSessions/index.ts +32 -0
- package/src/agentSessions/remoteBrowserGoalEngine.ts +191 -0
- package/src/agentSessions/remoteBrowserGoalRegistry.ts +122 -0
- package/src/auth/initializeProviders.ts +4 -2
- package/src/config.ts +13 -2
- package/src/context.ts +42 -7
- package/src/generated/generated.ts +33808 -29025
- package/src/generic/ResolverBase.ts +1 -1
- package/src/generic/RunViewResolver.ts +9 -9
- package/src/index.ts +128 -54
- package/src/integration/CustomColumnPromoter.ts +597 -0
- package/src/logging/StartupLogger.ts +441 -0
- package/src/logging/variablesLoggingMiddleware.ts +25 -5
- package/src/resolvers/AgentSessionResolver.ts +138 -0
- package/src/resolvers/EntityPermissionResolver.ts +73 -0
- package/src/resolvers/ExecuteRemoteOperationResolver.ts +108 -0
- package/src/resolvers/IntegrationDiscoveryResolver.ts +396 -37
- package/src/resolvers/RealtimeBridgeResolver.ts +450 -0
- package/src/resolvers/RealtimeClientSessionResolver.ts +2162 -0
- package/src/resolvers/RemoteBrowserActionResolver.ts +1122 -0
- package/src/resolvers/RunAIAgentResolver.ts +1 -9
- package/src/rest/setupRESTEndpoints.ts +5 -3
- package/src/services/ScheduledJobsService.ts +6 -5
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { BaseSingleton } from '@memberjunction/global';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Terminal/structural outcome of a remote-browser goal run — the fields the client needs to render
|
|
5
|
+
* the result, decoupled from the GraphQL `RemoteBrowserGoalResultType` (avoids a resolver↔registry
|
|
6
|
+
* import cycle) and from the engine's `RemoteBrowserGoalResult` (whose `Strategy` is a string union).
|
|
7
|
+
*/
|
|
8
|
+
export interface RemoteBrowserGoalOutcome {
|
|
9
|
+
Success: boolean;
|
|
10
|
+
Strategy?: string;
|
|
11
|
+
CurrentUrl?: string;
|
|
12
|
+
Status?: string;
|
|
13
|
+
StepCount?: number;
|
|
14
|
+
Detail?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
type GoalRunStatus = 'Running' | 'Complete';
|
|
18
|
+
|
|
19
|
+
/** One tracked goal run: its lifecycle status plus the terminal outcome once it finishes. */
|
|
20
|
+
export interface GoalRunRecord {
|
|
21
|
+
GoalRunID: string;
|
|
22
|
+
/** Lowercased `AIAgentSession` id the goal runs against (one in-flight goal per session). */
|
|
23
|
+
AgentSessionID: string;
|
|
24
|
+
Status: GoalRunStatus;
|
|
25
|
+
Outcome?: RemoteBrowserGoalOutcome;
|
|
26
|
+
StartedAt: number;
|
|
27
|
+
CompletedAt?: number;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Default time a COMPLETED record is retained so a slow poller can still read it (then swept). */
|
|
31
|
+
const COMPLETED_TTL_MS = 5 * 60 * 1000;
|
|
32
|
+
/** Safety cap: a record that never completes (e.g. a crashed loop) is swept after this long. */
|
|
33
|
+
const RUNNING_MAX_MS = 30 * 60 * 1000;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Process-local registry of in-flight + recently-completed remote-browser GOAL runs.
|
|
37
|
+
*
|
|
38
|
+
* ## Why this exists
|
|
39
|
+
* A goal-driven browser run (computer-use loop) can take many minutes. Running it inside a single
|
|
40
|
+
* synchronous GraphQL mutation means the client holds one long-lived HTTP request open for the whole
|
|
41
|
+
* loop — fragile against browser fetch timeouts, proxy/ngrok idle limits, and session-janitor churn.
|
|
42
|
+
* Observed symptom: the loop completes successfully server-side but the client's request has already
|
|
43
|
+
* died, so the agent gets "no response from the server" and never learns the outcome.
|
|
44
|
+
*
|
|
45
|
+
* The fix is async: the start mutation kicks the goal off and registers it here (status `Running`),
|
|
46
|
+
* returning a `GoalRunID` immediately; the client then POLLS a short query that reads this registry
|
|
47
|
+
* until the run is `Complete`. Every request stays short, so no transport boundary times out.
|
|
48
|
+
*
|
|
49
|
+
* Keyed by `AIAgentSession` id (at most one in-flight goal per session, mirroring the engine's
|
|
50
|
+
* per-session abort registry). Process-local is correct: the browser session — and thus the goal —
|
|
51
|
+
* is pinned to the MJAPI instance hosting its CDP connection, the same instance the client's
|
|
52
|
+
* session-scoped mutations already target.
|
|
53
|
+
*/
|
|
54
|
+
export class RemoteBrowserGoalRegistry extends BaseSingleton<RemoteBrowserGoalRegistry> {
|
|
55
|
+
private runs = new Map<string, GoalRunRecord>();
|
|
56
|
+
|
|
57
|
+
protected constructor() {
|
|
58
|
+
super();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
public static get Instance(): RemoteBrowserGoalRegistry {
|
|
62
|
+
return super.getInstance<RemoteBrowserGoalRegistry>();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
private key(agentSessionID: string): string {
|
|
66
|
+
return (agentSessionID ?? '').toLowerCase();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Registers a newly-started goal as `Running`, replacing any prior record for the session. */
|
|
70
|
+
public Begin(agentSessionID: string, goalRunID: string): void {
|
|
71
|
+
this.sweep();
|
|
72
|
+
this.runs.set(this.key(agentSessionID), {
|
|
73
|
+
GoalRunID: goalRunID,
|
|
74
|
+
AgentSessionID: this.key(agentSessionID),
|
|
75
|
+
Status: 'Running',
|
|
76
|
+
StartedAt: Date.now(),
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Marks a goal run `Complete` with its terminal outcome. No-op when the session's current record is
|
|
82
|
+
* for a DIFFERENT `goalRunID` (a newer goal superseded it) — the stale completion is dropped.
|
|
83
|
+
*/
|
|
84
|
+
public Complete(agentSessionID: string, goalRunID: string, outcome: RemoteBrowserGoalOutcome): void {
|
|
85
|
+
const rec = this.runs.get(this.key(agentSessionID));
|
|
86
|
+
if (!rec || rec.GoalRunID !== goalRunID) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
rec.Status = 'Complete';
|
|
90
|
+
rec.Outcome = outcome;
|
|
91
|
+
rec.CompletedAt = Date.now();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Returns the tracked record for a session, or `undefined` when none exists or `goalRunID` is
|
|
96
|
+
* supplied and doesn't match the current run (so a poll for an expired/superseded id reads nothing).
|
|
97
|
+
*/
|
|
98
|
+
public Get(agentSessionID: string, goalRunID?: string): GoalRunRecord | undefined {
|
|
99
|
+
this.sweep();
|
|
100
|
+
const rec = this.runs.get(this.key(agentSessionID));
|
|
101
|
+
if (!rec) {
|
|
102
|
+
return undefined;
|
|
103
|
+
}
|
|
104
|
+
if (goalRunID && rec.GoalRunID !== goalRunID) {
|
|
105
|
+
return undefined;
|
|
106
|
+
}
|
|
107
|
+
return rec;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** Drops completed records past their TTL and never-completing records past the safety cap. */
|
|
111
|
+
private sweep(): void {
|
|
112
|
+
const now = Date.now();
|
|
113
|
+
for (const [k, rec] of this.runs) {
|
|
114
|
+
const expired =
|
|
115
|
+
(rec.Status === 'Complete' && rec.CompletedAt != null && now - rec.CompletedAt > COMPLETED_TTL_MS) ||
|
|
116
|
+
now - rec.StartedAt > RUNNING_MAX_MS;
|
|
117
|
+
if (expired) {
|
|
118
|
+
this.runs.delete(k);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { configInfo } from '../config.js';
|
|
2
|
-
import { AuthProviderConfig, LogError
|
|
2
|
+
import { AuthProviderConfig, LogError } from '@memberjunction/core';
|
|
3
3
|
import { AuthProviderFactory } from '@memberjunction/auth-providers';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -16,8 +16,10 @@ export function initializeAuthProviders(): void {
|
|
|
16
16
|
for (const providerConfig of configInfo.authProviders) {
|
|
17
17
|
try {
|
|
18
18
|
const provider = AuthProviderFactory.createProvider(providerConfig as AuthProviderConfig);
|
|
19
|
+
// register() emits the verbose-only "Registered auth provider: … with issuer: …" line
|
|
20
|
+
// (gated by the global verbose flag). Provider NAMES are surfaced compactly in the
|
|
21
|
+
// startup summary `Auth` line at `standard`.
|
|
19
22
|
factory.register(provider);
|
|
20
|
-
LogStatus(`Registered auth provider: ${provider.name} (type: ${providerConfig.type})`);
|
|
21
23
|
} catch (error) {
|
|
22
24
|
LogError(`Failed to initialize auth provider ${providerConfig.name}: ${error}`);
|
|
23
25
|
}
|
package/src/config.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { cosmiconfigSync } from 'cosmiconfig';
|
|
3
|
-
import { LogError, LogStatus } from '@memberjunction/core';
|
|
3
|
+
import { LogError, LogStatus, LogStatusEx } from '@memberjunction/core';
|
|
4
4
|
import { mergeConfigs, parseBooleanEnv } from '@memberjunction/config';
|
|
5
5
|
|
|
6
6
|
const explorer = cosmiconfigSync('mj', { searchStrategy: 'global' });
|
|
@@ -586,6 +586,14 @@ export const DEFAULT_SERVER_CONFIG: Partial<ConfigInfo> = {
|
|
|
586
586
|
].filter(Boolean),
|
|
587
587
|
};
|
|
588
588
|
|
|
589
|
+
/**
|
|
590
|
+
* Absolute path to the resolved config file, captured during {@link loadConfig}.
|
|
591
|
+
* `undefined` when no config file was found (defaults in use). Rendered in the
|
|
592
|
+
* startup summary `Config` line. Declared before `configInfo` so the assignment
|
|
593
|
+
* inside `loadConfig()` (invoked below) is not in its temporal dead zone.
|
|
594
|
+
*/
|
|
595
|
+
export let configFilePath: string | undefined;
|
|
596
|
+
|
|
589
597
|
export const configInfo: ConfigInfo = loadConfig();
|
|
590
598
|
|
|
591
599
|
export const {
|
|
@@ -621,7 +629,10 @@ export function loadConfig() {
|
|
|
621
629
|
|
|
622
630
|
// If user config exists, merge it with defaults
|
|
623
631
|
if (configSearchResult && !configSearchResult.isEmpty) {
|
|
624
|
-
|
|
632
|
+
// Resolved config-file path. Surfaced in the startup summary `Config` line at standard
|
|
633
|
+
// level (see StartupLogger). Demoted to verbose-only here to avoid a duplicate inline line.
|
|
634
|
+
configFilePath = configSearchResult.filepath;
|
|
635
|
+
LogStatusEx({ message: `Config file found at ${configSearchResult.filepath}`, verboseOnly: true });
|
|
625
636
|
|
|
626
637
|
// Merge user config with defaults (user config takes precedence)
|
|
627
638
|
mergedConfig = mergeConfigs(DEFAULT_SERVER_CONFIG, configSearchResult.config);
|
package/src/context.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { IncomingMessage } from 'http';
|
|
2
|
-
import * as url from 'url';
|
|
3
2
|
import { default as jwt } from 'jsonwebtoken';
|
|
4
3
|
import 'reflect-metadata';
|
|
5
4
|
import { Subject, firstValueFrom } from 'rxjs';
|
|
@@ -10,6 +9,7 @@ import { TokenExpiredError, AuthProviderFactory } from '@memberjunction/auth-pro
|
|
|
10
9
|
import { authCache } from './cache.js';
|
|
11
10
|
import { userEmailMap, apiKey, mj_core_schema } from './config.js';
|
|
12
11
|
import { buildBoundaryLogPayload } from './logging/boundaryLogPayload.js';
|
|
12
|
+
import { StartupLogger } from './logging/StartupLogger.js';
|
|
13
13
|
import { DataSourceInfo, UserPayload } from './types.js';
|
|
14
14
|
import { GetReadOnlyDataSource, GetReadWriteDataSource } from './util.js';
|
|
15
15
|
import { v4 as uuidv4 } from 'uuid';
|
|
@@ -30,6 +30,20 @@ import { GetAPIKeyEngine } from '@memberjunction/api-keys';
|
|
|
30
30
|
// long-lived sessions every hour.) Reviewers asked to audit all user access with
|
|
31
31
|
// timestamp/IP/browser — that payload lives in the Details JSON; the table has no
|
|
32
32
|
// dedicated columns. Best-effort throughout: an audit failure NEVER blocks auth.
|
|
33
|
+
/**
|
|
34
|
+
* Memoized "is the server log level `debug`?" check for the per-request log
|
|
35
|
+
* gates below. Resolved once from `telemetry.level` (the single log-level knob)
|
|
36
|
+
* — config is immutable after boot, so caching is safe and keeps the hot request
|
|
37
|
+
* path free of repeated config reads.
|
|
38
|
+
*/
|
|
39
|
+
let isDebugLevelCache: boolean | undefined;
|
|
40
|
+
function isDebugLogLevel(): boolean {
|
|
41
|
+
if (isDebugLevelCache === undefined) {
|
|
42
|
+
isDebugLevelCache = StartupLogger.resolveLevelFromConfig() === 'debug';
|
|
43
|
+
}
|
|
44
|
+
return isDebugLevelCache;
|
|
45
|
+
}
|
|
46
|
+
|
|
33
47
|
const SESSION_AUDIT_TYPE = 'Session Established';
|
|
34
48
|
const LOGIN_FAILED_AUDIT_TYPE = 'Login Failed';
|
|
35
49
|
const SESSION_AUDIT_CACHE_MAX = 50_000;
|
|
@@ -269,9 +283,12 @@ const verifyAsync = async (issuer: string, token: string): Promise<jwt.JwtPayloa
|
|
|
269
283
|
if (jwt && typeof jwt !== 'string' && !err) {
|
|
270
284
|
const payload = jwt.payload ?? jwt;
|
|
271
285
|
|
|
272
|
-
//
|
|
273
|
-
|
|
274
|
-
|
|
286
|
+
// Per-request token confirmation — debug-only (one of the worst
|
|
287
|
+
// "constantly on" offenders on an authenticated server).
|
|
288
|
+
if (isDebugLogLevel()) {
|
|
289
|
+
const userInfo = extractUserInfoFromPayload(payload);
|
|
290
|
+
console.log(`Valid token: ${userInfo.fullName || 'Unknown'} (${userInfo.email || userInfo.preferredUsername || 'Unknown'})`);
|
|
291
|
+
}
|
|
275
292
|
resolve(payload);
|
|
276
293
|
} else {
|
|
277
294
|
console.warn('Invalid token');
|
|
@@ -462,6 +479,22 @@ export const getUserPayload = async (
|
|
|
462
479
|
* Extracts auth headers and builds a RequestContext from an Express request.
|
|
463
480
|
* Shared by both the unified auth middleware and the WebSocket context.
|
|
464
481
|
*/
|
|
482
|
+
/**
|
|
483
|
+
* Extracts the hostname from a request `Origin` header using the WHATWG `URL` API (replacing the
|
|
484
|
+
* deprecated, security-flagged `url.parse()` — Node DEP0169). Returns `undefined` for a missing,
|
|
485
|
+
* empty, or unparseable origin, matching the prior `url.parse().hostname ?? undefined` semantics.
|
|
486
|
+
*/
|
|
487
|
+
function parseRequestHostname(origin: string | undefined): string | undefined {
|
|
488
|
+
if (!origin) {
|
|
489
|
+
return undefined;
|
|
490
|
+
}
|
|
491
|
+
try {
|
|
492
|
+
return new URL(origin).hostname || undefined;
|
|
493
|
+
} catch {
|
|
494
|
+
return undefined;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
465
498
|
function extractAuthInputs(req: IncomingMessage): {
|
|
466
499
|
bearerToken: string;
|
|
467
500
|
sessionId: string;
|
|
@@ -471,7 +504,7 @@ function extractAuthInputs(req: IncomingMessage): {
|
|
|
471
504
|
requestContext: RequestContext;
|
|
472
505
|
} {
|
|
473
506
|
const sessionIdRaw = req.headers['x-session-id'];
|
|
474
|
-
const requestDomain =
|
|
507
|
+
const requestDomain = parseRequestHostname(req.headers.origin);
|
|
475
508
|
const sessionId = sessionIdRaw ? sessionIdRaw.toString() : '';
|
|
476
509
|
const bearerToken = req.headers.authorization ?? '';
|
|
477
510
|
const systemApiKey = String(req.headers['x-mj-api-key']);
|
|
@@ -489,7 +522,7 @@ function extractAuthInputs(req: IncomingMessage): {
|
|
|
489
522
|
return {
|
|
490
523
|
bearerToken,
|
|
491
524
|
sessionId,
|
|
492
|
-
requestDomain
|
|
525
|
+
requestDomain,
|
|
493
526
|
systemApiKey,
|
|
494
527
|
userApiKey,
|
|
495
528
|
requestContext,
|
|
@@ -573,7 +606,9 @@ export const contextFunction =
|
|
|
573
606
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
574
607
|
const reqAny = req as any;
|
|
575
608
|
const operationName: string | undefined = reqAny.body?.operationName;
|
|
576
|
-
|
|
609
|
+
// Per-request GraphQL boundary line — debug-only. Kept (not deleted) so the
|
|
610
|
+
// data is available when an operator opts into debug, but off by default.
|
|
611
|
+
if (operationName !== 'IntrospectionQuery' && isDebugLogLevel()) {
|
|
577
612
|
console.dir(buildBoundaryLogPayload(operationName), { depth: null, breakLength: 200 });
|
|
578
613
|
}
|
|
579
614
|
|