@memberjunction/server 5.49.0 → 5.50.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/README.md +1 -1
- package/dist/auth/newUsers.d.ts.map +1 -1
- package/dist/auth/newUsers.js +6 -4
- package/dist/auth/newUsers.js.map +1 -1
- package/dist/generated/generated.d.ts +259 -14
- package/dist/generated/generated.d.ts.map +1 -1
- package/dist/generated/generated.js +1380 -73
- package/dist/generated/generated.js.map +1 -1
- package/dist/realtimeWidget/WidgetSessionService.d.ts +12 -1
- package/dist/realtimeWidget/WidgetSessionService.d.ts.map +1 -1
- package/dist/realtimeWidget/WidgetSessionService.js +23 -8
- package/dist/realtimeWidget/WidgetSessionService.js.map +1 -1
- package/dist/resolvers/IntegrationDiscoveryResolver.d.ts.map +1 -1
- package/dist/resolvers/IntegrationDiscoveryResolver.js +9 -4
- package/dist/resolvers/IntegrationDiscoveryResolver.js.map +1 -1
- package/package.json +91 -89
- package/src/__tests__/WidgetSessionService.test.ts +96 -0
- package/src/auth/newUsers.ts +6 -5
- package/src/generated/generated.ts +967 -55
- package/src/realtimeWidget/WidgetSessionService.ts +24 -8
- package/src/resolvers/IntegrationDiscoveryResolver.ts +9 -4
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
17
|
import { Metadata, RunView, UserInfo, LogError, LogStatus } from '@memberjunction/core';
|
|
18
|
-
import { UUIDsEqual } from '@memberjunction/global';
|
|
18
|
+
import { MJLruCache, UUIDsEqual } from '@memberjunction/global';
|
|
19
19
|
import type { MJConversationWidgetInstanceEntity, MJConversationEntity } from '@memberjunction/core-entities';
|
|
20
20
|
import { UserCache } from '@memberjunction/sqlserver-dataprovider';
|
|
21
21
|
import { MagicLinkKeyManager } from '../auth/magicLink/MagicLinkKeys.js';
|
|
@@ -283,9 +283,26 @@ export class WidgetSessionService {
|
|
|
283
283
|
return this.MintGuestSession(input);
|
|
284
284
|
}
|
|
285
285
|
|
|
286
|
-
/**
|
|
287
|
-
|
|
286
|
+
/**
|
|
287
|
+
* Short-TTL, size-bounded cache of resolved per-instance rate limits, so the limiter doesn't hit
|
|
288
|
+
* the DB per request. The cache key (`widgetKey`) is presented by an UNAUTHENTICATED caller on the
|
|
289
|
+
* public `/widget/session` endpoint, so an unbounded `Map` here lets anyone grow server memory
|
|
290
|
+
* forever by sending an endless stream of distinct garbage keys (Memory Leak Audit Round 8,
|
|
291
|
+
* Critical finding). `MJLruCache` evicts the least-recently-used entry once `RATE_LIMIT_CACHE_MAX_SIZE`
|
|
292
|
+
* is reached, in addition to the existing TTL — `RATE_LIMIT_CACHE_MAX_SIZE` is sized well above any
|
|
293
|
+
* realistic deployment's widget-instance count so legitimate lookups are never evicted prematurely.
|
|
294
|
+
*/
|
|
295
|
+
private readonly rateLimitCache = new MJLruCache<string, number>({
|
|
296
|
+
maxSize: WidgetSessionService.RATE_LIMIT_CACHE_MAX_SIZE,
|
|
297
|
+
ttlMs: WidgetSessionService.RATE_LIMIT_CACHE_TTL_MS,
|
|
298
|
+
});
|
|
288
299
|
private static readonly RATE_LIMIT_CACHE_TTL_MS = 60_000;
|
|
300
|
+
private static readonly RATE_LIMIT_CACHE_MAX_SIZE = 10_000;
|
|
301
|
+
|
|
302
|
+
/** Current entry count in the per-instance rate-limit cache (diagnostic / test hook). */
|
|
303
|
+
public get RateLimitCacheSize(): number {
|
|
304
|
+
return this.rateLimitCache.Size;
|
|
305
|
+
}
|
|
289
306
|
|
|
290
307
|
/**
|
|
291
308
|
* Resolves the per-instance `RateLimitPerMinute` for a widget key (W6 hardening), falling back to the
|
|
@@ -299,10 +316,9 @@ export class WidgetSessionService {
|
|
|
299
316
|
if (!key) {
|
|
300
317
|
return fallback;
|
|
301
318
|
}
|
|
302
|
-
const cached = this.rateLimitCache.
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
return cached.limit;
|
|
319
|
+
const cached = this.rateLimitCache.Get(key);
|
|
320
|
+
if (cached !== undefined) {
|
|
321
|
+
return cached;
|
|
306
322
|
}
|
|
307
323
|
let limit = fallback;
|
|
308
324
|
try {
|
|
@@ -316,7 +332,7 @@ export class WidgetSessionService {
|
|
|
316
332
|
} catch (e) {
|
|
317
333
|
LogError(e);
|
|
318
334
|
}
|
|
319
|
-
this.rateLimitCache.
|
|
335
|
+
this.rateLimitCache.Set(key, limit);
|
|
320
336
|
return limit;
|
|
321
337
|
}
|
|
322
338
|
|
|
@@ -3433,9 +3433,11 @@ export class IntegrationDiscoveryResolver extends ResolverBase {
|
|
|
3433
3433
|
{ Path: pendingFilePath, Content: JSON.stringify(pendingPayload, null, 2) }
|
|
3434
3434
|
];
|
|
3435
3435
|
|
|
3436
|
-
// Step 5: Run pipeline (restart kills process at the end)
|
|
3436
|
+
// Step 5: Run pipeline (restart kills process at the end).
|
|
3437
|
+
// Retrying variant — an install should not die because the migration hit a dropped
|
|
3438
|
+
// connection or a transient lock; it only replays while nothing has been applied.
|
|
3437
3439
|
const rsm = RuntimeSchemaManager.Instance;
|
|
3438
|
-
const batchResult = await rsm.
|
|
3440
|
+
const batchResult = await rsm.RunPipelineBatchWithRetry([rsuInput]);
|
|
3439
3441
|
|
|
3440
3442
|
const migrationSucceeded = batchResult.SuccessCount > 0;
|
|
3441
3443
|
const pipelineSteps = batchResult.Results[0]?.Steps.map((s: RSUPipelineStep) => ({
|
|
@@ -5306,10 +5308,13 @@ export class IntegrationDiscoveryResolver extends ResolverBase {
|
|
|
5306
5308
|
};
|
|
5307
5309
|
}
|
|
5308
5310
|
|
|
5309
|
-
// Phase 2: Run all successful RSU inputs through one pipeline batch
|
|
5311
|
+
// Phase 2: Run all successful RSU inputs through one pipeline batch.
|
|
5312
|
+
// Retrying variant — a multi-connector install is the longest-running thing a user
|
|
5313
|
+
// does here, and it should not be lost to one transient step. Replays only while
|
|
5314
|
+
// nothing has been applied, so a partially-committed batch is never re-executed.
|
|
5310
5315
|
const pipelineInputs = successfulBuilds.map(b => b.rsuInput);
|
|
5311
5316
|
const rsm = RuntimeSchemaManager.Instance;
|
|
5312
|
-
const batchResult = await rsm.
|
|
5317
|
+
const batchResult = await rsm.RunPipelineBatchWithRetry(pipelineInputs);
|
|
5313
5318
|
|
|
5314
5319
|
// Phase 3: Post-pipeline — create entity maps, field maps, schedules for each success
|
|
5315
5320
|
for (let i = 0; i < successfulBuilds.length; i++) {
|