@memberjunction/server 6.1.0-edge.1 → 6.1.0-edge.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +180 -4
- package/dist/auth/AuthProviderCatalogRouter.d.ts +31 -0
- package/dist/auth/AuthProviderCatalogRouter.d.ts.map +1 -0
- package/dist/auth/AuthProviderCatalogRouter.js +69 -0
- package/dist/auth/AuthProviderCatalogRouter.js.map +1 -0
- package/dist/auth/AuthProviderEngine.d.ts +96 -0
- package/dist/auth/AuthProviderEngine.d.ts.map +1 -0
- package/dist/auth/AuthProviderEngine.js +245 -0
- package/dist/auth/AuthProviderEngine.js.map +1 -0
- package/dist/auth/exampleNewUserSubClass.js +1 -1
- package/dist/auth/exampleNewUserSubClass.js.map +1 -1
- package/dist/auth/index.d.ts +11 -0
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +65 -18
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/initializeProviders.d.ts +64 -1
- package/dist/auth/initializeProviders.d.ts.map +1 -1
- package/dist/auth/initializeProviders.js +117 -5
- package/dist/auth/initializeProviders.js.map +1 -1
- package/dist/auth/magicLink/MagicLinkService.js +1 -1
- package/dist/auth/magicLink/MagicLinkService.js.map +1 -1
- package/dist/auth/newUsers.js +1 -1
- package/dist/auth/newUsers.js.map +1 -1
- package/dist/config.d.ts +89 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +52 -35
- package/dist/config.js.map +1 -1
- package/dist/context.js +2 -2
- package/dist/context.js.map +1 -1
- package/dist/generated/generated.d.ts +540 -1595
- package/dist/generated/generated.d.ts.map +1 -1
- package/dist/generated/generated.js +2755 -16857
- package/dist/generated/generated.js.map +1 -1
- package/dist/generic/ResolverBase.d.ts +20 -4
- package/dist/generic/ResolverBase.d.ts.map +1 -1
- package/dist/generic/ResolverBase.js +58 -21
- package/dist/generic/ResolverBase.js.map +1 -1
- package/dist/generic/RunViewResolver.d.ts +4 -0
- package/dist/generic/RunViewResolver.d.ts.map +1 -1
- package/dist/generic/RunViewResolver.js +31 -0
- package/dist/generic/RunViewResolver.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +183 -54
- package/dist/index.js.map +1 -1
- package/dist/integration/RSUProgressBridge.d.ts +83 -0
- package/dist/integration/RSUProgressBridge.d.ts.map +1 -0
- package/dist/integration/RSUProgressBridge.js +230 -0
- package/dist/integration/RSUProgressBridge.js.map +1 -0
- package/dist/integration/SchemaRefreshLaunch.d.ts +52 -0
- package/dist/integration/SchemaRefreshLaunch.d.ts.map +1 -0
- package/dist/integration/SchemaRefreshLaunch.js +50 -0
- package/dist/integration/SchemaRefreshLaunch.js.map +1 -0
- package/dist/logging/secretRedactor.d.ts +9 -0
- package/dist/logging/secretRedactor.d.ts.map +1 -1
- package/dist/logging/secretRedactor.js +39 -0
- package/dist/logging/secretRedactor.js.map +1 -1
- package/dist/middleware/BaseServerMiddleware.d.ts +5 -0
- package/dist/middleware/BaseServerMiddleware.d.ts.map +1 -1
- package/dist/middleware/BaseServerMiddleware.js +5 -0
- package/dist/middleware/BaseServerMiddleware.js.map +1 -1
- package/dist/providerConfigUnits.d.ts +20 -0
- package/dist/providerConfigUnits.d.ts.map +1 -0
- package/dist/providerConfigUnits.js +22 -0
- package/dist/providerConfigUnits.js.map +1 -0
- package/dist/realtimeWidget/WidgetSessionService.js +1 -1
- package/dist/realtimeWidget/WidgetSessionService.js.map +1 -1
- package/dist/realtimeWidget/widgetGuestElevation.js +1 -1
- package/dist/realtimeWidget/widgetGuestElevation.js.map +1 -1
- package/dist/resolvers/ActionResolver.d.ts.map +1 -1
- package/dist/resolvers/ActionResolver.js +12 -0
- package/dist/resolvers/ActionResolver.js.map +1 -1
- package/dist/resolvers/ComponentRegistryResolver.js +1 -1
- package/dist/resolvers/ComponentRegistryResolver.js.map +1 -1
- package/dist/resolvers/EntityPermissionResolver.js +1 -1
- package/dist/resolvers/EntityPermissionResolver.js.map +1 -1
- package/dist/resolvers/FileResolver.d.ts +34 -4
- package/dist/resolvers/FileResolver.d.ts.map +1 -1
- package/dist/resolvers/FileResolver.js +218 -28
- package/dist/resolvers/FileResolver.js.map +1 -1
- package/dist/resolvers/IntegrationDiscoveryResolver.d.ts +85 -3
- package/dist/resolvers/IntegrationDiscoveryResolver.d.ts.map +1 -1
- package/dist/resolvers/IntegrationDiscoveryResolver.js +302 -82
- package/dist/resolvers/IntegrationDiscoveryResolver.js.map +1 -1
- package/dist/resolvers/PotentialDuplicateRecordResolver.js +1 -1
- package/dist/resolvers/PotentialDuplicateRecordResolver.js.map +1 -1
- package/dist/resolvers/QueryResolver.d.ts +5 -4
- package/dist/resolvers/QueryResolver.d.ts.map +1 -1
- package/dist/resolvers/QueryResolver.js +29 -14
- package/dist/resolvers/QueryResolver.js.map +1 -1
- package/dist/resolvers/QuerySystemUserResolver.d.ts +12 -3
- package/dist/resolvers/QuerySystemUserResolver.d.ts.map +1 -1
- package/dist/resolvers/QuerySystemUserResolver.js +33 -14
- package/dist/resolvers/QuerySystemUserResolver.js.map +1 -1
- package/dist/resolvers/RealtimeClientSessionResolver.d.ts +10 -0
- package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -1
- package/dist/resolvers/RealtimeClientSessionResolver.js +5 -0
- package/dist/resolvers/RealtimeClientSessionResolver.js.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.d.ts +3 -1
- package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.js +20 -7
- package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
- package/dist/resolvers/SearchEntitiesResolver.js +1 -1
- package/dist/resolvers/SearchEntitiesResolver.js.map +1 -1
- package/dist/resolvers/SqlLoggingConfigResolver.d.ts.map +1 -1
- package/dist/resolvers/SqlLoggingConfigResolver.js +1 -1
- package/dist/resolvers/SqlLoggingConfigResolver.js.map +1 -1
- package/dist/resolvers/SyncRolesUsersResolver.js +2 -2
- package/dist/resolvers/SyncRolesUsersResolver.js.map +1 -1
- package/dist/resolvers/TaskGraphFrameResolver.d.ts +18 -0
- package/dist/resolvers/TaskGraphFrameResolver.d.ts.map +1 -1
- package/dist/resolvers/TaskGraphFrameResolver.js +75 -0
- package/dist/resolvers/TaskGraphFrameResolver.js.map +1 -1
- package/dist/resolvers/UserFavoriteResolver.js +1 -1
- package/dist/resolvers/UserFavoriteResolver.js.map +1 -1
- package/dist/resolvers/UserResolver.d.ts +19 -17
- package/dist/resolvers/UserResolver.d.ts.map +1 -1
- package/dist/resolvers/UserResolver.js +66 -67
- package/dist/resolvers/UserResolver.js.map +1 -1
- package/dist/resolvers/currentUserRoles.d.ts +19 -0
- package/dist/resolvers/currentUserRoles.d.ts.map +1 -0
- package/dist/resolvers/currentUserRoles.js +36 -0
- package/dist/resolvers/currentUserRoles.js.map +1 -0
- package/dist/rest/MediaAccessKeys.d.ts +17 -0
- package/dist/rest/MediaAccessKeys.d.ts.map +1 -1
- package/dist/rest/MediaAccessKeys.js +42 -1
- package/dist/rest/MediaAccessKeys.js.map +1 -1
- package/dist/rest/MediaStreamHandler.d.ts +3 -2
- package/dist/rest/MediaStreamHandler.d.ts.map +1 -1
- package/dist/rest/MediaStreamHandler.js +83 -2
- package/dist/rest/MediaStreamHandler.js.map +1 -1
- package/dist/rest/OAuthCallbackHandler.d.ts +49 -1
- package/dist/rest/OAuthCallbackHandler.d.ts.map +1 -1
- package/dist/rest/OAuthCallbackHandler.js +130 -44
- package/dist/rest/OAuthCallbackHandler.js.map +1 -1
- package/dist/rest/SignatureWebhookHandler.js +1 -1
- package/dist/rest/SignatureWebhookHandler.js.map +1 -1
- package/dist/rest/UploadTokenManager.d.ts +90 -0
- package/dist/rest/UploadTokenManager.d.ts.map +1 -0
- package/dist/rest/UploadTokenManager.js +156 -0
- package/dist/rest/UploadTokenManager.js.map +1 -0
- package/dist/services/DurableEntityActionTaskSubmitter.d.ts +26 -0
- package/dist/services/DurableEntityActionTaskSubmitter.d.ts.map +1 -0
- package/dist/services/DurableEntityActionTaskSubmitter.js +55 -0
- package/dist/services/DurableEntityActionTaskSubmitter.js.map +1 -0
- package/dist/services/IntegrationSyncWorkerService.d.ts +48 -0
- package/dist/services/IntegrationSyncWorkerService.d.ts.map +1 -0
- package/dist/services/IntegrationSyncWorkerService.js +132 -0
- package/dist/services/IntegrationSyncWorkerService.js.map +1 -0
- package/dist/services/ScheduledJobsService.js +1 -1
- package/dist/services/ScheduledJobsService.js.map +1 -1
- package/dist/services/StartTaskGraphDispatcher.d.ts +0 -10
- package/dist/services/StartTaskGraphDispatcher.d.ts.map +1 -1
- package/dist/services/StartTaskGraphDispatcher.js +37 -2
- package/dist/services/StartTaskGraphDispatcher.js.map +1 -1
- package/dist/services/TaskGraphActionRunner.d.ts +34 -0
- package/dist/services/TaskGraphActionRunner.d.ts.map +1 -0
- package/dist/services/TaskGraphActionRunner.js +95 -0
- package/dist/services/TaskGraphActionRunner.js.map +1 -0
- package/dist/services/TaskGraphAgentRunner.d.ts +20 -0
- package/dist/services/TaskGraphAgentRunner.d.ts.map +1 -1
- package/dist/services/TaskGraphAgentRunner.js +53 -0
- package/dist/services/TaskGraphAgentRunner.js.map +1 -1
- package/dist/services/TaskGraphContinuationDeliverer.d.ts.map +1 -1
- package/dist/services/TaskGraphContinuationDeliverer.js +12 -1
- package/dist/services/TaskGraphContinuationDeliverer.js.map +1 -1
- package/dist/services/TaskGraphPromptRunner.d.ts +31 -0
- package/dist/services/TaskGraphPromptRunner.d.ts.map +1 -0
- package/dist/services/TaskGraphPromptRunner.js +134 -0
- package/dist/services/TaskGraphPromptRunner.js.map +1 -0
- package/dist/telephony/RingCentralTelephonyService.js +1 -1
- package/dist/telephony/RingCentralTelephonyService.js.map +1 -1
- package/dist/telephony/TwilioTelephonyRouter.js +1 -1
- package/dist/telephony/TwilioTelephonyRouter.js.map +1 -1
- package/dist/telephony/VonageTelephonyRouter.js +1 -1
- package/dist/telephony/VonageTelephonyRouter.js.map +1 -1
- package/dist/types.d.ts +6 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +91 -91
- package/src/__tests__/APIKeyScopeAuth.test.ts +475 -0
- package/src/__tests__/AuthProviderEngine.test.ts +278 -0
- package/src/__tests__/EntityCRUDHandler.test.ts +378 -0
- package/src/__tests__/IntegrationSyncWorkerService.test.ts +275 -0
- package/src/__tests__/OAuthCallbackHandler.openRedirect.test.ts +117 -0
- package/src/__tests__/OAuthCallbackHandler.xss.test.ts +4 -1
- package/src/__tests__/RESTEndpointHandler.test.ts +672 -0
- package/src/__tests__/RSUProgressBridge.test.ts +250 -0
- package/src/__tests__/RealtimeBridgeResolver.test.ts +10 -2
- package/src/__tests__/RealtimeClientSessionResolver.test.ts +1 -1
- package/src/__tests__/ResolverBase.frozenRecordMapping.test.ts +133 -0
- package/src/__tests__/ResolverBase.transportMapping.test.ts +289 -0
- package/src/__tests__/SchemaRefreshLaunch.test.ts +114 -0
- package/src/__tests__/SessionManager.test.ts +1 -1
- package/src/__tests__/UserResolver.currentUser.test.ts +87 -0
- package/src/__tests__/ViewOperationsHandler.test.ts +277 -0
- package/src/__tests__/WidgetSessionService.test.ts +1 -1
- package/src/__tests__/config-units.test.ts +74 -0
- package/src/__tests__/databaseAbstraction.test.ts +1 -1
- package/src/__tests__/initializeProviders.refresh.test.ts +146 -0
- package/src/__tests__/magicLinkService.provisionLog.test.ts +1 -1
- package/src/__tests__/mjapi-bootstrap.test.ts +0 -6
- package/src/__tests__/newUsers.test.ts +726 -0
- package/src/__tests__/resolverBase.rls.test.ts +3 -0
- package/src/__tests__/secretRedactor.failOpen.test.ts +135 -0
- package/src/__tests__/uploadTokenManager.test.ts +159 -0
- package/src/__tests__/widgetGuestElevation.test.ts +1 -1
- package/src/auth/AuthProviderCatalogRouter.ts +76 -0
- package/src/auth/AuthProviderEngine.ts +253 -0
- package/src/auth/exampleNewUserSubClass.ts +1 -1
- package/src/auth/index.ts +69 -21
- package/src/auth/initializeProviders.ts +128 -7
- package/src/auth/magicLink/MagicLinkService.ts +1 -1
- package/src/auth/newUsers.ts +1 -1
- package/src/config.ts +55 -36
- package/src/context.ts +2 -2
- package/src/generated/generated.ts +2038 -10529
- package/src/generic/ResolverBase.ts +63 -21
- package/src/generic/RunViewResolver.ts +31 -0
- package/src/index.ts +199 -56
- package/src/integration/RSUProgressBridge.ts +248 -0
- package/src/integration/SchemaRefreshLaunch.ts +77 -0
- package/src/logging/secretRedactor.ts +43 -0
- package/src/middleware/BaseServerMiddleware.ts +5 -0
- package/src/providerConfigUnits.ts +21 -0
- package/src/realtimeWidget/WidgetSessionService.ts +1 -1
- package/src/realtimeWidget/widgetGuestElevation.ts +1 -1
- package/src/resolvers/ActionResolver.ts +13 -0
- package/src/resolvers/ComponentRegistryResolver.ts +1 -1
- package/src/resolvers/EntityPermissionResolver.ts +1 -1
- package/src/resolvers/FileResolver.ts +200 -40
- package/src/resolvers/IntegrationDiscoveryResolver.ts +328 -70
- package/src/resolvers/PotentialDuplicateRecordResolver.ts +1 -1
- package/src/resolvers/QueryResolver.ts +25 -11
- package/src/resolvers/QuerySystemUserResolver.ts +28 -13
- package/src/resolvers/RealtimeClientSessionResolver.ts +13 -0
- package/src/resolvers/RunAIAgentResolver.ts +21 -6
- package/src/resolvers/SearchEntitiesResolver.ts +1 -1
- package/src/resolvers/SqlLoggingConfigResolver.ts +1 -2
- package/src/resolvers/SyncRolesUsersResolver.ts +2 -2
- package/src/resolvers/TaskGraphFrameResolver.ts +67 -0
- package/src/resolvers/UserFavoriteResolver.ts +1 -1
- package/src/resolvers/UserResolver.ts +99 -60
- package/src/resolvers/currentUserRoles.ts +47 -0
- package/src/rest/MediaAccessKeys.ts +47 -1
- package/src/rest/MediaStreamHandler.ts +98 -3
- package/src/rest/OAuthCallbackHandler.ts +150 -42
- package/src/rest/SignatureWebhookHandler.ts +1 -1
- package/src/rest/UploadTokenManager.ts +211 -0
- package/src/services/DurableEntityActionTaskSubmitter.ts +78 -0
- package/src/services/IntegrationSyncWorkerService.ts +141 -0
- package/src/services/ScheduledJobsService.ts +1 -1
- package/src/services/StartTaskGraphDispatcher.ts +38 -1
- package/src/services/TaskGraphActionRunner.ts +99 -0
- package/src/services/TaskGraphAgentRunner.ts +58 -1
- package/src/services/TaskGraphContinuationDeliverer.ts +11 -1
- package/src/services/TaskGraphPromptRunner.ts +144 -0
- package/src/telephony/RingCentralTelephonyService.ts +1 -1
- package/src/telephony/TwilioTelephonyRouter.ts +1 -1
- package/src/telephony/VonageTelephonyRouter.ts +1 -1
- package/src/types.ts +6 -0
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Bridges RSU pipeline lifecycle events onto the integration progress artifact
|
|
3
|
+
* stream, so a Runtime Schema Update is observable the same way a sync or a connector build is.
|
|
4
|
+
*
|
|
5
|
+
* The gap this closes: `IntegrationRunKind` has always included `'RSU'` and
|
|
6
|
+
* `RUN_KIND_TO_TOPIC` has always mapped it to an `'RSU'` subscription channel — but nothing in
|
|
7
|
+
* production ever constructed an emitter with that kind, so the channel carried no traffic and
|
|
8
|
+
* `IntegrationTailRunEvents` had no RSU runs to tail. The only live signal was polling
|
|
9
|
+
* `RuntimeSchemaUpdateStatus`, which reports the CURRENT step and nothing else: no history, no
|
|
10
|
+
* durable record, and nothing at all to read after a mid-run API restart — which the RSU pipeline
|
|
11
|
+
* does to itself, by design, as one of its own steps.
|
|
12
|
+
*
|
|
13
|
+
* Layering: `@memberjunction/schema-engine` must not depend on the progress artifacts package, so
|
|
14
|
+
* it publishes plain {@link RSUObserverEvent}s and this module — one layer up, where both are
|
|
15
|
+
* already dependencies — owns the emitter lifecycle.
|
|
16
|
+
*/
|
|
17
|
+
import { IntegrationProgressEmitter } from '@memberjunction/integration-progress-artifacts';
|
|
18
|
+
import type { EmitterOptions } from '@memberjunction/integration-progress-artifacts';
|
|
19
|
+
import { RuntimeSchemaManager } from '@memberjunction/schema-engine';
|
|
20
|
+
import type { RSUObserverEvent } from '@memberjunction/schema-engine';
|
|
21
|
+
import { LogError } from '@memberjunction/core';
|
|
22
|
+
import { BaseSingleton } from '@memberjunction/global';
|
|
23
|
+
|
|
24
|
+
/** The emitter stage name used for run-level (non-step) events. */
|
|
25
|
+
const RUN_STAGE = 'RSUPipeline';
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* How often an in-flight step reports that it is still alive.
|
|
29
|
+
*
|
|
30
|
+
* RSU's expensive steps — CodeGen, TypeScript compile, npm install — run for minutes with no
|
|
31
|
+
* intermediate observer event, so without this the stream goes silent for the entire step and a
|
|
32
|
+
* watcher cannot tell "compiling" from "hung". 30s matches the interval the agent notification
|
|
33
|
+
* conventions ask for on long phases.
|
|
34
|
+
*/
|
|
35
|
+
const HEARTBEAT_INTERVAL_MS = 30_000;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Owns one emitter per RSU pipeline run and translates observer events into progress events.
|
|
39
|
+
*
|
|
40
|
+
* A {@link BaseSingleton} because the state here is inherently process-wide AND has to be readable
|
|
41
|
+
* from elsewhere. `RuntimeSchemaManager` is itself a singleton and serializes its runs behind the
|
|
42
|
+
* pipeline lock, so at most one RSU run is ever in flight and one current-emitter slot is sufficient
|
|
43
|
+
* — but the reason for the singleton is {@link CurrentRunID}: a resolver that has just triggered an
|
|
44
|
+
* RSU needs to hand its client a run to tail, and it can only do that if it can reach this instance.
|
|
45
|
+
* Constructing the bridge and keeping the only reference inside the observer closure made that
|
|
46
|
+
* accessor unreachable from any caller.
|
|
47
|
+
*
|
|
48
|
+
* NOT a `BaseEngine`: there is no metadata to load and nothing to `Config()`. The distinction is
|
|
49
|
+
* "single shared instance" (this) versus "cached metadata with a load lifecycle" (that).
|
|
50
|
+
*/
|
|
51
|
+
export class RSUProgressBridge extends BaseSingleton<RSUProgressBridge> {
|
|
52
|
+
private emitter: IntegrationProgressEmitter | null = null;
|
|
53
|
+
private currentRunID: string | null = null;
|
|
54
|
+
/** Liveness timer for the step currently in flight — see {@link HEARTBEAT_INTERVAL_MS}. */
|
|
55
|
+
private heartbeat: ReturnType<typeof setInterval> | null = null;
|
|
56
|
+
private emitterOptions: EmitterOptions = {};
|
|
57
|
+
|
|
58
|
+
/** The one bridge for this process. */
|
|
59
|
+
public static get Instance(): RSUProgressBridge {
|
|
60
|
+
return super.getInstance<RSUProgressBridge>();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Options passed through to each run's emitter. Set once, before the first run.
|
|
65
|
+
*
|
|
66
|
+
* The only field that matters in practice is `rootDir` — left unset in production so RSU runs
|
|
67
|
+
* land in the same `logs/integration-runs/` tree as every other run kind and
|
|
68
|
+
* `IntegrationTailRunEvents` finds them without configuration. Tests point it at a temp dir.
|
|
69
|
+
*
|
|
70
|
+
* A settable property rather than a constructor argument because `BaseSingleton.getInstance`
|
|
71
|
+
* requires a zero-argument constructor.
|
|
72
|
+
*/
|
|
73
|
+
public Configure(emitterOptions: EmitterOptions): void {
|
|
74
|
+
this.emitterOptions = emitterOptions;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* The run ID of the in-flight RSU run, or null when idle. Lets a caller that triggered an RSU
|
|
79
|
+
* hand a client something to tail via `IntegrationTailRunEvents` / the `RSU` subscription
|
|
80
|
+
* channel.
|
|
81
|
+
*/
|
|
82
|
+
public get CurrentRunID(): string | null {
|
|
83
|
+
return this.currentRunID;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Abandons any in-flight run state and stops its heartbeat, without emitting a terminal event.
|
|
88
|
+
*
|
|
89
|
+
* Needed because singleton state outlives a single caller: a test (or a server tearing down)
|
|
90
|
+
* must be able to return the bridge to idle without the next run inheriting a stale emitter or
|
|
91
|
+
* leaking an interval that keeps the process alive.
|
|
92
|
+
*/
|
|
93
|
+
public Reset(): void {
|
|
94
|
+
this.stopHeartbeat();
|
|
95
|
+
this.emitter = null;
|
|
96
|
+
this.currentRunID = null;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** The observer to hand to {@link RuntimeSchemaManager.PipelineObserver}. */
|
|
100
|
+
public readonly Observe = (event: RSUObserverEvent): void => {
|
|
101
|
+
switch (event.Kind) {
|
|
102
|
+
case 'run.start':
|
|
103
|
+
return this.onRunStart(event);
|
|
104
|
+
case 'step.start':
|
|
105
|
+
return this.onStepStart(event);
|
|
106
|
+
case 'step.end':
|
|
107
|
+
return this.onStepEnd(event);
|
|
108
|
+
case 'run.end':
|
|
109
|
+
return this.onRunEnd(event);
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
private onRunStart(event: Extract<RSUObserverEvent, { Kind: 'run.start' }>): void {
|
|
114
|
+
// A retry re-enters RunPipelineBatch, so it legitimately starts a NEW run. If a previous
|
|
115
|
+
// emitter is still open (a throw escaped before run.end), close it rather than leak it.
|
|
116
|
+
if (this.emitter) this.closeCurrent('Superseded by a new RSU run');
|
|
117
|
+
|
|
118
|
+
const runID = IntegrationProgressEmitter.newRunID('rsu');
|
|
119
|
+
this.currentRunID = runID;
|
|
120
|
+
this.emitter = new IntegrationProgressEmitter(
|
|
121
|
+
{
|
|
122
|
+
runID,
|
|
123
|
+
runKind: 'RSU',
|
|
124
|
+
triggerType: 'Pipeline',
|
|
125
|
+
startedAt: new Date().toISOString(),
|
|
126
|
+
context: {
|
|
127
|
+
itemCount: event.ItemCount,
|
|
128
|
+
descriptions: event.Descriptions,
|
|
129
|
+
affectedTables: event.AffectedTables,
|
|
130
|
+
stepTotal: event.StepTotal,
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
this.emitterOptions,
|
|
134
|
+
);
|
|
135
|
+
this.emitter.runStart(
|
|
136
|
+
`RSU pipeline started — ${event.ItemCount} migration(s), ${event.StepTotal} expected step(s): ` +
|
|
137
|
+
event.Descriptions.join('; ')
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
private onStepStart(event: Extract<RSUObserverEvent, { Kind: 'step.start' }>): void {
|
|
142
|
+
if (!this.emitter) return;
|
|
143
|
+
this.emitter.stageStart(event.Name, this.positionLabel(event.StepIndex, event.StepTotal));
|
|
144
|
+
this.startHeartbeat(event.Name);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
private onStepEnd(event: Extract<RSUObserverEvent, { Kind: 'step.end' }>): void {
|
|
148
|
+
this.stopHeartbeat();
|
|
149
|
+
if (!this.emitter) return;
|
|
150
|
+
// A failed step is a stage error, not a stage completion — the run may still continue (a
|
|
151
|
+
// per-item migration failure does not abort the batch), so this is deliberately not fatal.
|
|
152
|
+
if (event.Status === 'failed') {
|
|
153
|
+
this.emitter.stageError(event.Name, event.Message, { durationMs: event.DurationMs, code: 'RSU_STEP_FAILED' });
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
// Deliberately no `counts` here. Counts roll up into the run aggregate, and the countable
|
|
157
|
+
// unit of an RSU run is MIGRATIONS, not steps — reporting "processed: 12" for a 12-step
|
|
158
|
+
// single-migration run would read as 12 migrations. The run-level quartet is emitted once,
|
|
159
|
+
// in onRunEnd. Step position lives in the stage name + the step.start message.
|
|
160
|
+
this.emitter.stageComplete(event.Name);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
private onRunEnd(event: Extract<RSUObserverEvent, { Kind: 'run.end' }>): void {
|
|
164
|
+
this.stopHeartbeat();
|
|
165
|
+
const emitter = this.emitter;
|
|
166
|
+
if (!emitter) return;
|
|
167
|
+
this.emitter = null;
|
|
168
|
+
this.currentRunID = null;
|
|
169
|
+
const summary = `${event.SuccessCount}/${event.TotalCount} migration(s) succeeded`;
|
|
170
|
+
// The one authoritative applied quartet for the run, in migrations — this is what feeds
|
|
171
|
+
// the reader's aggregateCounts.
|
|
172
|
+
emitter.stageComplete(RUN_STAGE, {
|
|
173
|
+
processed: event.TotalCount,
|
|
174
|
+
succeeded: event.SuccessCount,
|
|
175
|
+
failed: event.FailureCount,
|
|
176
|
+
totalKnown: event.TotalCount,
|
|
177
|
+
});
|
|
178
|
+
// Terminal writes are async (they flush the JSONL + write result.json). The observer
|
|
179
|
+
// contract is synchronous, so this is intentionally fire-and-forget with a logged catch —
|
|
180
|
+
// a progress-artifact write must never surface as a pipeline failure.
|
|
181
|
+
const terminal = event.Success
|
|
182
|
+
? emitter.complete(`RSU pipeline complete — ${summary}`)
|
|
183
|
+
: emitter.fail(
|
|
184
|
+
`RSU pipeline failed at '${event.ErrorStep ?? 'unknown step'}' — ${summary}: ` +
|
|
185
|
+
`${event.ErrorMessage ?? 'no error message'}`,
|
|
186
|
+
'rsu-pipeline-failed'
|
|
187
|
+
);
|
|
188
|
+
void terminal.catch(err => LogError(`RSUProgressBridge: terminal write failed — ${err}`));
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Starts reporting liveness for the step that just opened. Any previous timer is cleared first,
|
|
193
|
+
* so a missed `step.end` can never leave two timers running.
|
|
194
|
+
*/
|
|
195
|
+
private startHeartbeat(stage: string): void {
|
|
196
|
+
this.stopHeartbeat();
|
|
197
|
+
const startedAt = Date.now();
|
|
198
|
+
this.heartbeat = setInterval(() => {
|
|
199
|
+
const elapsedSec = Math.round((Date.now() - startedAt) / 1000);
|
|
200
|
+
this.emitter?.heartbeat(stage, `${stage} still running — ${elapsedSec}s elapsed`);
|
|
201
|
+
}, HEARTBEAT_INTERVAL_MS);
|
|
202
|
+
// Never hold the process open on a progress timer.
|
|
203
|
+
this.heartbeat.unref?.();
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/** Stops the liveness timer. Safe to call when none is running. */
|
|
207
|
+
private stopHeartbeat(): void {
|
|
208
|
+
if (!this.heartbeat) return;
|
|
209
|
+
clearInterval(this.heartbeat);
|
|
210
|
+
this.heartbeat = null;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/** Closes an orphaned emitter so a leaked run does not stay in-flight forever. */
|
|
214
|
+
private closeCurrent(reason: string): void {
|
|
215
|
+
this.stopHeartbeat();
|
|
216
|
+
const emitter = this.emitter;
|
|
217
|
+
this.emitter = null;
|
|
218
|
+
this.currentRunID = null;
|
|
219
|
+
void emitter?.fail(reason, 'rsu-run-abandoned')
|
|
220
|
+
.catch(err => LogError(`RSUProgressBridge: abandon write failed — ${err}`));
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* "step 4 of 12" — omitted entirely when the determinate counter isn't armed.
|
|
225
|
+
*
|
|
226
|
+
* Explicit null/undefined checks rather than truthiness: a 0 index is falsy, and silently
|
|
227
|
+
* dropping the label for the first step of a 0-based counter would be a bug that only appears
|
|
228
|
+
* if the pipeline's numbering ever changes.
|
|
229
|
+
*/
|
|
230
|
+
private positionLabel(stepIndex?: number, stepTotal?: number): string | undefined {
|
|
231
|
+
if (stepIndex == null || stepTotal == null) return undefined;
|
|
232
|
+
return `step ${stepIndex} of ${stepTotal}`;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Attaches a fresh {@link RSUProgressBridge} to the RuntimeSchemaManager singleton. Call once at
|
|
238
|
+
* server startup, after the progress publish hook is registered so the very first RSU event also
|
|
239
|
+
* reaches live subscribers.
|
|
240
|
+
*
|
|
241
|
+
* @returns the bridge, so callers can read {@link RSUProgressBridge.CurrentRunID}.
|
|
242
|
+
*/
|
|
243
|
+
export function RegisterRSUProgressBridge(emitterOptions: EmitterOptions = {}): RSUProgressBridge {
|
|
244
|
+
const bridge = RSUProgressBridge.Instance;
|
|
245
|
+
bridge.Configure(emitterOptions);
|
|
246
|
+
RuntimeSchemaManager.Instance.PipelineObserver = bridge.Observe;
|
|
247
|
+
return bridge;
|
|
248
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Pure reporting helpers for the connector schema-refresh pipeline launch.
|
|
3
|
+
*
|
|
4
|
+
* The refresh is a live vendor introspect and can run for minutes on a large catalog, so
|
|
5
|
+
* IntegrationCreateConnection / IntegrationUpdateConnection can launch it *detached* and hand the
|
|
6
|
+
* caller a tailable run ID instead of blocking. That split creates one easy-to-get-wrong reporting
|
|
7
|
+
* case: a detached summary's counts are placeholder zeros, and describing them as results ("0
|
|
8
|
+
* created, 0 updated") reads to an operator as "the refresh found nothing" — the opposite of "the
|
|
9
|
+
* refresh has not finished yet". This module owns that decision so it can be pinned by a test
|
|
10
|
+
* without standing up a resolver.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The subset of the pipeline summary that message construction reads. Structural so both the
|
|
15
|
+
* GraphQL `@ObjectType` and a test fixture satisfy it without either depending on the other.
|
|
16
|
+
*/
|
|
17
|
+
export interface SchemaRefreshSummaryLike {
|
|
18
|
+
/** Tailable run ID — the only meaningful field while `InProgress` is true. */
|
|
19
|
+
RunID: string;
|
|
20
|
+
/** True ⇒ launched detached and still running; every count below is a placeholder zero. */
|
|
21
|
+
InProgress: boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Whether the pipeline itself succeeded. A pipeline that fails at ConnectionTest RETURNS rather
|
|
24
|
+
* than throwing, with every count at zero — so counts alone read identically to a clean
|
|
25
|
+
* no-op refresh. Reporting that as "0 created, 0 updated" tells an operator the vendor had
|
|
26
|
+
* nothing new when in fact the credentials were never accepted.
|
|
27
|
+
*/
|
|
28
|
+
Succeeded: boolean;
|
|
29
|
+
/** The pipeline's own failure reason, when `Succeeded` is false. */
|
|
30
|
+
FailureMessage?: string;
|
|
31
|
+
ObjectsCreated: number;
|
|
32
|
+
ObjectsUpdated: number;
|
|
33
|
+
UnresolvedObjects: string[];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** The counts clause, or an explicit failure clause when the pipeline did not succeed. */
|
|
37
|
+
function describeFinishedRefresh(summary: SchemaRefreshSummaryLike): string {
|
|
38
|
+
if (!summary.Succeeded) {
|
|
39
|
+
return `schema refresh FAILED (${summary.FailureMessage ?? 'no reason reported'}) — see run ${summary.RunID}`;
|
|
40
|
+
}
|
|
41
|
+
return `schema refresh: ${summary.ObjectsCreated} created, ` +
|
|
42
|
+
`${summary.ObjectsUpdated} updated, ${summary.UnresolvedObjects.length} PK-unresolved`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Builds the human-facing `Message` for IntegrationCreateConnection.
|
|
47
|
+
*
|
|
48
|
+
* @param testConnection whether the caller asked for (and got) a live connection test
|
|
49
|
+
* @param summary the refresh summary, or undefined when no refresh was requested
|
|
50
|
+
*/
|
|
51
|
+
export function BuildCreateConnectionMessage(
|
|
52
|
+
testConnection: boolean,
|
|
53
|
+
summary: SchemaRefreshSummaryLike | undefined
|
|
54
|
+
): string {
|
|
55
|
+
if (!summary) return 'Connection created and test passed';
|
|
56
|
+
const created = `Connection created${testConnection ? ', test passed' : ''}`;
|
|
57
|
+
// Never report a detached run's placeholder counts as findings — point at the run stream instead.
|
|
58
|
+
if (summary.InProgress) return `${created}, schema refresh running — tail run ${summary.RunID}`;
|
|
59
|
+
return `${created}, ${describeFinishedRefresh(summary)}`;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Builds the human-facing `Message` for IntegrationUpdateConnection's blocking-refresh path.
|
|
64
|
+
* Shares {@link describeFinishedRefresh} with create so a failed refresh can never be reported as
|
|
65
|
+
* a clean zero-count run on one path and honestly on the other.
|
|
66
|
+
*/
|
|
67
|
+
export function BuildUpdateConnectionMessage(summary: SchemaRefreshSummaryLike): string {
|
|
68
|
+
return `Updated, ${describeFinishedRefresh(summary)}`;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Builds the human-facing `Message` for IntegrationUpdateConnection's detached-refresh path.
|
|
73
|
+
* Separate from {@link BuildCreateConnectionMessage} because update has no connection-test clause.
|
|
74
|
+
*/
|
|
75
|
+
export function BuildDetachedRefreshMessage(runID: string): string {
|
|
76
|
+
return `Updated, schema refresh running — tail run ${runID}`;
|
|
77
|
+
}
|
|
@@ -51,6 +51,20 @@ export function redactArg(ctx: RedactionContext): unknown {
|
|
|
51
51
|
const entity = ctx.provider.Entities.find((e) => e.ClassName === match.groups!.name);
|
|
52
52
|
if (entity) {
|
|
53
53
|
encryptedFieldNames = new Set(entity.EncryptedFields.map((f) => f.Name));
|
|
54
|
+
} else {
|
|
55
|
+
// The name matched the CRUD convention but resolves to no entity — so the
|
|
56
|
+
// metadata half of the redactor contributes nothing and, absent any @NoLog
|
|
57
|
+
// field, this arg falls through to shortenForLog with its values intact.
|
|
58
|
+
//
|
|
59
|
+
// This is the silent case: the boot audit tests only the NAME pattern, so an
|
|
60
|
+
// input like `CreateConnectionInput` is classified as metadata-bound and never
|
|
61
|
+
// warned about, while the lookup here quietly fails. Every hand-written
|
|
62
|
+
// `Create*Input` in the resolvers hits this — codegen inputs embed the entity
|
|
63
|
+
// ClassName (`CreateMJCredentialInput`), hand-written ones do not.
|
|
64
|
+
//
|
|
65
|
+
// Report it once per input type so the operator who enabled variables logging
|
|
66
|
+
// learns which args are unprotected, rather than discovering it in a log file.
|
|
67
|
+
WarnUnboundCrudInput(ctx.inputTypeName);
|
|
54
68
|
}
|
|
55
69
|
}
|
|
56
70
|
|
|
@@ -80,3 +94,32 @@ export function redactArg(ctx: RedactionContext): unknown {
|
|
|
80
94
|
}
|
|
81
95
|
|
|
82
96
|
const EMPTY_SET: ReadonlySet<string> = new Set<string>();
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Input type names already reported by {@link WarnUnboundCrudInput}. Warning once
|
|
100
|
+
* per type keeps a hot mutation from flooding the log with the same diagnostic.
|
|
101
|
+
*/
|
|
102
|
+
const warnedUnboundInputTypes = new Set<string>();
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Reports, once per input type, that an arg named like a CRUD input resolves to no
|
|
106
|
+
* entity and is therefore logged unredacted unless its fields carry `@NoLog`.
|
|
107
|
+
*
|
|
108
|
+
* Exported for tests; not part of the redaction path's contract.
|
|
109
|
+
*/
|
|
110
|
+
export function WarnUnboundCrudInput(inputTypeName: string): void {
|
|
111
|
+
if (warnedUnboundInputTypes.has(inputTypeName)) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
warnedUnboundInputTypes.add(inputTypeName);
|
|
115
|
+
console.warn(
|
|
116
|
+
`[MJServer] Variables logging: '${inputTypeName}' matches the Create/Update/Delete input ` +
|
|
117
|
+
`naming convention but maps to no entity, so encrypted-field redaction cannot apply to it. ` +
|
|
118
|
+
`Its values are being logged. Mark any sensitive @Field() on this input with @NoLog.`,
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** Test seam — clears the once-per-type warning state. */
|
|
123
|
+
export function ResetUnboundCrudInputWarnings(): void {
|
|
124
|
+
warnedUnboundInputTypes.clear();
|
|
125
|
+
}
|
|
@@ -126,6 +126,11 @@ export abstract class BaseServerMiddleware {
|
|
|
126
126
|
/**
|
|
127
127
|
* Hook that runs after a RunView operation completes. Can modify the result
|
|
128
128
|
* (e.g., filtering or augmenting data) before it is returned to the caller.
|
|
129
|
+
*
|
|
130
|
+
* ⚠️ `results.Results` and its rows may be FROZEN shared cache state (LocalCacheManager
|
|
131
|
+
* freeze-on-write) — writing into a row throws `TypeError`. To modify, build copies and
|
|
132
|
+
* either reassign `results.Results = results.Results.map(r => ({ ...r, ... }))` or return
|
|
133
|
+
* a new result; never mutate rows in place.
|
|
129
134
|
*/
|
|
130
135
|
PostRunView(params: RunViewParams, results: RunViewResult, contextUser: UserInfo | undefined): RunViewResult | Promise<RunViewResult> {
|
|
131
136
|
return results;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit-conversion seam between MJServer's configuration and SQLServerDataProvider.
|
|
3
|
+
*
|
|
4
|
+
* MJServer configures `databaseSettings.metadataCacheRefreshInterval` in **milliseconds**
|
|
5
|
+
* (default 180000 = 3 minutes, overridable via the METADATA_CACHE_REFRESH_INTERVAL env var).
|
|
6
|
+
* `SQLServerProviderConfigData`'s third constructor parameter is `checkRefreshIntervalSeconds`
|
|
7
|
+
* — declared in **seconds**.
|
|
8
|
+
*
|
|
9
|
+
* Commit 645c5a5e8 fixed a bug where the raw millisecond value was handed straight to the
|
|
10
|
+
* provider, so the "3 minute" default became a 180000-second (~50 hour) metadata cache
|
|
11
|
+
* refresh cadence. This function is the single place that conversion happens; both provider
|
|
12
|
+
* bootstrap call sites in `index.ts` (the main pool and the CodeGen-credentialed pool) go
|
|
13
|
+
* through it, and `config-units.test.ts` pins the contract so the units can never silently
|
|
14
|
+
* regress again.
|
|
15
|
+
*
|
|
16
|
+
* @param metadataCacheRefreshIntervalMs - the configured refresh interval, in milliseconds
|
|
17
|
+
* @returns the same interval expressed in seconds, the unit `checkRefreshIntervalSeconds` declares
|
|
18
|
+
*/
|
|
19
|
+
export function MetadataCacheRefreshIntervalSeconds(metadataCacheRefreshIntervalMs: number): number {
|
|
20
|
+
return metadataCacheRefreshIntervalMs / 1000;
|
|
21
|
+
}
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
import { Metadata, RunView, UserInfo, LogError, LogStatus } from '@memberjunction/core';
|
|
18
18
|
import { MJLruCache, UUIDsEqual } from '@memberjunction/global';
|
|
19
19
|
import type { MJConversationWidgetInstanceEntity, MJConversationEntity } from '@memberjunction/core-entities';
|
|
20
|
-
import { UserCache } from '@memberjunction/
|
|
20
|
+
import { UserCache } from '@memberjunction/generic-database-provider';
|
|
21
21
|
import { MagicLinkKeyManager } from '../auth/magicLink/MagicLinkKeys.js';
|
|
22
22
|
import { generateSessionId } from '../auth/magicLink/magicLinkCore.js';
|
|
23
23
|
import { MagicLinkService } from '../auth/magicLink/MagicLinkService.js';
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
|
|
16
16
|
import { RunView, LogError, type UserInfo, type DatabaseProviderBase } from '@memberjunction/core';
|
|
17
17
|
import type { MJConversationWidgetInstanceEntity } from '@memberjunction/core-entities';
|
|
18
|
-
import { UserCache } from '@memberjunction/
|
|
18
|
+
import { UserCache } from '@memberjunction/generic-database-provider';
|
|
19
19
|
import type { UserPayload } from '../types.js';
|
|
20
20
|
|
|
21
21
|
const WIDGET_ENTITY = 'MJ: Conversation Widget Instances';
|
|
@@ -366,6 +366,19 @@ export class ActionResolver extends ResolverBase {
|
|
|
366
366
|
// Run the entity action
|
|
367
367
|
const result = await EntityActionEngineServer.Instance.RunEntityAction(params);
|
|
368
368
|
|
|
369
|
+
if (!result) {
|
|
370
|
+
// The binding is scoped (ScopeEntityID/ScopeRecordID) and this record falls outside it, so
|
|
371
|
+
// the action never ran. That is a legitimate answer, not a failure — dereferencing the null
|
|
372
|
+
// here turned "this binding does not apply to that record" into a server error for the
|
|
373
|
+
// caller. Reported as a successful no-op with an explicit message, so a client can tell it
|
|
374
|
+
// apart from an action that ran and did nothing.
|
|
375
|
+
return {
|
|
376
|
+
Success: true,
|
|
377
|
+
Message: 'The entity action did not run: its binding is scoped to different records.',
|
|
378
|
+
ResultData: JSON.stringify({ Ran: false, Reason: 'OutOfScope' })
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
|
|
369
382
|
// Return the result
|
|
370
383
|
return {
|
|
371
384
|
Success: result.Success,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Arg, Ctx, Field, InputType, ObjectType, Query, Mutation, Resolver } from 'type-graphql';
|
|
2
2
|
import { UserInfo, LogError, LogStatus } from '@memberjunction/core';
|
|
3
3
|
import { UUIDsEqual, MJLruCache } from '@memberjunction/global';
|
|
4
|
-
import { UserCache } from '@memberjunction/
|
|
4
|
+
import { UserCache } from '@memberjunction/generic-database-provider';
|
|
5
5
|
import { MJComponentRegistryEntity, ComponentMetadataEngine } from '@memberjunction/core-entities';
|
|
6
6
|
import { CredentialEngine } from '@memberjunction/credentials';
|
|
7
7
|
import { ComponentSpec } from '@memberjunction/interactive-component-types';
|
|
@@ -3,7 +3,7 @@ import { LogError } from '@memberjunction/core';
|
|
|
3
3
|
import { AppContext } from '../types.js';
|
|
4
4
|
import { ResolverBase } from '../generic/ResolverBase.js';
|
|
5
5
|
import { GetReadOnlyProvider } from '../util.js';
|
|
6
|
-
import { UserCache } from '@memberjunction/
|
|
6
|
+
import { UserCache } from '@memberjunction/generic-database-provider';
|
|
7
7
|
|
|
8
8
|
@ObjectType()
|
|
9
9
|
class EntityPermissionResult {
|