@memberjunction/core 6.2.0-edge.4 → 6.2.0-edge.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 (150) hide show
  1. package/dist/generic/BaseEntitySaveQueue.d.ts +22 -0
  2. package/dist/generic/BaseEntitySaveQueue.d.ts.map +1 -1
  3. package/dist/generic/BaseEntitySaveQueue.js +43 -4
  4. package/dist/generic/BaseEntitySaveQueue.js.map +1 -1
  5. package/dist/generic/JSONType-interfaces/IEntityConfiguration.d.ts +18 -0
  6. package/dist/generic/JSONType-interfaces/IEntityConfiguration.d.ts.map +1 -1
  7. package/dist/generic/alternateBaseViews.d.ts +119 -0
  8. package/dist/generic/alternateBaseViews.d.ts.map +1 -0
  9. package/dist/generic/alternateBaseViews.js +223 -0
  10. package/dist/generic/alternateBaseViews.js.map +1 -0
  11. package/dist/generic/baseEngine.d.ts +82 -14
  12. package/dist/generic/baseEngine.d.ts.map +1 -1
  13. package/dist/generic/baseEngine.js +311 -46
  14. package/dist/generic/baseEngine.js.map +1 -1
  15. package/dist/generic/baseEngineRegistry.d.ts.map +1 -1
  16. package/dist/generic/baseEngineRegistry.js +2 -1
  17. package/dist/generic/baseEngineRegistry.js.map +1 -1
  18. package/dist/generic/baseEntity.d.ts +194 -14
  19. package/dist/generic/baseEntity.d.ts.map +1 -1
  20. package/dist/generic/baseEntity.js +638 -218
  21. package/dist/generic/baseEntity.js.map +1 -1
  22. package/dist/generic/baseInfo.d.ts +31 -3
  23. package/dist/generic/baseInfo.d.ts.map +1 -1
  24. package/dist/generic/baseInfo.js +138 -40
  25. package/dist/generic/baseInfo.js.map +1 -1
  26. package/dist/generic/contextTokens.d.ts +136 -0
  27. package/dist/generic/contextTokens.d.ts.map +1 -0
  28. package/dist/generic/contextTokens.js +319 -0
  29. package/dist/generic/contextTokens.js.map +1 -0
  30. package/dist/generic/databaseProviderBase.d.ts +103 -13
  31. package/dist/generic/databaseProviderBase.d.ts.map +1 -1
  32. package/dist/generic/databaseProviderBase.js +321 -77
  33. package/dist/generic/databaseProviderBase.js.map +1 -1
  34. package/dist/generic/embeddedRecord.d.ts +6 -0
  35. package/dist/generic/embeddedRecord.d.ts.map +1 -1
  36. package/dist/generic/embeddedRecord.js +15 -1
  37. package/dist/generic/embeddedRecord.js.map +1 -1
  38. package/dist/generic/entityCompanion.d.ts +7 -0
  39. package/dist/generic/entityCompanion.d.ts.map +1 -1
  40. package/dist/generic/entityCompanion.js +8 -0
  41. package/dist/generic/entityCompanion.js.map +1 -1
  42. package/dist/generic/entityConfiguration.d.ts +8 -2
  43. package/dist/generic/entityConfiguration.d.ts.map +1 -1
  44. package/dist/generic/entityConfiguration.js +9 -0
  45. package/dist/generic/entityConfiguration.js.map +1 -1
  46. package/dist/generic/entityInfo.d.ts +306 -12
  47. package/dist/generic/entityInfo.d.ts.map +1 -1
  48. package/dist/generic/entityInfo.js +894 -92
  49. package/dist/generic/entityInfo.js.map +1 -1
  50. package/dist/generic/entityValueFieldSecurity.d.ts +139 -0
  51. package/dist/generic/entityValueFieldSecurity.d.ts.map +1 -0
  52. package/dist/generic/entityValueFieldSecurity.js +250 -0
  53. package/dist/generic/entityValueFieldSecurity.js.map +1 -0
  54. package/dist/generic/fieldSecuritySQLGate.d.ts +350 -0
  55. package/dist/generic/fieldSecuritySQLGate.d.ts.map +1 -0
  56. package/dist/generic/fieldSecuritySQLGate.js +898 -0
  57. package/dist/generic/fieldSecuritySQLGate.js.map +1 -0
  58. package/dist/generic/indexRestrictedFields.d.ts +47 -0
  59. package/dist/generic/indexRestrictedFields.d.ts.map +1 -0
  60. package/dist/generic/indexRestrictedFields.js +129 -0
  61. package/dist/generic/indexRestrictedFields.js.map +1 -0
  62. package/dist/generic/infoProviderResolution.d.ts +16 -0
  63. package/dist/generic/infoProviderResolution.d.ts.map +1 -0
  64. package/dist/generic/infoProviderResolution.js +17 -0
  65. package/dist/generic/infoProviderResolution.js.map +1 -0
  66. package/dist/generic/interfaces.d.ts +39 -2
  67. package/dist/generic/interfaces.d.ts.map +1 -1
  68. package/dist/generic/interfaces.js.map +1 -1
  69. package/dist/generic/localCacheManager.d.ts +318 -51
  70. package/dist/generic/localCacheManager.d.ts.map +1 -1
  71. package/dist/generic/localCacheManager.js +675 -109
  72. package/dist/generic/localCacheManager.js.map +1 -1
  73. package/dist/generic/logging.d.ts.map +1 -1
  74. package/dist/generic/logging.js +7 -1
  75. package/dist/generic/logging.js.map +1 -1
  76. package/dist/generic/metadata.d.ts +3 -2
  77. package/dist/generic/metadata.d.ts.map +1 -1
  78. package/dist/generic/metadata.js +5 -2
  79. package/dist/generic/metadata.js.map +1 -1
  80. package/dist/generic/mjapiUrl.d.ts +21 -0
  81. package/dist/generic/mjapiUrl.d.ts.map +1 -0
  82. package/dist/generic/mjapiUrl.js +25 -0
  83. package/dist/generic/mjapiUrl.js.map +1 -0
  84. package/dist/generic/platformSQL.d.ts +54 -0
  85. package/dist/generic/platformSQL.d.ts.map +1 -1
  86. package/dist/generic/platformSQL.js +55 -0
  87. package/dist/generic/platformSQL.js.map +1 -1
  88. package/dist/generic/providerBase.d.ts +370 -57
  89. package/dist/generic/providerBase.d.ts.map +1 -1
  90. package/dist/generic/providerBase.js +1085 -284
  91. package/dist/generic/providerBase.js.map +1 -1
  92. package/dist/generic/queryInfo.d.ts +23 -9
  93. package/dist/generic/queryInfo.d.ts.map +1 -1
  94. package/dist/generic/queryInfo.js +73 -33
  95. package/dist/generic/queryInfo.js.map +1 -1
  96. package/dist/generic/queryInfoInterfaces.d.ts +8 -0
  97. package/dist/generic/queryInfoInterfaces.d.ts.map +1 -1
  98. package/dist/generic/queryTemplateContext.d.ts +49 -0
  99. package/dist/generic/queryTemplateContext.d.ts.map +1 -0
  100. package/dist/generic/queryTemplateContext.js +113 -0
  101. package/dist/generic/queryTemplateContext.js.map +1 -0
  102. package/dist/generic/recordChangeFieldSecurity.d.ts.map +1 -1
  103. package/dist/generic/recordChangeFieldSecurity.js +13 -2
  104. package/dist/generic/recordChangeFieldSecurity.js.map +1 -1
  105. package/dist/generic/relatedRecordCollection.d.ts +13 -0
  106. package/dist/generic/relatedRecordCollection.d.ts.map +1 -1
  107. package/dist/generic/relatedRecordCollection.js +44 -5
  108. package/dist/generic/relatedRecordCollection.js.map +1 -1
  109. package/dist/generic/runQuery.d.ts +42 -0
  110. package/dist/generic/runQuery.d.ts.map +1 -1
  111. package/dist/generic/runQuery.js +14 -0
  112. package/dist/generic/runQuery.js.map +1 -1
  113. package/dist/generic/runQuerySQLFilterImplementations.d.ts.map +1 -1
  114. package/dist/generic/runQuerySQLFilterImplementations.js +38 -2
  115. package/dist/generic/runQuerySQLFilterImplementations.js.map +1 -1
  116. package/dist/generic/saveEntityGraphOperation.d.ts.map +1 -1
  117. package/dist/generic/saveEntityGraphOperation.js +3 -0
  118. package/dist/generic/saveEntityGraphOperation.js.map +1 -1
  119. package/dist/generic/scoring/ReciprocalRankFusion.d.ts.map +1 -1
  120. package/dist/generic/scoring/ReciprocalRankFusion.js +10 -3
  121. package/dist/generic/scoring/ReciprocalRankFusion.js.map +1 -1
  122. package/dist/generic/securityInfo.d.ts +17 -6
  123. package/dist/generic/securityInfo.d.ts.map +1 -1
  124. package/dist/generic/securityInfo.js +25 -10
  125. package/dist/generic/securityInfo.js.map +1 -1
  126. package/dist/generic/telemetryManager.d.ts +9 -0
  127. package/dist/generic/telemetryManager.d.ts.map +1 -1
  128. package/dist/generic/telemetryManager.js +5 -1
  129. package/dist/generic/telemetryManager.js.map +1 -1
  130. package/dist/generic/util.d.ts.map +1 -1
  131. package/dist/generic/util.js +3 -0
  132. package/dist/generic/util.js.map +1 -1
  133. package/dist/generic/wellKnownUserSource.d.ts +3 -2
  134. package/dist/generic/wellKnownUserSource.d.ts.map +1 -1
  135. package/dist/generic/wellKnownUserSource.js +18 -9
  136. package/dist/generic/wellKnownUserSource.js.map +1 -1
  137. package/dist/index.d.ts +8 -0
  138. package/dist/index.d.ts.map +1 -1
  139. package/dist/index.js +8 -0
  140. package/dist/index.js.map +1 -1
  141. package/dist/views/runView.d.ts +89 -0
  142. package/dist/views/runView.d.ts.map +1 -1
  143. package/dist/views/runView.js +36 -0
  144. package/dist/views/runView.js.map +1 -1
  145. package/dist/views/viewProperties.d.ts +55 -0
  146. package/dist/views/viewProperties.d.ts.map +1 -0
  147. package/dist/views/viewProperties.js +258 -0
  148. package/dist/views/viewProperties.js.map +1 -0
  149. package/package.json +3 -3
  150. package/readme.md +29 -4
@@ -1,9 +1,10 @@
1
1
  import { BaseEntity, BaseEntityEvent } from "./baseEntity.js";
2
2
  import { EntityDependency, EntityDocumentTypeInfo, EntityInfo, RecordDependency, RecordMergeRequest, RecordMergeResult } from "./entityInfo.js";
3
- import { IMetadataProvider, MetadataSweepResult, ProviderConfigDataBase, MetadataInfo, ILocalStorageProvider, IFileSystemProvider, DatasetResultType, DatasetStatusResultType, DatasetItemFilterType, EntityRecordNameInput, EntityRecordNameResult, ProviderType, PotentialDuplicateRequest, PotentialDuplicateResponse, EntityMergeOptions, AllMetadata, IRunViewProvider, RunViewResult, IRunQueryProvider, RunQueryResult, RunViewWithCacheCheckParams, FullTextSearchParams, FullTextSearchResult, SearchEntityParams, EntitySearchResult, IRemoteOperationProvider, RemoteOpInvokeOptions, RemoteOpResult } from "./interfaces.js";
3
+ import { IMetadataProvider, MetadataSweepResult, ProviderConfigDataBase, MetadataInfo, ILocalStorageProvider, IFileSystemProvider, DatasetResultType, DatasetStatusResultType, DatasetItemFilterType, EntityRecordNameInput, EntityRecordNameResult, ProviderType, PotentialDuplicateRequest, PotentialDuplicateResponse, EntityMergeOptions, GetRecordDependenciesOptions, AllMetadata, IRunViewProvider, RunViewResult, IRunQueryProvider, RunQueryResult, RunViewWithCacheCheckParams, FullTextSearchParams, FullTextSearchResult, SearchEntityParams, EntitySearchResult, IRemoteOperationProvider, RemoteOpInvokeOptions, RemoteOpResult } from "./interfaces.js";
4
4
  import { ScoredCandidate } from "./scoring/ReciprocalRankFusion.js";
5
5
  import { RunQueryParams } from "./runQuery.js";
6
- import type { CacheChangedEvent } from "./localCacheManager.js";
6
+ import { CachedRunViewResult } from "./localCacheManager.js";
7
+ import type { CacheChangedEvent, QueryDefinitionSignature } from "./localCacheManager.js";
7
8
  import { ApplicationInfo } from "../generic/applicationInfo.js";
8
9
  import { AuditLogTypeInfo, AuthorizationInfo, AuthorizationRoleInfo, RoleInfo, RowLevelSecurityFilterInfo, UserInfo } from "./securityInfo.js";
9
10
  import { TransactionGroupBase } from "./transactionGroup.js";
@@ -14,6 +15,7 @@ import { CompositeKey } from "./compositeKey.js";
14
15
  import { ExplorerNavigationItem } from "./explorerNavigationItem.js";
15
16
  import { RunViewParams } from "../views/runView.js";
16
17
  import { DatabasePlatform, PlatformSQL } from "./platformSQL.js";
18
+ import { ContextToken } from "./contextTokens.js";
17
19
  /**
18
20
  * Creates a new instance of AllMetadata from a simple object.
19
21
  * Handles deserialization and proper instantiation of all metadata classes.
@@ -50,8 +52,28 @@ export interface QueryCacheAuthorization {
50
52
  categoryPath?: string;
51
53
  /** Resolved query name — for log messages when the request was by ID only. */
52
54
  queryName?: string;
55
+ /**
56
+ * The context tokens the resolved query's template reads, with every query it composes
57
+ * (`FindQueryContextTokens`). Their values join the fingerprint, so a result that depends on
58
+ * the user or the date is never served to another user or on another day.
59
+ */
60
+ contextTokens?: ContextToken[];
61
+ /**
62
+ * Resolved query ID. With `queryName`, the identity the RunQuery fingerprint is built from, so
63
+ * a request by ID, by name, or by both reaches the same slot.
64
+ */
65
+ queryId?: string;
66
+ /**
67
+ * The definition the provider would execute on a miss. Set by providers that execute queries
68
+ * themselves; a slot written under another definition is then a miss. Undefined where the
69
+ * provider cannot compute one (a client, whose slots the server validates instead).
70
+ */
71
+ definition?: QueryDefinitionSignature;
53
72
  }
54
73
  export declare const AllMetadataArrays: ({
74
+ key: string;
75
+ class: typeof EntityInfo;
76
+ } | {
55
77
  key: string;
56
78
  class: typeof ApplicationInfo;
57
79
  } | {
@@ -60,6 +82,12 @@ export declare const AllMetadataArrays: ({
60
82
  } | {
61
83
  key: string;
62
84
  class: typeof AuthorizationRoleInfo;
85
+ } | {
86
+ key: string;
87
+ class: typeof QueryCategoryInfo;
88
+ } | {
89
+ key: string;
90
+ class: typeof QueryInfo;
63
91
  } | {
64
92
  key: string;
65
93
  class: typeof QueryFieldInfo;
@@ -75,6 +103,9 @@ export declare const AllMetadataArrays: ({
75
103
  } | {
76
104
  key: string;
77
105
  class: typeof QueryDependencyInfo;
106
+ } | {
107
+ key: string;
108
+ class: typeof SQLDialectInfo;
78
109
  } | {
79
110
  key: string;
80
111
  class: typeof QuerySQLInfo;
@@ -99,6 +130,7 @@ export type EntityMetadataRow = BaseMetadataRow & {
99
130
  EntityRelationships?: unknown[];
100
131
  EntitySettings?: unknown[];
101
132
  EntityOrganicKeys?: unknown[];
133
+ EntityAlternateBaseViews?: unknown[];
102
134
  };
103
135
  export type EntityFieldMetadataRow = BaseMetadataRow & {
104
136
  ID: string;
@@ -322,6 +354,25 @@ export declare abstract class ProviderBase implements IMetadataProvider, IRunVie
322
354
  * step no-ops safely.
323
355
  */
324
356
  private ensureInflightViewInvalidation;
357
+ /**
358
+ * Drops this provider's in-flight/lingered RunView dedup entries so the next identical
359
+ * RunView goes to the server instead of replaying a result from the last
360
+ * {@link DedupLingerMs}.
361
+ *
362
+ * Saves and deletes made through a `BaseEntity` in this process already invalidate the entries
363
+ * for their entity automatically. Call this after anything that can write on the SERVER
364
+ * without such an event reaching this process — a Remote Operation, an Action, an agent run,
365
+ * any other mutation. {@link RouteOperation} and the GraphQL client's mutation transport call
366
+ * it for you.
367
+ *
368
+ * Dropping an in-flight entry never affects the callers already waiting on it; it only stops
369
+ * later callers from joining (and stops the result from lingering), since that query may have
370
+ * read pre-write data.
371
+ *
372
+ * @param entityNames - entities known to have been written. Only entries touching them are
373
+ * dropped. Omit (or pass an empty list) when the writes are unknown: every entry is dropped.
374
+ */
375
+ InvalidateRunViewDedupCache(entityNames?: readonly string[]): void;
325
376
  /**
326
377
  * Drops every in-flight/lingered RunView entry whose params touch the given
327
378
  * entity (lowercased name). Called on BaseEntity save/delete/remote-invalidate.
@@ -357,6 +408,34 @@ export declare abstract class ProviderBase implements IMetadataProvider, IRunVie
357
408
  * so a push can see the metadata rows it has not committed yet (MJ#4836).
358
409
  */
359
410
  private _metadataReadsJoinTransaction;
411
+ /**
412
+ * The MJ_Metadata dataset items (code + entity name + row count) as of the last load. The remote
413
+ * timestamps ({@link GetLatestMetadataUpdates}) are keyed by entity name while a dataset
414
+ * read is scoped by item code, so this is the bridge that lets a staleness check be turned
415
+ * into a scoped reload (see {@link PartialRefreshItemCodes}) and lets {@link adoptServerMetadata}
416
+ * verify that the received dataset items' row counts match `_latestRemoteMetadataTimestamps`.
417
+ */
418
+ private _metadataDatasetItems;
419
+ /**
420
+ * MJ_Metadata item codes that may be reloaded on their own, without rebuilding the rest of
421
+ * the metadata graph: the saved-query family. Those rows are written continuously by agents
422
+ * (a query is persisted per analysis step), and every write moves the timestamps of these
423
+ * items. Before this list existed, each such write cost a FULL reload — every EntityInfo and
424
+ * EntityFieldInfo rebuilt (hundreds of MB on a large schema) to pick up a few QueryInfo rows;
425
+ * on a busy server that was a fresh half-gigabyte graph per refresh window until the process
426
+ * hit its memory guard.
427
+ *
428
+ * Membership rule: an item qualifies only when no OTHER metadata class embeds or memoizes its
429
+ * rows at construction. The query classes reference each other lazily through the provider
430
+ * (`QueryInfo.Fields` filters `QueryFields` on first use), so they can be swapped as a set;
431
+ * entity metadata cannot (EntityInfo owns its fields, permissions and relationships).
432
+ * The family is always reloaded together — see {@link PartialRefreshItemCodes}.
433
+ * Intersected with {@link AllMetadataArrays} so the list is valid for whatever this build knows.
434
+ */
435
+ static get PartialRefreshMetadataItemCodes(): ReadonlyArray<string>;
436
+ private static readonly _queryFamilyItemCodes;
437
+ /** The synthetic roll-up row {@link GetLatestMetadataUpdates} appends to the per-entity timestamps. */
438
+ private static readonly _allMetadataTypeName;
360
439
  /** Debounce timer for {@link scheduleMetadataMemberRefresh}. */
361
440
  private _metadataMemberRefreshTimer;
362
441
  /**
@@ -747,6 +826,17 @@ export declare abstract class ProviderBase implements IMetadataProvider, IRunVie
747
826
  * internal execution, post-processing).
748
827
  */
749
828
  private executeRunViewsPipeline;
829
+ /**
830
+ * Puts each refused view's result back at its own index of the caller's batch, with the
831
+ * admitted views' results filling the remaining indexes in order.
832
+ */
833
+ private static interleaveRefusedResults;
834
+ /**
835
+ * The cache, database and post-processing stages of a RunViews batch, for the views the
836
+ * field-security predicate gate admitted. `params` and every index map in `preResult`
837
+ * describe the same admitted views.
838
+ */
839
+ private runAdmittedViews;
750
840
  /**
751
841
  * Enqueues a RunViews call for coalescing. Multiple calls arriving within
752
842
  * CoalesceWindowMs are merged into a single mega-batch, executed once, and
@@ -820,6 +910,17 @@ export declare abstract class ProviderBase implements IMetadataProvider, IRunVie
820
910
  * - entities where server caching is disallowed
821
911
  */
822
912
  protected runViewCacheEligible(param: RunViewParams): boolean;
913
+ /**
914
+ * True when the request targets an `AlternateBaseView` (explicitly on `param.AlternateBaseView`
915
+ * or pinned on `param.ViewEntity`) whose `CachePolicy` (or an ancestor's `CachePolicy` in its
916
+ * `ParentID` chain) is `'Bypass'` (`IsBypassCache === true`), when the requested alternate
917
+ * base view does not resolve to a usable view on the entity, or when a saved User View is
918
+ * referenced only by `ViewID`/`ViewName` without a loaded `ViewEntity` (fail-closed: we cannot
919
+ * synchronously inspect `DisplayState.alternateBaseView` until the view is loaded).
920
+ */
921
+ protected isAlternateBaseViewCacheBypassed(param: RunViewParams): boolean;
922
+ /** True when a run carries view properties (even an empty map): the browser sends it to the server as it is. */
923
+ private static carriesViewProperties;
823
924
  /** True when the entity is a CodeGen materialized-query wrapper (materialized_vw*) whose snapshot is
824
925
  * refreshed out-of-band — the same one IsServerCacheAllowedForEntity excludes from the server cache. */
825
926
  protected isMaterializedWrapperEntity(param: RunViewParams): boolean;
@@ -906,8 +1007,9 @@ export declare abstract class ProviderBase implements IMetadataProvider, IRunVie
906
1007
  private clientCacheFingerprint;
907
1008
  private _clientFingerprintMemo;
908
1009
  /**
909
- * True when `param.Fields` names EVERY field of the entity — i.e. an explicit list that is
910
- * semantically "full width". Case-insensitive; order-independent. Returns false on any
1010
+ * True when `param.Fields` names EVERY fetchable field of the entity — i.e. an explicit list
1011
+ * that is semantically "full width" (accounting for binary fields excluded when
1012
+ * `IncludeBinaryFields !== true`). Case-insensitive; order-independent. Returns false on any
911
1013
  * uncertainty (unknown entity, missing fields) so the fingerprint falls back to the explicit
912
1014
  * list — the safe direction, since misclassifying narrow-as-full would cross-serve shapes.
913
1015
  */
@@ -1049,6 +1151,13 @@ export declare abstract class ProviderBase implements IMetadataProvider, IRunVie
1049
1151
  * the miss path also checked entity read permissions).
1050
1152
  */
1051
1153
  protected ResolveQueryCacheAuthorization(params: RunQueryParams, user?: UserInfo): QueryCacheAuthorization;
1154
+ /**
1155
+ * The fingerprint segment for what the server sets on a run: `ServerParameters`, and the
1156
+ * `Context.*` values the query reads, worked out for this user, zone and day. Empty when the
1157
+ * run has neither, so the fingerprint is exactly what it was before either existed.
1158
+ * @returns null when the query reads `Context.Now`: such a result is never reused
1159
+ */
1160
+ private queryCacheServerSegment;
1052
1161
  RunQuery(params: RunQueryParams, contextUser?: UserInfo): Promise<RunQueryResult>;
1053
1162
  /**
1054
1163
  * Executes a query from a `QueryExecutionSpec` — the lower-layer interface-based entry point.
@@ -1080,6 +1189,12 @@ export declare abstract class ProviderBase implements IMetadataProvider, IRunVie
1080
1189
  * @param callerName
1081
1190
  */
1082
1191
  protected EntityStatusCheck(params: RunViewParams, callerName: string, contextUser?: UserInfo): Promise<void>;
1192
+ /**
1193
+ * Emits a `[Deprecated]` warning when a cache-hit RunView served rows for a deprecated
1194
+ * `AlternateBaseView` (or an alternate view whose ancestor in `ParentID` is deprecated).
1195
+ * Cache misses log deprecation once inside `ResolveAlternateBaseView` during execution.
1196
+ */
1197
+ protected logDeprecatedAlternateBaseViewIfAny(params: RunViewParams, callerName: string): void;
1083
1198
  /**
1084
1199
  * Result from PreRunView hook containing cache status and optional cached result
1085
1200
  */
@@ -1125,6 +1240,20 @@ export declare abstract class ProviderBase implements IMetadataProvider, IRunVie
1125
1240
  * fingerprint string for every batch item.
1126
1241
  */
1127
1242
  fingerprintMap?: Map<number, string>;
1243
+ /**
1244
+ * Pre-resolved local cache entries keyed by client cache fingerprint, populated
1245
+ * during prepareSmartCacheCheckParams so executeSmartCacheCheck can reuse the
1246
+ * already-parsed results for 'current' items without a second IndexedDB read.
1247
+ */
1248
+ preResolvedCache?: Map<string, CachedRunViewResult | null>;
1249
+ /**
1250
+ * Views the field-security predicate gate refused, keyed by their index in the caller's
1251
+ * batch, each with its `Success: false` result. Present only when at least one view was
1252
+ * refused. Every other field of this result — the index maps, `cachedResults`,
1253
+ * `uncachedParams`, `smartCacheCheckParams` — then describes the ADMITTED views only,
1254
+ * indexed by their position among them.
1255
+ */
1256
+ refusedResults?: Map<number, RunViewResult>;
1128
1257
  };
1129
1258
  /**
1130
1259
  * Result from PreRunQuery hook containing cache status and optional cached result
@@ -1186,7 +1315,7 @@ export declare abstract class ProviderBase implements IMetadataProvider, IRunVie
1186
1315
  */
1187
1316
  protected ComputeRunViewRLSWhereClause(params: RunViewParams, contextUser?: UserInfo): string;
1188
1317
  /**
1189
- * The CLIENT's field-security cache key: a canonical list of the fields this user MAY read.
1318
+ * The field-security cache key for one entity: a canonical list of the fields the user MAY read.
1190
1319
  *
1191
1320
  * Keyed on the ALLOWED set rather than the denied set for two reasons. Once metadata ships
1192
1321
  * to browsers filtered to what a user may see ([#3485](https://github.com/MemberJunction/MJ/issues/3485)),
@@ -1198,36 +1327,90 @@ export declare abstract class ProviderBase implements IMetadataProvider, IRunVie
1198
1327
  * Returns '' when the entity has field security off or the user is denied nothing, so
1199
1328
  * unrestricted users keep byte-identical fingerprints and shared slots.
1200
1329
  *
1201
- * Only takes effect once the client's metadata refreshes. A client on stale metadata
1202
- * computes a stale key; the backstop is that the server strips denied columns from every
1203
- * fresh fetch regardless.
1330
+ * Reads the denied set through {@link ResolveRequestDeniedReadFields}, so the key and the
1331
+ * SELECT list a database provider builds for the same request describe the same columns.
1332
+ *
1333
+ * @param params - The request; also the memo the denied set is shared through
1334
+ * @param contextUser - The request's user; defaults to {@link CurrentUser}
1204
1335
  */
1205
- protected ComputeClientFLSAllowedKey(params: RunViewParams): string;
1336
+ protected ComputeClientFLSAllowedKey(params: RunViewParams, contextUser?: UserInfo): string;
1206
1337
  /**
1207
- * The field-security segment for a {@link LocalCacheManager} RunView fingerprint — the one
1208
- * place the client/server asymmetry is decided, so the two tiers cannot drift.
1338
+ * The field-security segment for a {@link LocalCacheManager} RunView fingerprint, on both
1339
+ * tiers: the slot a request reads and fills belongs to the user's permission class.
1209
1340
  *
1210
- * **SERVER → no segment.** Its slots are full-width and shared by every user; per-request
1211
- * narrowing happens at read time in {@link ApplyFieldSecurityProjection}, which runs on
1212
- * every hit and every miss. A segment here would fragment one shared slot into one per
1213
- * permission class and protect nothing the projection does not already handle.
1341
+ * - **The entity's allowed columns** ({@link ComputeClientFLSAllowedKey}). A database
1342
+ * provider selects only the columns the reader may see, so a restricted reader's fill is
1343
+ * narrower than an unrestricted one's, and the two must never share a slot. A browser
1344
+ * stores rows exactly as the server returned them and never projects on read, so its
1345
+ * slots need the same identity. It also keeps user searches apart: which rows match a
1346
+ * search depends on which columns the reader may search.
1347
+ * - **For a saved view, the user's denials on every entity.** A view's stored clause can
1348
+ * reach other entities (nested view filters), and those clauses are only screened when the
1349
+ * database runs them, on a miss. Keying the slot on the whole denial profile means a hit
1350
+ * is only ever served from a fill by a user whose clauses screen identically.
1214
1351
  *
1215
- * **CLIENT → the allowed-list key.** Its slots are stored exactly as the server returned
1216
- * them (already narrowed on the wire) and are not projected on read, so slot identity has to
1217
- * carry the field set. Without it, a user whose access is tightened keeps being served their
1218
- * persisted IndexedDB slot: the currency check compares `maxUpdatedAt` and `rowCount` only,
1219
- * neither of which notices a column, so the server answers "current" and the browser keeps
1220
- * showing a column that was just taken away.
1352
+ * Empty for a user denied nothing, so unrestricted users keep byte-identical fingerprints
1353
+ * and share slots. Read-time projection ({@link ApplyFieldSecurityProjection}) still runs on
1354
+ * every hit and miss, as a backstop.
1221
1355
  *
1222
- * Empty/undefined for unrestricted users on both tiers, so their fingerprints stay
1223
- * byte-identical and keep sharing slots.
1356
+ * @param params - The request
1357
+ * @param contextUser - The request's user; defaults to {@link CurrentUser}. On a server
1358
+ * this must be the request's user, never the process user.
1224
1359
  */
1225
- protected ComputeRunViewFLSFingerprintKey(params: RunViewParams): string | undefined;
1360
+ protected ComputeRunViewFLSFingerprintKey(params: RunViewParams, contextUser?: UserInfo): string;
1361
+ /**
1362
+ * Every field `user` may not read, across all entities: one `entityID:field,field` entry per
1363
+ * entity on which the user is denied something, sorted. '' for a user denied nothing.
1364
+ *
1365
+ * @param user - The user whose denials to describe
1366
+ */
1367
+ protected ComputeFieldSecurityProfileKey(user: UserInfo): string;
1368
+ /**
1369
+ * The fingerprint of the server-cache slot a RunView reads and fills for `contextUser`:
1370
+ * the request, this provider's connection, the user's row-level-security clause and the
1371
+ * user's field-security key. Every server path that reads or writes a RunView slot builds
1372
+ * its fingerprint here, so none of them can leave a segment out — a path that omitted the
1373
+ * user's segments would read or fill another permission class's slot.
1374
+ *
1375
+ * @param params - The request
1376
+ * @param contextUser - The request's user; defaults to {@link CurrentUser}
1377
+ */
1378
+ ComputeRunViewCacheFingerprint(params: RunViewParams, contextUser?: UserInfo): string;
1379
+ /**
1380
+ * The field-security key a browser sends with a cached slot's status, and the one a server
1381
+ * compares it with: a hash of {@link ComputeRunViewFLSFingerprintKey}, or '' for a user
1382
+ * denied nothing.
1383
+ *
1384
+ * The currency check otherwise compares only `maxUpdatedAt` and `rowCount`, and neither
1385
+ * changes when a user loses a column. A browser whose metadata has not refreshed yet keeps
1386
+ * its old slot (same fingerprint) and would be told "current" while still showing the
1387
+ * column; with this key the server sees the mismatch and answers "stale" with fresh rows.
1388
+ *
1389
+ * @param params - The request
1390
+ * @param contextUser - The request's user; defaults to {@link CurrentUser}
1391
+ */
1392
+ protected ComputeFieldSecurityCacheStatusKey(params: RunViewParams, contextUser?: UserInfo): string;
1393
+ /**
1394
+ * The fields `user` may not read on `entity` for this one request, resolved once and reused by
1395
+ * everything that asks again for the same request (`params` object), user object and entity.
1396
+ *
1397
+ * The cache key and the SELECT list are built at different moments of a request. Read
1398
+ * independently, a metadata refresh landing between the two would key a slot for one
1399
+ * permission class and fill it with another's columns. Sharing one answer per request rules
1400
+ * that out. Empty when field security does not apply.
1401
+ *
1402
+ * @param params - The request; the memo is keyed on this object
1403
+ * @param entity - The entity being read
1404
+ * @param user - The request's user
1405
+ */
1406
+ protected ResolveRequestDeniedReadFields(params: RunViewParams, entity: EntityInfo, user: UserInfo | undefined): Set<string>;
1407
+ private _requestDeniedReadFields;
1226
1408
  /**
1227
1409
  * The fetch-widening field list for a cache-eligible request: always every entity field.
1228
- * One slot per (entity, filter, order) serves every caller regardless of the field subset
1229
- * they asked for, and field security narrows per request at read time via
1230
- * {@link ApplyFieldSecurityProjection}.
1410
+ * One slot per (entity, filter, order, permission class) serves every caller regardless of
1411
+ * the field subset they asked for. Field security is not applied here: a database provider
1412
+ * leaves the user's denied columns out of the SELECT it builds from this list, and the slot
1413
+ * key carries the user's class ({@link ComputeRunViewFLSFingerprintKey}).
1231
1414
  */
1232
1415
  protected ComputeRunViewFetchFields(entity: EntityInfo, params?: RunViewParams): string[];
1233
1416
  /**
@@ -1261,28 +1444,44 @@ export declare abstract class ProviderBase implements IMetadataProvider, IRunVie
1261
1444
  * @returns Pre-processing result with cache status and optional cached result
1262
1445
  */
1263
1446
  /**
1264
- * Rejects a RunView whose `ExtraFilter`, `OrderBy`, or `Aggregates` expressions reference a
1265
- * field the user cannot read.
1447
+ * Rejects a RunView whose caller-authored SQL reads a field the user cannot read: `ExtraFilter`,
1448
+ * `OrderBy`, `OverrideExcludeFilter`, every `Aggregates[].expression`, and — when the request
1449
+ * carries a loaded `ViewEntity` — that view's stored `WhereClause` and `OrderByClause`.
1266
1450
  *
1267
1451
  * Output projection alone is security theater. A user denied `Salary` can send
1268
1452
  * `ExtraFilter: "Salary > 200000"` or `OrderBy: "Salary DESC"` and reconstruct the values
1269
1453
  * from which rows come back and in what order — the column never appears in a result, so
1270
1454
  * every output-stripping point reports "secure." Aggregates are the same channel in a purer
1271
1455
  * form: `Aggregates: [{expression: 'MIN(Salary)'}]` under a narrow filter returns a denied
1272
- * field's exact values directly. Predicate validation is a first-class enforcement point,
1273
- * not a belt-and-braces afterthought. Together these cover every caller-authored expression
1274
- * surface (`UserSearchString` is handled by excluding denied fields from the searched set,
1275
- * not by rejection — see below).
1456
+ * field's exact values directly. `UserSearchString` is handled by excluding denied fields from
1457
+ * the searched set, not by rejection.
1276
1458
  *
1277
- * Lives at the provider layer rather than in the GraphQL resolver (where the plan first
1278
- * placed it) because every RunView funnels through here — the batch path, server-internal
1279
- * agents and actions running under a restricted `contextUser`, and the resolver alike. One
1280
- * gate, no path left uncovered.
1459
+ * Lives at the provider layer because every RunView funnels through here — the batch path,
1460
+ * server-internal agents and actions running under a restricted `contextUser`, and the
1461
+ * resolver alike. A database provider checks the clauses it actually runs a second time, once
1462
+ * a saved view is resolved, so a request that names only a `ViewID` is covered too.
1281
1463
  *
1282
- * The error deliberately does not say whether the field is missing or merely forbidden —
1283
- * see {@link ProviderBase.FieldSecurityDenialMessage}.
1464
+ * The reading of each clause, and the error it raises, is {@link FieldSecuritySQLGate}'s.
1284
1465
  */
1285
1466
  protected AssertPredicatesRespectFieldSecurity(params: RunViewParams, contextUser?: UserInfo): void;
1467
+ /**
1468
+ * Throws a {@link FieldSecurityError} when any of `clauses` — SQL fragments that run against
1469
+ * `entity`'s view — reads a field `user` cannot read, reads whole rows of an entity the user is
1470
+ * denied fields on, or cannot be read well enough to tell. Non-string and blank entries are
1471
+ * skipped. A no-op, with nothing scanned, for a user denied nothing on any entity.
1472
+ *
1473
+ * The shared entry point for every clause slot a provider runs: RunView parameters, a saved
1474
+ * view's stored clauses, dataset item filters, user-search text spliced as SQL.
1475
+ */
1476
+ protected AssertClausesRespectFieldSecurityForEntity(entity: EntityInfo, clauses: Iterable<unknown>, user: UserInfo): void;
1477
+ /**
1478
+ * {@link AssertClausesRespectFieldSecurityForEntity} for ORDER BY fragments, which also refuses
1479
+ * a sort by column position for a user denied a field on `entity`
1480
+ * ({@link FieldSecuritySQLGate.FindSortViolation}).
1481
+ */
1482
+ protected AssertSortRespectsFieldSecurityForEntity(entity: EntityInfo, orderBys: Iterable<unknown>, user: UserInfo): void;
1483
+ /** Throws the field-security error for the first of `fragments` that `find` refuses. */
1484
+ private assertFragmentsAllowed;
1286
1485
  /**
1287
1486
  * The single wording for "you cannot use this field," modeled on SQL Server's posture of
1288
1487
  * never disclosing whether an object is missing or merely inaccessible.
@@ -1299,15 +1498,13 @@ export declare abstract class ProviderBase implements IMetadataProvider, IRunVie
1299
1498
  /**
1300
1499
  * Strips fields the user cannot read from plain-object result rows.
1301
1500
  *
1302
- * This is the primary read-time control for list results, and it runs on BOTH cache hits
1303
- * and cache misses — the property the rest of the cache design is arranged around. Because
1304
- * it reads live metadata, a permission change takes effect on the next metadata refresh
1305
- * with no result-cache invalidation: the cached full-width superset stays valid and only
1306
- * the projection changes.
1307
- *
1308
- * It composes with two per-request, never-cached narrowings that avoid pulling columns a
1309
- * restricted service account has no business holding: the `simple`-path SELECT-list
1310
- * intersection, and `Load()`'s allowed-column SELECT.
1501
+ * The backstop behind slot segmentation: cache slots are keyed by the reader's permission
1502
+ * class ({@link ComputeRunViewFLSFingerprintKey}) and filled with only that class's columns,
1503
+ * and this still runs on BOTH cache hits and cache misses, checking every row. A slot can hold
1504
+ * more than its class may read (an `entity_object` fill selects every column, and a slot keyed
1505
+ * just before a permission change keeps its old width), and this is what keeps such a slot
1506
+ * from reaching a reader. It reads live metadata, so a tightened permission applies here from
1507
+ * the next metadata refresh.
1311
1508
  *
1312
1509
  * NEVER applied to `entity_object` results. Those become `BaseEntity` instances whose
1313
1510
  * fields round-trip through `GenerateSaveSQL`, which iterates ALL `IsSPParameter` fields
@@ -1319,13 +1516,30 @@ export declare abstract class ProviderBase implements IMetadataProvider, IRunVie
1319
1516
  protected ApplyFieldSecurityProjection<T>(rows: T[], params: RunViewParams, contextUser?: UserInfo): T[];
1320
1517
  /**
1321
1518
  * Row-loop half of {@link ApplyFieldSecurityProjection}, split out so the policy above reads
1322
- * as policy. Mirrors {@link ProjectRowsToFields}' matching semantics (trim + lowercase) and
1323
- * its no-op probe: when the first row carries none of the denied keys, the whole result is
1324
- * returned untouched rather than rebuilt row by row.
1519
+ * as policy. Mirrors {@link ProjectRowsToFields}' matching semantics (trim + lowercase).
1520
+ *
1521
+ * Every row is checked on its own: rows in one result need not share a shape (a slot can mix
1522
+ * rows written by different paths), so no row stands in for the others. A row carrying no
1523
+ * denied key is kept as is, and when none do the input array itself is returned.
1524
+ *
1525
+ * The per-key work (trim, lowercase, set lookup) is done once per key SHAPE rather than once
1526
+ * per row: rows with the same keys in the same order share one {@link RowProjectionPlan}. A
1527
+ * result is almost always a single shape, so a restricted user's 10k-row result pays for one
1528
+ * row's worth of key matching plus a copy per row.
1325
1529
  *
1326
1530
  * @param deniedLowercase field names to omit, already lowercased
1327
1531
  */
1328
1532
  private static omitFieldsFromRows;
1533
+ /** One row of {@link omitFieldsFromRows}: the row itself when it carries no denied key, else a copy without them. */
1534
+ private static omitFieldsFromRow;
1535
+ /**
1536
+ * The plan for a row with exactly these keys, built on first sight of the shape. The last plan
1537
+ * used is tried first; otherwise plans are found by the joined key list. Either way the plan's
1538
+ * own key list is compared with the row's before it is used, so two different shapes can
1539
+ * never share a plan.
1540
+ */
1541
+ private static rowProjectionPlanFor;
1542
+ private static sameKeys;
1329
1543
  /**
1330
1544
  * Strips or narrows the `MJ: Record Changes` payload columns a user may not read, using the
1331
1545
  * denied set of the entity each row is ABOUT rather than of Record Changes itself.
@@ -1336,6 +1550,9 @@ export declare abstract class ProviderBase implements IMetadataProvider, IRunVie
1336
1550
  * Everything about the per-row denied set, the payload treatment, and the fail-closed decision
1337
1551
  * lives in {@link RecordChangeFieldSecurityProjector}; this is only the RunView wiring.
1338
1552
  *
1553
+ * A request whose entity cannot be resolved without I/O (only a `ViewID` or `ViewName`) is
1554
+ * projected too, so a saved view over Record Changes is not a way around it.
1555
+ *
1339
1556
  * Runs at all four RunView projection points, matching the main projection: both cache-hit
1340
1557
  * paths and both cache-miss paths. The cache-hit path is not optional — it is the exact path
1341
1558
  * the original cross-user leak runs through, an unrestricted user warming a full-width slot
@@ -1371,11 +1588,34 @@ export declare abstract class ProviderBase implements IMetadataProvider, IRunVie
1371
1588
  /**
1372
1589
  * Pre-processing hook for RunViews (batch).
1373
1590
  * Handles telemetry, validation, and cache lookup for multiple views.
1591
+ * A view the field-security predicate gate refuses is left out of every later stage and
1592
+ * reported in `refusedResults`; the cache status describes the admitted views only.
1374
1593
  * @param params - Array of view parameters
1375
1594
  * @param contextUser - Optional user context
1376
- * @returns Pre-processing result with cache status for each view
1595
+ * @returns Pre-processing result with cache status for each admitted view
1377
1596
  */
1378
1597
  protected PreRunViews(params: RunViewParams[], contextUser?: UserInfo): Promise<typeof this._preRunViewsResultType>;
1598
+ /**
1599
+ * Runs each view's PreRunView hooks, replacing `params[i]` with the hooked params, then the
1600
+ * field-security predicate gate — the same contract as the single-view path, except that a
1601
+ * refusal fails only its own view: it becomes that view's `Success: false` result, carrying
1602
+ * the refusal's ambiguous message, so the views batched with it still run. A batch is often
1603
+ * several independent reads (a dashboard's panels), and one unreadable column in one of them
1604
+ * must not blank the rest; the refusal is still reported, at the refused view's index. Any
1605
+ * other error from the gate is thrown and fails the batch.
1606
+ * @returns The refused views' results keyed by batch index, or `undefined` when none was refused.
1607
+ */
1608
+ private runBatchHooksAndPredicateGate;
1609
+ /** `error` as a failed view result when it is a field-security refusal; otherwise rethrows it. */
1610
+ protected static fieldSecurityRefusalAsResult<T = unknown>(error: unknown): RunViewResult<T>;
1611
+ /**
1612
+ * The cache stage of PreRunViews for the views the predicate gate admitted. An empty batch
1613
+ * (every view refused) is reported as fully cached, so the pipeline returns without a
1614
+ * database call and still ends the batch's telemetry event.
1615
+ */
1616
+ private lookUpAdmittedViews;
1617
+ /** The server's trusted-cache lookup for a batch: serves hits, and lists the misses to run. */
1618
+ private lookUpBatchCache;
1379
1619
  /**
1380
1620
  * Prepares smart cache check parameters for RunViews when CacheLocal is enabled.
1381
1621
  * Instead of returning cached data immediately, this builds params to send to the server
@@ -1435,6 +1675,16 @@ export declare abstract class ProviderBase implements IMetadataProvider, IRunVie
1435
1675
  * @param contextUser - Optional user context
1436
1676
  */
1437
1677
  protected PostRunViews(results: RunViewResult[], params: RunViewParams[], preResult: typeof this._preRunViewsResultType, contextUser?: UserInfo): Promise<void>;
1678
+ /**
1679
+ * Works out a stored view's property values ({@link RunViewParams.Properties},
1680
+ * {@link RunViewParams.ServerProperties}, and each property's default) before the run's cache
1681
+ * lookup, so the result-cache fingerprint keys on the values the run will actually use. Called
1682
+ * on the run's own params copy, after the PreRunView hooks. Must never throw: a run whose
1683
+ * properties would be refused is left for the provider's run path to refuse with its message.
1684
+ *
1685
+ * Does nothing here. A database provider, which resolves token defaults on the server, overrides it.
1686
+ */
1687
+ protected PrepareRunViewProperties(_params: RunViewParams, _contextUser?: UserInfo): Promise<void>;
1438
1688
  /**
1439
1689
  * Runs all registered PreRunView hooks against a single RunViewParams,
1440
1690
  * returning the (possibly mutated) params.
@@ -1665,15 +1915,54 @@ export declare abstract class ProviderBase implements IMetadataProvider, IRunVie
1665
1915
  * @returns True if configuration was successful
1666
1916
  */
1667
1917
  Config(data: ProviderConfigDataBase, providerToUse?: IMetadataProvider): Promise<boolean>;
1918
+ /**
1919
+ * Reloads this provider's metadata. Single-flight: at most one reload runs at a time, and a
1920
+ * request arriving mid-reload queues exactly one follow-up pass.
1921
+ * @param hardRefresh - re-read the remote timestamps first and bypass every cache layer
1922
+ * @param scopedFirst - try the scoped reload of the saved-query family (see
1923
+ * {@link PartialRefreshMetadataItemCodes}) before the full one. Defaults to true unless
1924
+ * hardRefresh: an explicit Refresh() is always a full reload.
1925
+ */
1926
+ protected ReloadMetadata(hardRefresh: boolean, providerToUse?: IMetadataProvider, scopedFirst?: boolean): Promise<boolean>;
1668
1927
  /**
1669
1928
  * Adopts a metadata snapshot just loaded from the server: swaps it in, records that the
1670
- * local copy now matches the server's timestamps, and persists it to local storage.
1929
+ * local copy now matches the server's timestamps (verified per item against the received
1930
+ * dataset row counts), and persists it to local storage.
1671
1931
  *
1672
1932
  * The swap goes through UpdateLocalMetadata — a single property assignment, atomic in
1673
1933
  * JavaScript, so readers see the old snapshot until the new one is complete — which keeps
1674
1934
  * it consistent with LoadLocalMetadataFromStorage() and lets subclasses override it.
1675
1935
  */
1676
1936
  private adoptServerMetadata;
1937
+ /**
1938
+ * Verifies that each item in `_latestRemoteMetadataTimestamps` matches the row count actually
1939
+ * received in the loaded `MJ_Metadata` dataset (or `AllMetadata` collections) before advancing
1940
+ * `_latestLocalMetadataTimestamps`. If any item has a mismatched row count (e.g., because a
1941
+ * server cache slot returned stale rows while the SQL status probe reported newer counts),
1942
+ * logs a warning and preserves the unadvanced local timestamp for that item so the next
1943
+ * staleness check (`LocalMetadataObsolete`) detects the discrepancy and self-heals.
1944
+ */
1945
+ private reconcileAdoptedMetadataTimestamps;
1946
+ /**
1947
+ * Decides whether the current staleness can be served by a scoped reload of the saved-query
1948
+ * family. Compares local and remote timestamps item by item (the same test as
1949
+ * {@link LocalMetadataObsolete}) and maps each stale entity back to its MJ_Metadata item code.
1950
+ * @returns the family's item codes (the whole family — the query classes are swapped as a set)
1951
+ * when every stale item belongs to it; null when anything else is stale, nothing is, or the
1952
+ * inputs needed to attribute the change are missing — the full reload is then the answer.
1953
+ */
1954
+ protected PartialRefreshItemCodes(): string[] | null;
1955
+ private static metadataStampChanged;
1956
+ /**
1957
+ * Reloads only the saved-query family of MJ_Metadata and swaps it into the current metadata,
1958
+ * keeping every other array — and every object in them — as is. The dataset definition is
1959
+ * reused verbatim (same columns, where clauses and post-processing): every other item is
1960
+ * requested with a `1=0` filter, so it reads nothing. Always bypasses cache layers — the read
1961
+ * is small, and a cached answer is exactly what a staleness check cannot trust.
1962
+ * @returns true when the scoped reload was applied; false when it does not apply or any part
1963
+ * of it failed — the caller then runs the full reload, so a failure never leaves stale queries.
1964
+ */
1965
+ protected TryPartialMetadataReload(providerToUse?: IMetadataProvider): Promise<boolean>;
1677
1966
  /**
1678
1967
  * Background validation for the stale-while-revalidate fast-start pattern.
1679
1968
  * Checks if local metadata is still current; if stale, fetches fresh metadata
@@ -1743,6 +2032,15 @@ export declare abstract class ProviderBase implements IMetadataProvider, IRunVie
1743
2032
  * were on the client's global provider): treat Info objects and everything
1744
2033
  * they own as read-only; copy before sorting.
1745
2034
  *
2035
+ * Owning provider: the shared Info objects deliberately keep the
2036
+ * {@link BaseInfo.OwningProvider} of the provider that built them (the source
2037
+ * global provider); the shell does NOT re-stamp them. Re-stamping a shared
2038
+ * instance would re-point it for every other provider holding it. The sibling
2039
+ * lookups it drives (IS-A children, RLS filters, query fields, ...) therefore
2040
+ * read the source provider's graph — the same immutable graph this shell
2041
+ * points into, so the answers are identical. A provider that needs its own
2042
+ * graph (a different database) must load its own metadata, not adopt a shell.
2043
+ *
1746
2044
  * Override precedence: if a subclass overrides BOTH this method and the
1747
2045
  * deprecated {@link CloneAllMetadata}, the CloneAllMetadata override wins on
1748
2046
  * the fast path (see {@link CopyMetadataFromGlobalProvider}) — the
@@ -1750,11 +2048,22 @@ export declare abstract class ProviderBase implements IMetadataProvider, IRunVie
1750
2048
  * customized adoption through CloneAllMetadata. Remove the CloneAllMetadata
1751
2049
  * override to activate a CreateSharedMetadataShell override.
1752
2050
  */
2051
+ /**
2052
+ * Builds this instance's AllMetadata as a zero-copy prototype-delegated shell over
2053
+ * another provider's already-loaded metadata (`Object.create(shared)`), keeping
2054
+ * `CurrentUser` as an own property on the shell instance.
2055
+ *
2056
+ * Why zero-copy prototype delegation is safe: the metadata graph (`AllMetadata` and its
2057
+ * arrays) is treated as immutable after `Config`. Refreshes swap the entire `AllMetadata`
2058
+ * reference via `UpdateLocalMetadata` rather than mutating the previous `AllMetadata`
2059
+ * instance in place, so in-flight shells continue reading a consistent snapshot while
2060
+ * avoiding 19 array shallow-copies on every per-request provider creation.
2061
+ */
1753
2062
  protected CreateSharedMetadataShell(shared: AllMetadata): AllMetadata;
1754
2063
  /**
1755
2064
  * Adopts the global provider's metadata for this instance without reloading it
1756
2065
  * from the server: shares the (immutable post-Config) metadata arrays by
1757
- * reference via {@link CreateSharedMetadataShell} and builds this instance's
2066
+ * reference via {@link CreateSharedMetadataShell} and shares or builds this instance's
1758
2067
  * entity lookup maps.
1759
2068
  */
1760
2069
  protected CopyMetadataFromGlobalProvider(): boolean;
@@ -1802,7 +2111,7 @@ export declare abstract class ProviderBase implements IMetadataProvider, IRunVie
1802
2111
  * metadata post-processing, replacing repeated O(N*M) filter scans.
1803
2112
  */
1804
2113
  private groupByNormalizedUUID;
1805
- protected PostProcessEntityMetadata(entities: EntityMetadataRow[], fields: EntityFieldMetadataRow[], fieldValues: EntityFieldValueMetadataRow[], permissions: EntityChildMetadataRow[], relationships: EntityChildMetadataRow[], settings: EntityChildMetadataRow[], organicKeys?: OrganicKeyMetadataRow[], organicKeyRelatedEntities?: OrganicKeyRelatedEntityMetadataRow[], fieldPermissions?: EntityFieldPermissionMetadataRow[]): EntityInfo[];
2114
+ protected PostProcessEntityMetadata(entities: EntityMetadataRow[], fields: EntityFieldMetadataRow[], fieldValues: EntityFieldValueMetadataRow[], permissions: EntityChildMetadataRow[], relationships: EntityChildMetadataRow[], settings: EntityChildMetadataRow[], organicKeys?: OrganicKeyMetadataRow[], organicKeyRelatedEntities?: OrganicKeyRelatedEntityMetadataRow[], fieldPermissions?: EntityFieldPermissionMetadataRow[], alternateBaseViews?: EntityChildMetadataRow[]): EntityInfo[];
1806
2115
  /**
1807
2116
  * Gets the configuration data that was provided to the provider.
1808
2117
  * @returns The provider configuration including schema filters
@@ -1982,9 +2291,11 @@ export declare abstract class ProviderBase implements IMetadataProvider, IRunVie
1982
2291
  * is within the EntityField table and specifically the RelatedEntity and RelatedEntityField columns. In turn, this method uses that metadata and queries the database to determine the dependencies. To get the list of entity dependencies
1983
2292
  * you can use the utility method GetEntityDependencies(), which doesn't check for dependencies on a specific record, but rather gets the metadata in one shot that can be used for dependency checking.
1984
2293
  * @param entityName the name of the entity to check
1985
- * @param KeyValuePairs the values of the primary key of the record to check
2294
+ * @param CompositeKey the values of the primary key of the record to check
2295
+ * @param contextUser optional user context (or options when called with 3 arguments)
2296
+ * @param options optional filtering options for dependency discovery
1986
2297
  */
1987
- abstract GetRecordDependencies(entityName: string, CompositeKey: CompositeKey, contextUser?: UserInfo): Promise<RecordDependency[]>;
2298
+ abstract GetRecordDependencies(entityName: string, CompositeKey: CompositeKey, contextUser?: UserInfo | GetRecordDependenciesOptions, options?: GetRecordDependenciesOptions): Promise<RecordDependency[]>;
1988
2299
  /**
1989
2300
  * Returns a list of record IDs that are possible duplicates of the specified record.
1990
2301
  *
@@ -2196,6 +2507,8 @@ export declare abstract class ProviderBase implements IMetadataProvider, IRunVie
2196
2507
  protected UpdateLocalMetadata(res: AllMetadata): void;
2197
2508
  /**
2198
2509
  * Rebuilds the O(1) entity lookup Maps from the current AllEntities array.
2510
+ * Allocates fresh Map instances so any per-request provider sharing a previous
2511
+ * snapshot's maps is unaffected by a subsequent refresh.
2199
2512
  * Called automatically from UpdateLocalMetadata().
2200
2513
  */
2201
2514
  protected RebuildEntityMaps(): void;