@memberjunction/core 6.1.5 → 6.1.6

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 (36) hide show
  1. package/dist/generic/baseEngine.d.ts +21 -0
  2. package/dist/generic/baseEngine.d.ts.map +1 -1
  3. package/dist/generic/baseEngine.js +60 -5
  4. package/dist/generic/baseEngine.js.map +1 -1
  5. package/dist/generic/baseEntity.d.ts +2 -1
  6. package/dist/generic/baseEntity.d.ts.map +1 -1
  7. package/dist/generic/baseEntity.js +3 -4
  8. package/dist/generic/baseEntity.js.map +1 -1
  9. package/dist/generic/databaseProviderBase.d.ts +41 -8
  10. package/dist/generic/databaseProviderBase.d.ts.map +1 -1
  11. package/dist/generic/databaseProviderBase.js +107 -42
  12. package/dist/generic/databaseProviderBase.js.map +1 -1
  13. package/dist/generic/entityRecordNameCache.d.ts +48 -0
  14. package/dist/generic/entityRecordNameCache.d.ts.map +1 -0
  15. package/dist/generic/entityRecordNameCache.js +92 -0
  16. package/dist/generic/entityRecordNameCache.js.map +1 -0
  17. package/dist/generic/interfaces.d.ts +39 -4
  18. package/dist/generic/interfaces.d.ts.map +1 -1
  19. package/dist/generic/interfaces.js.map +1 -1
  20. package/dist/generic/localCacheManager.d.ts +105 -11
  21. package/dist/generic/localCacheManager.d.ts.map +1 -1
  22. package/dist/generic/localCacheManager.js +221 -40
  23. package/dist/generic/localCacheManager.js.map +1 -1
  24. package/dist/generic/providerBase.d.ts +18 -22
  25. package/dist/generic/providerBase.d.ts.map +1 -1
  26. package/dist/generic/providerBase.js +26 -103
  27. package/dist/generic/providerBase.js.map +1 -1
  28. package/dist/generic/queryInfo.d.ts +14 -2
  29. package/dist/generic/queryInfo.d.ts.map +1 -1
  30. package/dist/generic/queryInfo.js +18 -5
  31. package/dist/generic/queryInfo.js.map +1 -1
  32. package/dist/index.d.ts +1 -0
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +1 -0
  35. package/dist/index.js.map +1 -1
  36. package/package.json +3 -3
@@ -6,7 +6,7 @@ import { ComputeRRF } from "./scoring/ReciprocalRankFusion.js";
6
6
  import { LocalCacheManager } from "./localCacheManager.js";
7
7
  import { ApplicationInfo } from "../generic/applicationInfo.js";
8
8
  import { AuditLogTypeInfo, AuthorizationInfo, AuthorizationRoleInfo, RoleInfo, RowLevelSecurityFilterInfo, UserInfo } from "./securityInfo.js";
9
- import { MJGlobal, MJEventType, NormalizeUUID, UUIDsEqual, MJLruCache, EscapeSQLString, ordinalCompare } from "@memberjunction/global";
9
+ import { MJGlobal, MJEventType, NormalizeUUID, UUIDsEqual, EscapeSQLString, ordinalCompare } from "@memberjunction/global";
10
10
  import { FindReferencedIdentifiers } from "@memberjunction/sql-dialect";
11
11
  import { TelemetryManager } from "./telemetryManager.js";
12
12
  import { LogDebug, LogError, LogStatus, LogStatusEx } from "./logging.js";
@@ -186,10 +186,6 @@ export class ProviderBase {
186
186
  this._localMetadata = new AllMetadata();
187
187
  this._entityMapByName = new Map();
188
188
  this._entityMapByID = new Map();
189
- // Bounded LRU (unlike its siblings above, this cache holds one entry per distinct
190
- // *record* touched via Load()/Save()/LoadFromData() — not per entity definition — so
191
- // it can't be reset on metadata refresh; it needs its own eviction policy.
192
- this._entityRecordNameCache = new MJLruCache({ maxSize: 10000, ttlMs: 60 * 60 * 1000 });
193
189
  this._refresh = false;
194
190
  /** Single-flight guard for the full metadata reload in {@link Config} — see the comment there. */
195
191
  this._metadataReloadInFlight = null;
@@ -591,124 +587,49 @@ export class ProviderBase {
591
587
  return false;
592
588
  }
593
589
  /**
594
- * Helper to generate cache key for entity record names
595
- */
596
- getCacheKey(entityName, compositeKey) {
597
- return `${entityName}|${compositeKey.ToString()}`;
598
- }
599
- /**
600
- * Asynchronous lookup of a cached entity record name. Returns the cached name if available, or undefined if not cached.
601
- * Use this for synchronous contexts (like template rendering) where you can't await GetEntityRecordName().
590
+ * Returns a record's name for synchronous display code, or `undefined` when it is not at hand.
591
+ *
592
+ * This provider keeps no record names, so without `loadIfNeeded` the answer is always
593
+ * `undefined`. A provider that serves a single user (`GraphQLDataProvider`) overrides the
594
+ * record-name methods with a cache; one shared by several users must not, since a name can be
595
+ * withheld from some of them.
596
+ *
602
597
  * @param entityName - The name of the entity
603
598
  * @param compositeKey - The primary key value(s) for the record
604
- * @param loadIfNeeded - If set to true, will load from database if not already cached
605
- * @returns The cached display name, or undefined if not in cache
599
+ * @param loadIfNeeded - Look the name up when it is not cached
600
+ * @returns The display name, or undefined if not cached and not looked up
606
601
  */
607
602
  async GetCachedRecordName(entityName, compositeKey, loadIfNeeded) {
608
- let cachedEntry = this._entityRecordNameCache.Get(this.getCacheKey(entityName, compositeKey));
609
- if (!cachedEntry && loadIfNeeded) {
610
- cachedEntry = await this.GetEntityRecordName(entityName, compositeKey);
611
- }
612
- return cachedEntry;
603
+ return loadIfNeeded ? this.GetEntityRecordName(entityName, compositeKey) : undefined;
613
604
  }
614
605
  /**
615
- * Stores a record name in the cache for later synchronous retrieval via GetCachedRecordName().
616
- * Called automatically by BaseEntity after Load(), LoadFromData(), and Save() operations.
617
- * @param entityName - The name of the entity
618
- * @param compositeKey - The primary key value(s) for the record
619
- * @param recordName - The display name to cache
606
+ * Offers a record's name for later synchronous retrieval. `BaseEntity` calls it after every
607
+ * Load(), LoadFromData() and Save(). Ignored here; see {@link GetCachedRecordName}.
620
608
  */
621
609
  SetCachedRecordName(entityName, compositeKey, recordName) {
622
- this._entityRecordNameCache.Set(this.getCacheKey(entityName, compositeKey), recordName);
610
+ // Intentionally empty: this provider keeps no record names.
623
611
  }
624
612
  /**
625
- * Gets the display name for a single entity record with caching.
613
+ * Gets the display name for a single entity record.
626
614
  * Uses the entity's IsNameField or falls back to 'Name' field if available.
627
615
  * @param entityName - The name of the entity
628
616
  * @param compositeKey - The primary key value(s) for the record
629
- * @param contextUser - Optional user context for permissions
630
- * @param forceRefresh - If true, bypasses cache and queries database
631
- * @returns The display name of the record or null if not found
617
+ * @param contextUser - The acting user; field- and row-level security are applied for them
618
+ * @param forceRefresh - Bypass any cache. This provider has none, so it always looks up.
619
+ * @returns The display name of the record, or an empty string if not found or not readable
632
620
  */
633
621
  async GetEntityRecordName(entityName, compositeKey, contextUser, forceRefresh = false) {
634
- const cacheKey = this.getCacheKey(entityName, compositeKey);
635
- // Check cache unless forceRefresh
636
- if (!forceRefresh) {
637
- const cached = this._entityRecordNameCache.Get(cacheKey);
638
- if (cached !== undefined) {
639
- return cached;
640
- }
641
- }
642
- // Fetch from database via provider-specific implementation
643
- const name = await this.InternalGetEntityRecordName(entityName, compositeKey, contextUser);
644
- if (name) {
645
- this._entityRecordNameCache.Set(cacheKey, name);
646
- }
647
- return name;
622
+ return this.InternalGetEntityRecordName(entityName, compositeKey, contextUser);
648
623
  }
649
624
  /**
650
- * Gets display names for multiple entity records in a single operation with caching.
651
- * More efficient than multiple GetEntityRecordName calls.
625
+ * Gets display names for multiple entity records in a single operation.
652
626
  * @param info - Array of entity/key pairs to lookup
653
- * @param contextUser - Optional user context for permissions
654
- * @param forceRefresh - If true, bypasses cache and queries database for all records
627
+ * @param contextUser - The acting user; field- and row-level security are applied for them
628
+ * @param forceRefresh - Bypass any cache. This provider has none, so it always looks up.
655
629
  * @returns Array of results with names and status for each requested record
656
630
  */
657
631
  async GetEntityRecordNames(info, contextUser, forceRefresh = false) {
658
- if (!forceRefresh) {
659
- // Check cache for each item, collect uncached items
660
- const results = [];
661
- const uncachedInfo = [];
662
- const uncachedIndexes = [];
663
- for (let i = 0; i < info.length; i++) {
664
- const item = info[i];
665
- const cacheKey = this.getCacheKey(item.EntityName, item.CompositeKey);
666
- const cached = this._entityRecordNameCache.Get(cacheKey);
667
- if (cached !== undefined) {
668
- // Cache hit
669
- results[i] = {
670
- EntityName: item.EntityName,
671
- CompositeKey: item.CompositeKey,
672
- Status: 'cached',
673
- Success: true,
674
- RecordName: cached
675
- };
676
- }
677
- else {
678
- // Cache miss - need to fetch
679
- uncachedInfo.push(item);
680
- uncachedIndexes.push(i);
681
- }
682
- }
683
- // Fetch uncached items from database
684
- if (uncachedInfo.length > 0) {
685
- const uncachedResults = await this.InternalGetEntityRecordNames(uncachedInfo, contextUser);
686
- // Merge results and update cache
687
- for (let i = 0; i < uncachedResults.length; i++) {
688
- const result = uncachedResults[i];
689
- const originalIndex = uncachedIndexes[i];
690
- results[originalIndex] = result;
691
- // Cache successful results
692
- if (result.Success && result.RecordName) {
693
- const cacheKey = this.getCacheKey(result.EntityName, result.CompositeKey);
694
- this._entityRecordNameCache.Set(cacheKey, result.RecordName);
695
- }
696
- }
697
- }
698
- return results;
699
- }
700
- else {
701
- // Force refresh - bypass cache entirely
702
- const results = await this.InternalGetEntityRecordNames(info, contextUser);
703
- // Update cache with fresh results
704
- for (const result of results) {
705
- if (result.Success && result.RecordName) {
706
- const cacheKey = this.getCacheKey(result.EntityName, result.CompositeKey);
707
- this._entityRecordNameCache.Set(cacheKey, result.RecordName);
708
- }
709
- }
710
- return results;
711
- }
632
+ return this.InternalGetEntityRecordNames(info, contextUser);
712
633
  }
713
634
  // ========================================================================
714
635
  // PUBLIC API METHODS - Orchestrate Pre → Cache → Internal → Post flow
@@ -1779,7 +1700,9 @@ export class ProviderBase {
1779
1700
  resolvable: true,
1780
1701
  // No user (client-side / trusted single-user context) ⇒ nothing to authorize against;
1781
1702
  // the gate only consults `authorized` when a contextUser is present.
1782
- authorized: !user || qInfo.UserCanRun(user),
1703
+ // This provider's permissions, not the global provider's: a client connection that is not
1704
+ // the global one may have no global provider at all, or one holding another server's.
1705
+ authorized: !user || qInfo.UserCanRun(user, this),
1783
1706
  categoryPath: qInfo.CategoryPath,
1784
1707
  queryName: qInfo.Name
1785
1708
  };