@memberjunction/core 5.11.0 → 5.13.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 (41) hide show
  1. package/dist/generic/QueryCache.d.ts +41 -7
  2. package/dist/generic/QueryCache.d.ts.map +1 -1
  3. package/dist/generic/QueryCache.js +146 -12
  4. package/dist/generic/QueryCache.js.map +1 -1
  5. package/dist/generic/QueryCacheManager.d.ts +105 -0
  6. package/dist/generic/QueryCacheManager.d.ts.map +1 -0
  7. package/dist/generic/QueryCacheManager.js +314 -0
  8. package/dist/generic/QueryCacheManager.js.map +1 -0
  9. package/dist/generic/baseEntity.js +2 -2
  10. package/dist/generic/baseEntity.js.map +1 -1
  11. package/dist/generic/dataHooks.d.ts +56 -0
  12. package/dist/generic/dataHooks.d.ts.map +1 -0
  13. package/dist/generic/dataHooks.js +56 -0
  14. package/dist/generic/dataHooks.js.map +1 -0
  15. package/dist/generic/interfaces.d.ts +10 -0
  16. package/dist/generic/interfaces.d.ts.map +1 -1
  17. package/dist/generic/interfaces.js.map +1 -1
  18. package/dist/generic/providerBase.js +3 -3
  19. package/dist/generic/providerBase.js.map +1 -1
  20. package/dist/generic/queryCompositionEngine.d.ts +7 -0
  21. package/dist/generic/queryCompositionEngine.d.ts.map +1 -1
  22. package/dist/generic/queryCompositionEngine.js +41 -5
  23. package/dist/generic/queryCompositionEngine.js.map +1 -1
  24. package/dist/generic/queryInfoInterfaces.d.ts +6 -0
  25. package/dist/generic/queryInfoInterfaces.d.ts.map +1 -1
  26. package/dist/generic/queryPagingEngine.d.ts +110 -0
  27. package/dist/generic/queryPagingEngine.d.ts.map +1 -0
  28. package/dist/generic/queryPagingEngine.js +335 -0
  29. package/dist/generic/queryPagingEngine.js.map +1 -0
  30. package/dist/generic/runQuery.d.ts +8 -0
  31. package/dist/generic/runQuery.d.ts.map +1 -1
  32. package/dist/generic/runQuery.js.map +1 -1
  33. package/dist/index.d.ts +3 -1
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js +3 -1
  36. package/dist/index.js.map +1 -1
  37. package/package.json +2 -2
  38. package/dist/generic/hookRegistry.d.ts +0 -83
  39. package/dist/generic/hookRegistry.d.ts.map +0 -1
  40. package/dist/generic/hookRegistry.js +0 -87
  41. package/dist/generic/hookRegistry.js.map +0 -1
@@ -0,0 +1,314 @@
1
+ import { LocalCacheManager } from './localCacheManager.js';
2
+ import { Metadata } from './metadata.js';
3
+ import { LogStatus } from './logging.js';
4
+ import { UUIDsEqual } from '@memberjunction/global';
5
+ /**
6
+ * Wrapper around LocalCacheManager that translates query-specific caching concerns
7
+ * (paging, count, ad-hoc keys, dependency invalidation, entity-change awareness)
8
+ * into LocalCacheManager's generic RunQueryCache methods.
9
+ *
10
+ * Replaces the standalone QueryCache class so that query result caching participates
11
+ * in LCM's unified eviction, TTL, statistics, and optional Redis persistence.
12
+ *
13
+ * ## IMPORTANT — Caching is intentionally BYPASSED for now
14
+ *
15
+ * All Get/Set methods are wired up but short-circuit to no-ops. The reason:
16
+ *
17
+ * Query results cannot be reliably invalidated today. Unlike RunView caching (which
18
+ * tracks individual entities and can invalidate on any row-level mutation), queries
19
+ * are arbitrary SQL that may join, filter, or aggregate across many tables. The
20
+ * `QueryEntity` table maps queries to the entities they reference, but those mappings
21
+ * are LLM-discovered and not guaranteed to be complete. A TTL-only strategy risks
22
+ * serving stale data when the underlying rows change before the TTL expires.
23
+ *
24
+ * The full caching implementation is preserved below so that when we have a reliable
25
+ * invalidation strategy (e.g., entity mutation hooks that can confidently evict all
26
+ * affected query caches with complete QueryEntity coverage), we can enable caching
27
+ * by removing the early returns at the top of each method. Until then, every query
28
+ * execution hits the database to guarantee fresh results.
29
+ */
30
+ export class QueryCacheManager {
31
+ constructor(connectionPrefix) {
32
+ /** Tracks cache timestamps per fingerprint for computing CacheTTLRemaining */
33
+ this._cacheTimestamps = new Map();
34
+ /** Reverse index: normalized entity name → set of fingerprints that depend on it */
35
+ this._queryEntityIndex = new Map();
36
+ this._connectionPrefix = connectionPrefix;
37
+ }
38
+ get LCM() {
39
+ return LocalCacheManager.Instance;
40
+ }
41
+ // ── Fingerprint generation ──────────────────────────────────────────────
42
+ Fingerprint(queryId, queryName, params) {
43
+ return this.LCM.GenerateRunQueryFingerprint(queryId, queryName, params, this._connectionPrefix);
44
+ }
45
+ PagedFingerprint(queryId, queryName, params, startRow, maxRows) {
46
+ return `${this.Fingerprint(queryId, queryName, params)}|page:${startRow}:${maxRows}`;
47
+ }
48
+ CountFingerprint(queryId, queryName, params) {
49
+ return `${this.Fingerprint(queryId, queryName, params)}|count`;
50
+ }
51
+ AdhocFingerprint(sql) {
52
+ return `_adhoc_|_|${QueryCacheManager.Fnv1aHash(sql)}|${this._connectionPrefix}`;
53
+ }
54
+ // ── Full-result cache ───────────────────────────────────────────────────
55
+ /**
56
+ * Retrieves cached full (unpaginated) results for a saved query.
57
+ * Returns null on miss or if caching is disabled.
58
+ */
59
+ async Get(query, params) {
60
+ // BYPASSED — see class-level comment for rationale
61
+ return null;
62
+ /* When caching is enabled, uncomment the block below:
63
+ if (!query.CacheConfig?.enabled) return null;
64
+
65
+ const fp = this.Fingerprint(query.ID, query.Name, params);
66
+ const cached = await this.LCM.GetRunQueryResult(fp);
67
+ if (!cached) return null;
68
+
69
+ const ttlRemainingMs = this.computeTTLRemaining(fp, query.CacheConfig.ttlMinutes);
70
+ return { results: cached.results, ttlRemainingMs };
71
+ */
72
+ }
73
+ /**
74
+ * Stores full (unpaginated) query results in the cache.
75
+ */
76
+ async Set(query, params, results) {
77
+ // BYPASSED — see class-level comment for rationale
78
+ return;
79
+ /* When caching is enabled, uncomment the block below:
80
+ const config = query.CacheConfig;
81
+ if (!config?.enabled) return;
82
+
83
+ const fp = this.Fingerprint(query.ID, query.Name, params);
84
+ const ttlMs = config.ttlMinutes * 60 * 1000;
85
+
86
+ await this.LCM.SetRunQueryResult(
87
+ fp, query.Name, results,
88
+ new Date().toISOString(),
89
+ results.length, query.ID, ttlMs,
90
+ );
91
+ this._cacheTimestamps.set(fp, Date.now());
92
+ this.RegisterEntityDependencies(fp, query);
93
+ */
94
+ }
95
+ // ── Paged cache ─────────────────────────────────────────────────────────
96
+ /**
97
+ * Retrieves cached results for a specific page of a paged query.
98
+ */
99
+ async GetPaged(query, params, startRow, maxRows) {
100
+ // BYPASSED — see class-level comment for rationale
101
+ return null;
102
+ /* When caching is enabled, uncomment the block below:
103
+ if (!query.CacheConfig?.enabled) return null;
104
+
105
+ const fp = this.PagedFingerprint(query.ID, query.Name, params, startRow, maxRows);
106
+ const cached = await this.LCM.GetRunQueryResult(fp);
107
+ if (!cached) return null;
108
+
109
+ const ttlRemainingMs = this.computeTTLRemaining(fp, query.CacheConfig.ttlMinutes);
110
+ return { results: cached.results, ttlRemainingMs };
111
+ */
112
+ }
113
+ /**
114
+ * Stores results for a specific page in the cache.
115
+ */
116
+ async SetPaged(query, params, startRow, maxRows, results) {
117
+ // BYPASSED — see class-level comment for rationale
118
+ return;
119
+ /* When caching is enabled, uncomment the block below:
120
+ const config = query.CacheConfig;
121
+ if (!config?.enabled) return;
122
+
123
+ const fp = this.PagedFingerprint(query.ID, query.Name, params, startRow, maxRows);
124
+ const ttlMs = config.ttlMinutes * 60 * 1000;
125
+ const displayName = `${query.Name} [page ${startRow}+${maxRows}]`;
126
+
127
+ await this.LCM.SetRunQueryResult(
128
+ fp, displayName, results,
129
+ new Date().toISOString(), results.length, query.ID, ttlMs,
130
+ );
131
+ this._cacheTimestamps.set(fp, Date.now());
132
+ this.RegisterEntityDependencies(fp, query);
133
+ */
134
+ }
135
+ // ── Count cache ─────────────────────────────────────────────────────────
136
+ /**
137
+ * Retrieves a cached TotalRowCount for a query + params combination.
138
+ */
139
+ async GetTotalRowCount(query, params) {
140
+ // BYPASSED — see class-level comment for rationale
141
+ return null;
142
+ /* When caching is enabled, uncomment the block below:
143
+ if (!query.CacheConfig?.enabled) return null;
144
+
145
+ const fp = this.CountFingerprint(query.ID, query.Name, params);
146
+ const cached = await this.LCM.GetRunQueryResult(fp);
147
+ if (!cached) return null;
148
+
149
+ const row = cached.results[0] as { TotalRowCount: number } | undefined;
150
+ return row?.TotalRowCount ?? null;
151
+ */
152
+ }
153
+ /**
154
+ * Stores a TotalRowCount value in the cache, encoded as a single-row result.
155
+ */
156
+ async SetTotalRowCount(query, params, count) {
157
+ // BYPASSED — see class-level comment for rationale
158
+ return;
159
+ /* When caching is enabled, uncomment the block below:
160
+ const config = query.CacheConfig;
161
+ if (!config?.enabled) return;
162
+
163
+ const fp = this.CountFingerprint(query.ID, query.Name, params);
164
+ const ttlMs = config.ttlMinutes * 60 * 1000;
165
+
166
+ await this.LCM.SetRunQueryResult(
167
+ fp, `${query.Name} [count]`, [{ TotalRowCount: count }],
168
+ new Date().toISOString(), 1, query.ID, ttlMs,
169
+ );
170
+ this._cacheTimestamps.set(fp, Date.now());
171
+ */
172
+ }
173
+ // ── Ad-hoc cache ────────────────────────────────────────────────────────
174
+ /**
175
+ * Retrieves cached results for an ad-hoc SQL query.
176
+ */
177
+ async GetAdhoc(sql, ttlMinutes) {
178
+ // BYPASSED — see class-level comment for rationale
179
+ return null;
180
+ /* When caching is enabled, uncomment the block below:
181
+ if (ttlMinutes <= 0) return null;
182
+
183
+ const fp = this.AdhocFingerprint(sql);
184
+ const cached = await this.LCM.GetRunQueryResult(fp);
185
+ return cached ? { results: cached.results } : null;
186
+ */
187
+ }
188
+ /**
189
+ * Stores ad-hoc SQL query results in the cache.
190
+ */
191
+ async SetAdhoc(sql, ttlMinutes, results) {
192
+ // BYPASSED — see class-level comment for rationale
193
+ return;
194
+ /* When caching is enabled, uncomment the block below:
195
+ if (ttlMinutes <= 0) return;
196
+
197
+ const fp = this.AdhocFingerprint(sql);
198
+ const ttlMs = ttlMinutes * 60 * 1000;
199
+
200
+ await this.LCM.SetRunQueryResult(
201
+ fp, 'Ad-Hoc Query', results,
202
+ new Date().toISOString(), results.length, undefined, ttlMs,
203
+ );
204
+ this._cacheTimestamps.set(fp, Date.now());
205
+ */
206
+ }
207
+ // ── Invalidation ────────────────────────────────────────────────────────
208
+ /**
209
+ * Invalidates all cache entries (full, paged, count) for a single query by name.
210
+ */
211
+ async InvalidateQuery(queryName) {
212
+ // No-op while caching is bypassed, but safe to call unconditionally
213
+ await this.LCM.InvalidateQueryCaches(queryName);
214
+ }
215
+ /**
216
+ * Invalidates cache entries for a query and all queries that transitively depend on it.
217
+ */
218
+ async InvalidateWithDependents(query, visited = new Set()) {
219
+ // No-op while caching is bypassed (nothing in cache to invalidate),
220
+ // but the traversal logic is preserved for when caching is enabled.
221
+ if (visited.has(query.ID))
222
+ return;
223
+ visited.add(query.ID);
224
+ await this.LCM.InvalidateQueryCaches(query.Name);
225
+ const dependents = query.Dependents;
226
+ if (dependents && dependents.length > 0) {
227
+ for (const dep of dependents) {
228
+ const depQuery = this.FindQueryById(dep.QueryID);
229
+ if (depQuery) {
230
+ await this.InvalidateWithDependents(depQuery, visited);
231
+ }
232
+ }
233
+ }
234
+ }
235
+ // ── Entity-change invalidation ──────────────────────────────────────────
236
+ /**
237
+ * Called when an entity changes (save/delete). Invalidates all query cache entries
238
+ * that depend on the changed entity via QueryEntity mappings.
239
+ *
240
+ * While caching is bypassed the reverse index will always be empty, so this is
241
+ * effectively a no-op. Kept wired up so enabling caching requires no caller changes.
242
+ */
243
+ async HandleEntityChange(entityName) {
244
+ const normalized = entityName.trim().toLowerCase();
245
+ const fingerprints = this._queryEntityIndex.get(normalized);
246
+ if (!fingerprints || fingerprints.size === 0)
247
+ return;
248
+ const fpArray = [...fingerprints];
249
+ for (const fp of fpArray) {
250
+ await this.LCM.InvalidateRunQueryResult(fp);
251
+ this._cacheTimestamps.delete(fp);
252
+ }
253
+ fingerprints.clear();
254
+ LogStatus(`QueryCacheManager: invalidated ${fpArray.length} cache entries for entity "${entityName}"`);
255
+ }
256
+ // ── Entity dependency registration (private) ────────────────────────────
257
+ RegisterEntityDependencies(fingerprint, query) {
258
+ const entityNames = this.GetTransitiveEntityDependencies(query);
259
+ for (const name of entityNames) {
260
+ const normalized = name.trim().toLowerCase();
261
+ if (!this._queryEntityIndex.has(normalized)) {
262
+ this._queryEntityIndex.set(normalized, new Set());
263
+ }
264
+ this._queryEntityIndex.get(normalized).add(fingerprint);
265
+ }
266
+ }
267
+ GetTransitiveEntityDependencies(query, visited = new Set()) {
268
+ if (visited.has(query.ID))
269
+ return [];
270
+ visited.add(query.ID);
271
+ const entities = [];
272
+ // Direct entity mappings from QueryEntity table
273
+ const queryEntities = query.Entities;
274
+ if (queryEntities) {
275
+ for (const qe of queryEntities) {
276
+ if (qe.Entity) {
277
+ entities.push(qe.Entity);
278
+ }
279
+ }
280
+ }
281
+ // Walk composition dependencies for transitive entity mappings
282
+ const dependencies = query.Dependencies;
283
+ if (dependencies) {
284
+ for (const dep of dependencies) {
285
+ const depQuery = this.FindQueryById(dep.DependsOnQueryID);
286
+ if (depQuery) {
287
+ entities.push(...this.GetTransitiveEntityDependencies(depQuery, visited));
288
+ }
289
+ }
290
+ }
291
+ return [...new Set(entities)];
292
+ }
293
+ // ── Helpers ─────────────────────────────────────────────────────────────
294
+ FindQueryById(queryId) {
295
+ return Metadata.Provider.Queries.find((q) => UUIDsEqual(q.ID, queryId));
296
+ }
297
+ computeTTLRemaining(fingerprint, ttlMinutes) {
298
+ const cachedAt = this._cacheTimestamps.get(fingerprint) ?? 0;
299
+ const ttlMs = ttlMinutes * 60 * 1000;
300
+ return Math.max(0, (cachedAt + ttlMs) - Date.now());
301
+ }
302
+ /**
303
+ * FNV-1a 32-bit hash — fast, deterministic, good distribution for cache keys.
304
+ */
305
+ static Fnv1aHash(input) {
306
+ let hash = 0x811c9dc5; // FNV offset basis
307
+ for (let i = 0; i < input.length; i++) {
308
+ hash ^= input.charCodeAt(i);
309
+ hash = (hash * 0x01000193) >>> 0; // FNV prime, keep as uint32
310
+ }
311
+ return hash.toString(16);
312
+ }
313
+ }
314
+ //# sourceMappingURL=QueryCacheManager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"QueryCacheManager.js","sourceRoot":"","sources":["../../src/generic/QueryCacheManager.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { MJEventType, MJGlobal, uuidv4, UUIDsEqual, WarningManager } from '@memberjunction/global';
2
- import { HookRegistry } from './hookRegistry.js';
2
+ import { GetDataHooks } from './dataHooks.js';
3
3
  import { EntityFieldInfo, EntityInfo, EntityFieldTSType, EntityPermissionType, RecordChange, ValidationErrorInfo, ValidationResult } from './entityInfo.js';
4
4
  import { EntityDeleteOptions, EntitySaveOptions, ProviderType } from './interfaces.js';
5
5
  import { Metadata } from './metadata.js';
@@ -1775,7 +1775,7 @@ export class BaseEntity {
1775
1775
  * (error message), the save is rejected by throwing an Error.
1776
1776
  */
1777
1777
  async RunPreSaveHooks() {
1778
- const preSaveHooks = HookRegistry.GetHooks('PreSave');
1778
+ const preSaveHooks = GetDataHooks('PreSave');
1779
1779
  for (const hook of preSaveHooks) {
1780
1780
  const hookResult = await hook(this, this.ActiveUser);
1781
1781
  if (hookResult === false) {