@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.
- package/dist/generic/BaseEntitySaveQueue.d.ts +22 -0
- package/dist/generic/BaseEntitySaveQueue.d.ts.map +1 -1
- package/dist/generic/BaseEntitySaveQueue.js +43 -4
- package/dist/generic/BaseEntitySaveQueue.js.map +1 -1
- package/dist/generic/JSONType-interfaces/IEntityConfiguration.d.ts +18 -0
- package/dist/generic/JSONType-interfaces/IEntityConfiguration.d.ts.map +1 -1
- package/dist/generic/alternateBaseViews.d.ts +119 -0
- package/dist/generic/alternateBaseViews.d.ts.map +1 -0
- package/dist/generic/alternateBaseViews.js +223 -0
- package/dist/generic/alternateBaseViews.js.map +1 -0
- package/dist/generic/baseEngine.d.ts +82 -14
- package/dist/generic/baseEngine.d.ts.map +1 -1
- package/dist/generic/baseEngine.js +311 -46
- package/dist/generic/baseEngine.js.map +1 -1
- package/dist/generic/baseEngineRegistry.d.ts.map +1 -1
- package/dist/generic/baseEngineRegistry.js +2 -1
- package/dist/generic/baseEngineRegistry.js.map +1 -1
- package/dist/generic/baseEntity.d.ts +194 -14
- package/dist/generic/baseEntity.d.ts.map +1 -1
- package/dist/generic/baseEntity.js +638 -218
- package/dist/generic/baseEntity.js.map +1 -1
- package/dist/generic/baseInfo.d.ts +31 -3
- package/dist/generic/baseInfo.d.ts.map +1 -1
- package/dist/generic/baseInfo.js +138 -40
- package/dist/generic/baseInfo.js.map +1 -1
- package/dist/generic/contextTokens.d.ts +136 -0
- package/dist/generic/contextTokens.d.ts.map +1 -0
- package/dist/generic/contextTokens.js +319 -0
- package/dist/generic/contextTokens.js.map +1 -0
- package/dist/generic/databaseProviderBase.d.ts +103 -13
- package/dist/generic/databaseProviderBase.d.ts.map +1 -1
- package/dist/generic/databaseProviderBase.js +321 -77
- package/dist/generic/databaseProviderBase.js.map +1 -1
- package/dist/generic/embeddedRecord.d.ts +6 -0
- package/dist/generic/embeddedRecord.d.ts.map +1 -1
- package/dist/generic/embeddedRecord.js +15 -1
- package/dist/generic/embeddedRecord.js.map +1 -1
- package/dist/generic/entityCompanion.d.ts +7 -0
- package/dist/generic/entityCompanion.d.ts.map +1 -1
- package/dist/generic/entityCompanion.js +8 -0
- package/dist/generic/entityCompanion.js.map +1 -1
- package/dist/generic/entityConfiguration.d.ts +8 -2
- package/dist/generic/entityConfiguration.d.ts.map +1 -1
- package/dist/generic/entityConfiguration.js +9 -0
- package/dist/generic/entityConfiguration.js.map +1 -1
- package/dist/generic/entityInfo.d.ts +306 -12
- package/dist/generic/entityInfo.d.ts.map +1 -1
- package/dist/generic/entityInfo.js +894 -92
- package/dist/generic/entityInfo.js.map +1 -1
- package/dist/generic/entityValueFieldSecurity.d.ts +139 -0
- package/dist/generic/entityValueFieldSecurity.d.ts.map +1 -0
- package/dist/generic/entityValueFieldSecurity.js +250 -0
- package/dist/generic/entityValueFieldSecurity.js.map +1 -0
- package/dist/generic/fieldSecuritySQLGate.d.ts +350 -0
- package/dist/generic/fieldSecuritySQLGate.d.ts.map +1 -0
- package/dist/generic/fieldSecuritySQLGate.js +898 -0
- package/dist/generic/fieldSecuritySQLGate.js.map +1 -0
- package/dist/generic/indexRestrictedFields.d.ts +47 -0
- package/dist/generic/indexRestrictedFields.d.ts.map +1 -0
- package/dist/generic/indexRestrictedFields.js +129 -0
- package/dist/generic/indexRestrictedFields.js.map +1 -0
- package/dist/generic/infoProviderResolution.d.ts +16 -0
- package/dist/generic/infoProviderResolution.d.ts.map +1 -0
- package/dist/generic/infoProviderResolution.js +17 -0
- package/dist/generic/infoProviderResolution.js.map +1 -0
- package/dist/generic/interfaces.d.ts +39 -2
- package/dist/generic/interfaces.d.ts.map +1 -1
- package/dist/generic/interfaces.js.map +1 -1
- package/dist/generic/localCacheManager.d.ts +318 -51
- package/dist/generic/localCacheManager.d.ts.map +1 -1
- package/dist/generic/localCacheManager.js +675 -109
- package/dist/generic/localCacheManager.js.map +1 -1
- package/dist/generic/logging.d.ts.map +1 -1
- package/dist/generic/logging.js +7 -1
- package/dist/generic/logging.js.map +1 -1
- package/dist/generic/metadata.d.ts +3 -2
- package/dist/generic/metadata.d.ts.map +1 -1
- package/dist/generic/metadata.js +5 -2
- package/dist/generic/metadata.js.map +1 -1
- package/dist/generic/mjapiUrl.d.ts +21 -0
- package/dist/generic/mjapiUrl.d.ts.map +1 -0
- package/dist/generic/mjapiUrl.js +25 -0
- package/dist/generic/mjapiUrl.js.map +1 -0
- package/dist/generic/platformSQL.d.ts +54 -0
- package/dist/generic/platformSQL.d.ts.map +1 -1
- package/dist/generic/platformSQL.js +55 -0
- package/dist/generic/platformSQL.js.map +1 -1
- package/dist/generic/providerBase.d.ts +370 -57
- package/dist/generic/providerBase.d.ts.map +1 -1
- package/dist/generic/providerBase.js +1085 -284
- package/dist/generic/providerBase.js.map +1 -1
- package/dist/generic/queryInfo.d.ts +23 -9
- package/dist/generic/queryInfo.d.ts.map +1 -1
- package/dist/generic/queryInfo.js +73 -33
- package/dist/generic/queryInfo.js.map +1 -1
- package/dist/generic/queryInfoInterfaces.d.ts +8 -0
- package/dist/generic/queryInfoInterfaces.d.ts.map +1 -1
- package/dist/generic/queryTemplateContext.d.ts +49 -0
- package/dist/generic/queryTemplateContext.d.ts.map +1 -0
- package/dist/generic/queryTemplateContext.js +113 -0
- package/dist/generic/queryTemplateContext.js.map +1 -0
- package/dist/generic/recordChangeFieldSecurity.d.ts.map +1 -1
- package/dist/generic/recordChangeFieldSecurity.js +13 -2
- package/dist/generic/recordChangeFieldSecurity.js.map +1 -1
- package/dist/generic/relatedRecordCollection.d.ts +13 -0
- package/dist/generic/relatedRecordCollection.d.ts.map +1 -1
- package/dist/generic/relatedRecordCollection.js +44 -5
- package/dist/generic/relatedRecordCollection.js.map +1 -1
- package/dist/generic/runQuery.d.ts +42 -0
- package/dist/generic/runQuery.d.ts.map +1 -1
- package/dist/generic/runQuery.js +14 -0
- package/dist/generic/runQuery.js.map +1 -1
- package/dist/generic/runQuerySQLFilterImplementations.d.ts.map +1 -1
- package/dist/generic/runQuerySQLFilterImplementations.js +38 -2
- package/dist/generic/runQuerySQLFilterImplementations.js.map +1 -1
- package/dist/generic/saveEntityGraphOperation.d.ts.map +1 -1
- package/dist/generic/saveEntityGraphOperation.js +3 -0
- package/dist/generic/saveEntityGraphOperation.js.map +1 -1
- package/dist/generic/scoring/ReciprocalRankFusion.d.ts.map +1 -1
- package/dist/generic/scoring/ReciprocalRankFusion.js +10 -3
- package/dist/generic/scoring/ReciprocalRankFusion.js.map +1 -1
- package/dist/generic/securityInfo.d.ts +17 -6
- package/dist/generic/securityInfo.d.ts.map +1 -1
- package/dist/generic/securityInfo.js +25 -10
- package/dist/generic/securityInfo.js.map +1 -1
- package/dist/generic/telemetryManager.d.ts +9 -0
- package/dist/generic/telemetryManager.d.ts.map +1 -1
- package/dist/generic/telemetryManager.js +5 -1
- package/dist/generic/telemetryManager.js.map +1 -1
- package/dist/generic/util.d.ts.map +1 -1
- package/dist/generic/util.js +3 -0
- package/dist/generic/util.js.map +1 -1
- package/dist/generic/wellKnownUserSource.d.ts +3 -2
- package/dist/generic/wellKnownUserSource.d.ts.map +1 -1
- package/dist/generic/wellKnownUserSource.js +18 -9
- package/dist/generic/wellKnownUserSource.js.map +1 -1
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/dist/views/runView.d.ts +89 -0
- package/dist/views/runView.d.ts.map +1 -1
- package/dist/views/runView.js +36 -0
- package/dist/views/runView.js.map +1 -1
- package/dist/views/viewProperties.d.ts +55 -0
- package/dist/views/viewProperties.d.ts.map +1 -0
- package/dist/views/viewProperties.js +258 -0
- package/dist/views/viewProperties.js.map +1 -0
- package/package.json +3 -3
- package/readme.md +29 -4
package/dist/views/runView.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { MJGlobal } from '@memberjunction/global';
|
|
2
|
+
import { IsExplicitBaseViewOverride } from '../generic/alternateBaseViews.js';
|
|
3
|
+
import { ViewPropertyValuesKey } from './viewProperties.js';
|
|
2
4
|
/**
|
|
3
5
|
* Thrown by {@link RunView} / RunViews when `AfterKey` is provided but cannot be honored.
|
|
4
6
|
*
|
|
@@ -49,6 +51,20 @@ export function IsKeysetPaginationOrderableType(sqlTypeName) {
|
|
|
49
51
|
const normalized = sqlTypeName.replace(/\s*\([^)]*\)\s*$/, '').trim().toLowerCase();
|
|
50
52
|
return KEYSET_PAGINATION_ORDERABLE_PK_TYPES.includes(normalized);
|
|
51
53
|
}
|
|
54
|
+
/**
|
|
55
|
+
* `RunViewParams` fields only server code may set. A transport that builds run params from a
|
|
56
|
+
* client's request body (the REST endpoint) removes these before running the view, so a client
|
|
57
|
+
* cannot choose them. GraphQL run inputs never carry them in the first place.
|
|
58
|
+
*/
|
|
59
|
+
export const SERVER_ONLY_RUN_VIEW_PARAMS = ['TimeZone', 'ServerProperties'];
|
|
60
|
+
/** A copy of client-supplied run params without the {@link SERVER_ONLY_RUN_VIEW_PARAMS} fields. */
|
|
61
|
+
export function StripServerOnlyRunViewParams(params) {
|
|
62
|
+
const copy = { ...params };
|
|
63
|
+
for (const key of SERVER_ONLY_RUN_VIEW_PARAMS) {
|
|
64
|
+
delete copy[key];
|
|
65
|
+
}
|
|
66
|
+
return copy;
|
|
67
|
+
}
|
|
52
68
|
/**
|
|
53
69
|
* Parameters for running either a stored or dynamic view.
|
|
54
70
|
* A stored view is a view that is saved in the database and can be run either by ID or Name.
|
|
@@ -98,6 +114,8 @@ export class RunViewParams {
|
|
|
98
114
|
return false;
|
|
99
115
|
if (a.MaxRows !== b.MaxRows)
|
|
100
116
|
return false;
|
|
117
|
+
if (a.SkipTotalRowCount !== b.SkipTotalRowCount)
|
|
118
|
+
return false;
|
|
101
119
|
if (a.StartRow !== b.StartRow)
|
|
102
120
|
return false;
|
|
103
121
|
if (!RunViewParams.afterKeyEqual(a.AfterKey, b.AfterKey))
|
|
@@ -116,11 +134,22 @@ export class RunViewParams {
|
|
|
116
134
|
// undefined and false mean the same thing and must not cause a spurious one.
|
|
117
135
|
if ((a.IncludeBinaryFields === true) !== (b.IncludeBinaryFields === true))
|
|
118
136
|
return false;
|
|
137
|
+
if (a.TimeZone !== b.TimeZone)
|
|
138
|
+
return false;
|
|
139
|
+
if (ViewPropertyValuesKey(a.Properties, a.ServerProperties) !== ViewPropertyValuesKey(b.Properties, b.ServerProperties))
|
|
140
|
+
return false;
|
|
119
141
|
// A Live↔Materialized DataSource toggle changes the result set and MUST trigger a reload. Compared via
|
|
120
142
|
// IsMaterializedDataSource so undefined/'Live' are treated as equal (no spurious reload) while a switch to
|
|
121
143
|
// (or from) 'Materialized' is not — matching the read-routing decision everywhere else.
|
|
122
144
|
if (IsMaterializedDataSource(a.DataSource) !== IsMaterializedDataSource(b.DataSource))
|
|
123
145
|
return false;
|
|
146
|
+
// Switching AlternateBaseView (case-insensitive, trimmed) changes the underlying SQL view and MUST trigger
|
|
147
|
+
// a reload. An explicit empty string (`""`) overrides a saved User View's pinned `DisplayState.alternateBaseView`
|
|
148
|
+
// back to `BaseView` and therefore differs from `undefined` (which inherits the saved view's pin).
|
|
149
|
+
if (IsExplicitBaseViewOverride(a) !== IsExplicitBaseViewOverride(b))
|
|
150
|
+
return false;
|
|
151
|
+
if (NormalizeAlternateBaseViewName(a.AlternateBaseView) !== NormalizeAlternateBaseViewName(b.AlternateBaseView))
|
|
152
|
+
return false;
|
|
124
153
|
// Compare ViewEntity by reference (deep comparison would be expensive)
|
|
125
154
|
if (a.ViewEntity !== b.ViewEntity)
|
|
126
155
|
return false;
|
|
@@ -219,6 +248,13 @@ export class RunViewParams {
|
|
|
219
248
|
export function IsMaterializedDataSource(dataSource) {
|
|
220
249
|
return typeof dataSource === 'string' && dataSource.trim().toLowerCase() === 'materialized';
|
|
221
250
|
}
|
|
251
|
+
/**
|
|
252
|
+
* Normalizes an `AlternateBaseView` name for comparison and cache-key construction:
|
|
253
|
+
* trims whitespace and lowercases. Returns `''` when the input is null, undefined, or whitespace-only.
|
|
254
|
+
*/
|
|
255
|
+
export function NormalizeAlternateBaseViewName(name) {
|
|
256
|
+
return typeof name === 'string' ? name.trim().toLowerCase() : '';
|
|
257
|
+
}
|
|
222
258
|
/**
|
|
223
259
|
* Class for running views in a generic, tier-independent manner - uses a provider model for
|
|
224
260
|
* implementation transparently from the viewpoint of the consumer of the class. By default the RunView class you create will
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runView.js","sourceRoot":"","sources":["../../src/views/runView.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAc,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"runView.js","sourceRoot":"","sources":["../../src/views/runView.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* User View properties at run time: checking a view's declared properties when it is saved,
|
|
3
|
+
* working out each property's value for one run, and the SQL predicate that value drives.
|
|
4
|
+
*
|
|
5
|
+
* A property's value comes from, in order: the run's `ServerProperties` (server code only), the
|
|
6
|
+
* run's `Properties` (refused for a property whose `AllowOverride` is false), then the property's
|
|
7
|
+
* `Default`. A key that is present with `null` means "no value": the default is not used and no
|
|
8
|
+
* predicate is added. Every value is checked against the property's type and written as a typed
|
|
9
|
+
* SQL literal; nothing a caller sends is pasted into SQL as text.
|
|
10
|
+
*/
|
|
11
|
+
import type { SQLDialect } from '@memberjunction/sql-dialect';
|
|
12
|
+
import { PropertyResolutionContext, PropertyValue, PropertyValueType, UserViewProperty, UserViewPropertyOperator } from '../generic/contextTokens.js';
|
|
13
|
+
/** A run's value for one view property: one value, or a list for the `IN` operator. */
|
|
14
|
+
export type ViewPropertyValue = PropertyValue | PropertyValue[];
|
|
15
|
+
/** A run's values for a view's properties, keyed by property name. */
|
|
16
|
+
export type ViewPropertyValues = Record<string, ViewPropertyValue>;
|
|
17
|
+
/** Every operator a view property can use. */
|
|
18
|
+
export declare const USER_VIEW_PROPERTY_OPERATORS: readonly UserViewPropertyOperator[];
|
|
19
|
+
/** The operators each property type allows: ranges and lists only where they mean something. */
|
|
20
|
+
export declare const USER_VIEW_PROPERTY_OPERATORS_BY_TYPE: Record<PropertyValueType, readonly UserViewPropertyOperator[]>;
|
|
21
|
+
/**
|
|
22
|
+
* Checks a view's declared properties, as when the view is saved.
|
|
23
|
+
* @param properties the parsed `Properties` JSON; null or undefined means the view has none
|
|
24
|
+
* @param fieldNames the field names of the view's entity
|
|
25
|
+
* @returns the problems found, each naming the property; empty when the properties are valid
|
|
26
|
+
*/
|
|
27
|
+
export declare function ValidateUserViewProperties(properties: unknown, fieldNames: readonly string[]): string[];
|
|
28
|
+
/**
|
|
29
|
+
* Works out every declared property's value for one run.
|
|
30
|
+
* @param declared the view's properties
|
|
31
|
+
* @param clientValues values a client may set (`RunViewParams.Properties`)
|
|
32
|
+
* @param serverValues values only server code sets (`RunViewParams.ServerProperties`)
|
|
33
|
+
* @param ctx the context user, clock and time zone that token defaults are worked out with
|
|
34
|
+
* @returns each declared property's value, `null` where the run adds no predicate for it
|
|
35
|
+
* @throws naming the property: a name the view does not declare, a client value for a property
|
|
36
|
+
* that does not allow override, a value of the wrong type, a required property with no value,
|
|
37
|
+
* or a default that cannot be worked out
|
|
38
|
+
*/
|
|
39
|
+
export declare function ResolveViewPropertyValues(declared: readonly UserViewProperty[], clientValues: ViewPropertyValues | undefined, serverValues: ViewPropertyValues | undefined, ctx: PropertyResolutionContext): ViewPropertyValues;
|
|
40
|
+
/**
|
|
41
|
+
* The WHERE clause a run's property values add: one predicate per property with a value, ANDed.
|
|
42
|
+
* Values are checked again here, so nothing reaches SQL unchecked whoever produced `values`.
|
|
43
|
+
* @param fieldNames the field names of the view's entity; a target that is not one is refused
|
|
44
|
+
* @param dialect the database platform, for identifier quoting and literals
|
|
45
|
+
* @returns '' when no property has a value
|
|
46
|
+
*/
|
|
47
|
+
export declare function BuildViewPropertyFilter(declared: readonly UserViewProperty[], values: ViewPropertyValues, fieldNames: readonly string[], dialect: SQLDialect): string;
|
|
48
|
+
/**
|
|
49
|
+
* A stable text key for a run's property values (names sorted), for result-cache fingerprints and
|
|
50
|
+
* params comparison. Empty when neither map holds a value, so a run without properties keys as before.
|
|
51
|
+
*/
|
|
52
|
+
export declare function ViewPropertyValuesKey(clientValues?: ViewPropertyValues, serverValues?: ViewPropertyValues): string;
|
|
53
|
+
/** True when a property-values map holds at least one key with a defined value. */
|
|
54
|
+
export declare function HasViewPropertyValues(values: ViewPropertyValues | undefined | null): boolean;
|
|
55
|
+
//# sourceMappingURL=viewProperties.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"viewProperties.d.ts","sourceRoot":"","sources":["../../src/views/viewProperties.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAIH,yBAAyB,EACzB,aAAa,EACb,iBAAiB,EAEjB,gBAAgB,EAChB,wBAAwB,EAE3B,MAAM,0BAA0B,CAAC;AAElC,uFAAuF;AACvF,MAAM,MAAM,iBAAiB,GAAG,aAAa,GAAG,aAAa,EAAE,CAAC;AAEhE,sEAAsE;AACtE,MAAM,MAAM,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;AAEnE,8CAA8C;AAC9C,eAAO,MAAM,4BAA4B,EAAE,SAAS,wBAAwB,EAA4C,CAAC;AAEzH,gGAAgG;AAChG,eAAO,MAAM,oCAAoC,EAAE,MAAM,CAAC,iBAAiB,EAAE,SAAS,wBAAwB,EAAE,CAM/G,CAAC;AAKF;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,UAAU,EAAE,OAAO,EAAE,UAAU,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CAqBvG;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,yBAAyB,CACrC,QAAQ,EAAE,SAAS,gBAAgB,EAAE,EACrC,YAAY,EAAE,kBAAkB,GAAG,SAAS,EAC5C,YAAY,EAAE,kBAAkB,GAAG,SAAS,EAC5C,GAAG,EAAE,yBAAyB,GAC/B,kBAAkB,CASpB;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACnC,QAAQ,EAAE,SAAS,gBAAgB,EAAE,EACrC,MAAM,EAAE,kBAAkB,EAC1B,UAAU,EAAE,SAAS,MAAM,EAAE,EAC7B,OAAO,EAAE,UAAU,GACpB,MAAM,CASR;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,YAAY,CAAC,EAAE,kBAAkB,EAAE,YAAY,CAAC,EAAE,kBAAkB,GAAG,MAAM,CAIlH;AAED,mFAAmF;AACnF,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,kBAAkB,GAAG,SAAS,GAAG,IAAI,GAAG,OAAO,CAE5F"}
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* User View properties at run time: checking a view's declared properties when it is saved,
|
|
3
|
+
* working out each property's value for one run, and the SQL predicate that value drives.
|
|
4
|
+
*
|
|
5
|
+
* A property's value comes from, in order: the run's `ServerProperties` (server code only), the
|
|
6
|
+
* run's `Properties` (refused for a property whose `AllowOverride` is false), then the property's
|
|
7
|
+
* `Default`. A key that is present with `null` means "no value": the default is not used and no
|
|
8
|
+
* predicate is added. Every value is checked against the property's type and written as a typed
|
|
9
|
+
* SQL literal; nothing a caller sends is pasted into SQL as text.
|
|
10
|
+
*/
|
|
11
|
+
import { EscapeSQLString } from '@memberjunction/global';
|
|
12
|
+
import { IsPropertyValueOfType, PROPERTY_VALUE_TYPES, ResolvePropertyDefault, ValidatePropertyDefault, } from '../generic/contextTokens.js';
|
|
13
|
+
/** Every operator a view property can use. */
|
|
14
|
+
export const USER_VIEW_PROPERTY_OPERATORS = ['=', '<>', 'IN', '>', '>=', '<', '<='];
|
|
15
|
+
/** The operators each property type allows: ranges and lists only where they mean something. */
|
|
16
|
+
export const USER_VIEW_PROPERTY_OPERATORS_BY_TYPE = {
|
|
17
|
+
string: USER_VIEW_PROPERTY_OPERATORS,
|
|
18
|
+
number: USER_VIEW_PROPERTY_OPERATORS,
|
|
19
|
+
date: USER_VIEW_PROPERTY_OPERATORS,
|
|
20
|
+
uuid: ['=', '<>', 'IN'],
|
|
21
|
+
boolean: ['=', '<>'],
|
|
22
|
+
};
|
|
23
|
+
/** `YYYY-MM-DD` with nothing after it: kept as written rather than turned into an instant. */
|
|
24
|
+
const DATE_ONLY = /^\d{4}-\d{2}-\d{2}$/;
|
|
25
|
+
/**
|
|
26
|
+
* Checks a view's declared properties, as when the view is saved.
|
|
27
|
+
* @param properties the parsed `Properties` JSON; null or undefined means the view has none
|
|
28
|
+
* @param fieldNames the field names of the view's entity
|
|
29
|
+
* @returns the problems found, each naming the property; empty when the properties are valid
|
|
30
|
+
*/
|
|
31
|
+
export function ValidateUserViewProperties(properties, fieldNames) {
|
|
32
|
+
if (properties === null || properties === undefined) {
|
|
33
|
+
return [];
|
|
34
|
+
}
|
|
35
|
+
if (!Array.isArray(properties)) {
|
|
36
|
+
return ['Properties must be a list.'];
|
|
37
|
+
}
|
|
38
|
+
const errors = [];
|
|
39
|
+
const seen = new Set();
|
|
40
|
+
properties.forEach((property, index) => {
|
|
41
|
+
const name = isPlainObject(property) && typeof property.Name === 'string' ? property.Name.trim() : '';
|
|
42
|
+
const label = name ? `Property "${name}"` : `Property ${index + 1}`;
|
|
43
|
+
for (const problem of propertyErrors(property, fieldNames)) {
|
|
44
|
+
errors.push(`${label}: ${problem}`);
|
|
45
|
+
}
|
|
46
|
+
if (name && seen.has(name.toLowerCase())) {
|
|
47
|
+
errors.push(`${label}: another property has the same name.`);
|
|
48
|
+
}
|
|
49
|
+
seen.add(name.toLowerCase());
|
|
50
|
+
});
|
|
51
|
+
return errors;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Works out every declared property's value for one run.
|
|
55
|
+
* @param declared the view's properties
|
|
56
|
+
* @param clientValues values a client may set (`RunViewParams.Properties`)
|
|
57
|
+
* @param serverValues values only server code sets (`RunViewParams.ServerProperties`)
|
|
58
|
+
* @param ctx the context user, clock and time zone that token defaults are worked out with
|
|
59
|
+
* @returns each declared property's value, `null` where the run adds no predicate for it
|
|
60
|
+
* @throws naming the property: a name the view does not declare, a client value for a property
|
|
61
|
+
* that does not allow override, a value of the wrong type, a required property with no value,
|
|
62
|
+
* or a default that cannot be worked out
|
|
63
|
+
*/
|
|
64
|
+
export function ResolveViewPropertyValues(declared, clientValues, serverValues, ctx) {
|
|
65
|
+
const names = new Set(declared.map(p => p.Name));
|
|
66
|
+
assertKnownNames(names, clientValues, 'Properties');
|
|
67
|
+
assertKnownNames(names, serverValues, 'ServerProperties');
|
|
68
|
+
const resolved = {};
|
|
69
|
+
for (const property of declared) {
|
|
70
|
+
resolved[property.Name] = resolveOne(property, clientValues, serverValues, ctx);
|
|
71
|
+
}
|
|
72
|
+
return resolved;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* The WHERE clause a run's property values add: one predicate per property with a value, ANDed.
|
|
76
|
+
* Values are checked again here, so nothing reaches SQL unchecked whoever produced `values`.
|
|
77
|
+
* @param fieldNames the field names of the view's entity; a target that is not one is refused
|
|
78
|
+
* @param dialect the database platform, for identifier quoting and literals
|
|
79
|
+
* @returns '' when no property has a value
|
|
80
|
+
*/
|
|
81
|
+
export function BuildViewPropertyFilter(declared, values, fieldNames, dialect) {
|
|
82
|
+
const predicates = [];
|
|
83
|
+
for (const property of declared) {
|
|
84
|
+
const value = values[property.Name];
|
|
85
|
+
if (value !== null && value !== undefined) {
|
|
86
|
+
predicates.push(buildPredicate(property, value, fieldNames, dialect));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return predicates.join(' AND ');
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* A stable text key for a run's property values (names sorted), for result-cache fingerprints and
|
|
93
|
+
* params comparison. Empty when neither map holds a value, so a run without properties keys as before.
|
|
94
|
+
*/
|
|
95
|
+
export function ViewPropertyValuesKey(clientValues, serverValues) {
|
|
96
|
+
const client = sortedEntries(clientValues);
|
|
97
|
+
const server = sortedEntries(serverValues);
|
|
98
|
+
return client.length === 0 && server.length === 0 ? '' : JSON.stringify({ p: client, s: server });
|
|
99
|
+
}
|
|
100
|
+
/** True when a property-values map holds at least one key with a defined value. */
|
|
101
|
+
export function HasViewPropertyValues(values) {
|
|
102
|
+
return sortedEntries(values ?? undefined).length > 0;
|
|
103
|
+
}
|
|
104
|
+
function sortedEntries(values) {
|
|
105
|
+
if (!values || typeof values !== 'object') {
|
|
106
|
+
return [];
|
|
107
|
+
}
|
|
108
|
+
return Object.keys(values)
|
|
109
|
+
.filter(key => values[key] !== undefined)
|
|
110
|
+
.sort()
|
|
111
|
+
.map(key => [key, values[key]]);
|
|
112
|
+
}
|
|
113
|
+
function isPlainObject(value) {
|
|
114
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
115
|
+
}
|
|
116
|
+
function hasValue(values, name) {
|
|
117
|
+
return !!values && Object.prototype.hasOwnProperty.call(values, name) && values[name] !== undefined;
|
|
118
|
+
}
|
|
119
|
+
/** Problems with one declared property, without its label. */
|
|
120
|
+
function propertyErrors(property, fieldNames) {
|
|
121
|
+
if (!isPlainObject(property)) {
|
|
122
|
+
return ['must be an object.'];
|
|
123
|
+
}
|
|
124
|
+
const errors = [];
|
|
125
|
+
if (typeof property.Name !== 'string' || property.Name.trim().length === 0) {
|
|
126
|
+
errors.push('Name is required.');
|
|
127
|
+
}
|
|
128
|
+
const type = property.Type;
|
|
129
|
+
if (!PROPERTY_VALUE_TYPES.includes(type)) {
|
|
130
|
+
errors.push(`Type must be one of ${PROPERTY_VALUE_TYPES.join(', ')}.`);
|
|
131
|
+
return errors;
|
|
132
|
+
}
|
|
133
|
+
errors.push(...targetErrors(property.Target, type, fieldNames));
|
|
134
|
+
if (property.Default !== undefined) {
|
|
135
|
+
errors.push(...ValidatePropertyDefault(property.Default, type));
|
|
136
|
+
}
|
|
137
|
+
for (const flag of ['IsRequired', 'AllowOverride']) {
|
|
138
|
+
if (property[flag] !== undefined && typeof property[flag] !== 'boolean') {
|
|
139
|
+
errors.push(`${flag} must be true or false.`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
if (property.Description !== undefined && typeof property.Description !== 'string') {
|
|
143
|
+
errors.push('Description must be text.');
|
|
144
|
+
}
|
|
145
|
+
return errors;
|
|
146
|
+
}
|
|
147
|
+
function targetErrors(target, type, fieldNames) {
|
|
148
|
+
if (!isPlainObject(target)) {
|
|
149
|
+
return ['Target is required, with a Field and an Operator.'];
|
|
150
|
+
}
|
|
151
|
+
const errors = [];
|
|
152
|
+
if (typeof target.Field !== 'string' || !findField(fieldNames, target.Field)) {
|
|
153
|
+
errors.push(`Target.Field "${String(target.Field)}" is not a field of the view's entity.`);
|
|
154
|
+
}
|
|
155
|
+
const operator = target.Operator;
|
|
156
|
+
if (!USER_VIEW_PROPERTY_OPERATORS.includes(operator)) {
|
|
157
|
+
errors.push(`Target.Operator must be one of ${USER_VIEW_PROPERTY_OPERATORS.join(', ')}.`);
|
|
158
|
+
}
|
|
159
|
+
else if (!USER_VIEW_PROPERTY_OPERATORS_BY_TYPE[type].includes(operator)) {
|
|
160
|
+
errors.push(`Target.Operator ${operator} does not apply to a ${type} property. Allowed: ${USER_VIEW_PROPERTY_OPERATORS_BY_TYPE[type].join(', ')}.`);
|
|
161
|
+
}
|
|
162
|
+
return errors;
|
|
163
|
+
}
|
|
164
|
+
function findField(fieldNames, name) {
|
|
165
|
+
const wanted = name.trim().toLowerCase();
|
|
166
|
+
return fieldNames.find(f => f.toLowerCase() === wanted);
|
|
167
|
+
}
|
|
168
|
+
function assertKnownNames(names, values, source) {
|
|
169
|
+
if (values === undefined || values === null) {
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
if (!isPlainObject(values)) {
|
|
173
|
+
throw new Error(`${source} must be an object of property values keyed by name.`);
|
|
174
|
+
}
|
|
175
|
+
for (const name of Object.keys(values)) {
|
|
176
|
+
if (!names.has(name)) {
|
|
177
|
+
const declared = names.size > 0 ? `The view declares: ${[...names].join(', ')}.` : 'The view declares no properties.';
|
|
178
|
+
throw new Error(`Unknown view property "${name}" in ${source}. ${declared}`);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
function resolveOne(property, clientValues, serverValues, ctx) {
|
|
183
|
+
if (hasValue(clientValues, property.Name) && property.AllowOverride === false) {
|
|
184
|
+
throw new Error(`View property "${property.Name}" is set by the server; a client value for it is refused.`);
|
|
185
|
+
}
|
|
186
|
+
const value = hasValue(serverValues, property.Name) ? serverValues[property.Name]
|
|
187
|
+
: hasValue(clientValues, property.Name) ? clientValues[property.Name]
|
|
188
|
+
: defaultValue(property, ctx);
|
|
189
|
+
const checked = checkValue(property, value);
|
|
190
|
+
if (checked === null && property.IsRequired) {
|
|
191
|
+
throw new Error(`View property "${property.Name}" is required and has no value.`);
|
|
192
|
+
}
|
|
193
|
+
return checked;
|
|
194
|
+
}
|
|
195
|
+
function defaultValue(property, ctx) {
|
|
196
|
+
if (!property.Default) {
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
try {
|
|
200
|
+
return ResolvePropertyDefault(property.Default, ctx);
|
|
201
|
+
}
|
|
202
|
+
catch (e) {
|
|
203
|
+
throw new Error(`View property "${property.Name}": its default cannot be worked out. ${e instanceof Error ? e.message : String(e)}`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
/** A value checked against the property: null, one value of its type, or (for `IN`) a list of them. */
|
|
207
|
+
function checkValue(property, value) {
|
|
208
|
+
if (value === null || value === undefined) {
|
|
209
|
+
return null;
|
|
210
|
+
}
|
|
211
|
+
if (!Array.isArray(value)) {
|
|
212
|
+
return checkScalar(property, value);
|
|
213
|
+
}
|
|
214
|
+
if (property.Target?.Operator !== 'IN') {
|
|
215
|
+
throw new Error(`View property "${property.Name}" takes one value, not a list; only the IN operator takes a list.`);
|
|
216
|
+
}
|
|
217
|
+
return value.map(item => checkScalar(property, item));
|
|
218
|
+
}
|
|
219
|
+
function checkScalar(property, value) {
|
|
220
|
+
const scalar = typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean';
|
|
221
|
+
if (!scalar || !IsPropertyValueOfType(value, property.Type)) {
|
|
222
|
+
throw new Error(`View property "${property.Name}" needs a ${property.Type} value; ${JSON.stringify(value)} is not one.`);
|
|
223
|
+
}
|
|
224
|
+
if (property.Type === 'date' && !DATE_ONLY.test(value)) {
|
|
225
|
+
return new Date(value).toISOString(); // one spelling per instant, in UTC
|
|
226
|
+
}
|
|
227
|
+
return value;
|
|
228
|
+
}
|
|
229
|
+
function buildPredicate(property, value, fieldNames, dialect) {
|
|
230
|
+
const target = property.Target;
|
|
231
|
+
const field = typeof target?.Field === 'string' ? findField(fieldNames, target.Field) : undefined;
|
|
232
|
+
if (!field) {
|
|
233
|
+
throw new Error(`View property "${property.Name}" targets "${String(target?.Field)}", which is not a field of the view's entity.`);
|
|
234
|
+
}
|
|
235
|
+
if (!USER_VIEW_PROPERTY_OPERATORS_BY_TYPE[property.Type]?.includes(target.Operator)) {
|
|
236
|
+
throw new Error(`View property "${property.Name}": operator ${String(target.Operator)} does not apply to a ${property.Type} property.`);
|
|
237
|
+
}
|
|
238
|
+
const column = dialect.QuoteIdentifier(field);
|
|
239
|
+
const checked = checkValue(property, value);
|
|
240
|
+
if (target.Operator !== 'IN') {
|
|
241
|
+
return `${column} ${target.Operator} ${sqlLiteral(property.Type, checked, dialect)}`;
|
|
242
|
+
}
|
|
243
|
+
const list = Array.isArray(checked) ? checked : [checked];
|
|
244
|
+
// An empty list matches nothing, as IN over an empty set does; SQL has no empty IN ().
|
|
245
|
+
return list.length === 0 ? '1 = 0' : `${column} IN (${list.map(v => sqlLiteral(property.Type, v, dialect)).join(', ')})`;
|
|
246
|
+
}
|
|
247
|
+
/** A checked, non-null value as a typed SQL literal. */
|
|
248
|
+
function sqlLiteral(type, value, dialect) {
|
|
249
|
+
if (type === 'number') {
|
|
250
|
+
return String(value);
|
|
251
|
+
}
|
|
252
|
+
if (type === 'boolean') {
|
|
253
|
+
return dialect.BooleanLiteral(value);
|
|
254
|
+
}
|
|
255
|
+
const text = String(value);
|
|
256
|
+
return `${dialect.StringLiteralPrefix(text)}'${EscapeSQLString(text)}'`;
|
|
257
|
+
}
|
|
258
|
+
//# sourceMappingURL=viewProperties.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"viewProperties.js","sourceRoot":"","sources":["../../src/views/viewProperties.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@memberjunction/core",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "6.2.0-edge.
|
|
4
|
+
"version": "6.2.0-edge.6",
|
|
5
5
|
"description": "MemberJunction: Core Library including Metadata, Application, Entity Retrieval and Manipulation, and Utilities",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
"vitest": "^3.1.1"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@memberjunction/global": "6.2.0-edge.
|
|
21
|
-
"@memberjunction/sql-dialect": "6.2.0-edge.
|
|
20
|
+
"@memberjunction/global": "6.2.0-edge.6",
|
|
21
|
+
"@memberjunction/sql-dialect": "6.2.0-edge.6",
|
|
22
22
|
"rxjs": "^7.8.2",
|
|
23
23
|
"zod": "^3.25.0",
|
|
24
24
|
"debug": "^4.4.3"
|
package/readme.md
CHANGED
|
@@ -146,6 +146,7 @@ flowchart LR
|
|
|
146
146
|
| `telemetryManager.ts` | `TelemetryManager` for operation tracking and pattern detection |
|
|
147
147
|
| `queryCache.ts` / `QueryCacheConfig.ts` | LRU query result cache with TTL support |
|
|
148
148
|
| `databaseProviderBase.ts` | `DatabaseProviderBase` for server-side SQL execution and transactions |
|
|
149
|
+
| `BaseEntitySaveQueue.ts` | `BaseEntitySaveQueue` for per-record serialized fire-and-forget execution log saves with optional `saveReturnMode` |
|
|
149
150
|
| `util.ts` | Utility functions: `TypeScriptTypeFromSQLType`, `FormatValue`, `CodeNameFromString` |
|
|
150
151
|
| `runView.ts` | `RunView` class and `RunViewParams` for executing stored and dynamic views |
|
|
151
152
|
| `viewInfo.ts` | View metadata classes: `ViewInfo`, `ViewColumnInfo`, `ViewFilterInfo` |
|
|
@@ -329,25 +330,49 @@ It is **not** enforced on the low-level `EntityField.Value` accessor. Framework-
|
|
|
329
330
|
|
|
330
331
|
> Note: `EntityField.ActiveStatusAssertions` is retained as a **deprecated no-op** for backward compatibility. There is nothing to toggle at the field level anymore, since `EntityField.Value` no longer asserts.
|
|
331
332
|
|
|
333
|
+
#### Lazy `EntityField` Hydration (`_raw` Fast Path)
|
|
334
|
+
|
|
335
|
+
When records are loaded via `LoadFromData()` (for example, inside `RunView` with `ResultType: 'entity_object'`), `BaseEntity` stores the database row directly in an internal `_raw` reference without eagerly populating every `EntityField` value via `SetMany()`:
|
|
336
|
+
|
|
337
|
+
- **Zero-allocation read path**: `Get()`, `GetAll()`, `Dirty`, and `FieldIsDirty()` read directly from `_raw` (with `EntityFieldInfo.DefaultValue` fallback and `NotLoaded` column awareness for partial `Fields` projections), avoiding $N \times F$ `EntityField` setter invocations across large result sets.
|
|
338
|
+
- **Lazy hydration on mutation or `Fields` inspection**: The first call to `Set()`, `SetMany()`, `Revert()`, `Validate()`, `Save()`, or `entity.Fields` transparently hydrates `EntityField` instances from `_raw` and clears `_raw`, preserving full dirty tracking and `OldValue` history.
|
|
339
|
+
|
|
332
340
|
#### Save and Delete
|
|
333
341
|
|
|
334
342
|
```typescript
|
|
335
|
-
import { EntitySaveOptions } from '@memberjunction/core';
|
|
343
|
+
import { EntitySaveOptions, BaseEntitySaveQueue } from '@memberjunction/core';
|
|
336
344
|
|
|
337
345
|
// Simple save
|
|
338
346
|
const success = await entity.Save();
|
|
339
347
|
|
|
340
348
|
// Save with options
|
|
341
349
|
const options = new EntitySaveOptions();
|
|
342
|
-
options.IgnoreDirtyState = true;
|
|
343
|
-
options.SkipEntityAIActions = true;
|
|
344
|
-
options.SkipEntityActions = true;
|
|
350
|
+
options.IgnoreDirtyState = true; // Force save even if no changes detected
|
|
351
|
+
options.SkipEntityAIActions = true; // Skip AI-related actions
|
|
352
|
+
options.SkipEntityActions = true; // Skip entity actions
|
|
353
|
+
options.SaveReturnMode = 'datestamps_only'; // Return only PK + __mj_CreatedAt/__mj_UpdatedAt
|
|
345
354
|
await entity.Save(options);
|
|
346
355
|
|
|
356
|
+
// Fire-and-forget per-entity save queue (e.g., high-write execution logs)
|
|
357
|
+
const logQueue = new BaseEntitySaveQueue({ saveReturnMode: 'datestamps_only' });
|
|
358
|
+
logQueue.Insert(stepEntity);
|
|
359
|
+
logQueue.Update(stepEntity, (s) => { s.Status = 'Complete'; });
|
|
360
|
+
await logQueue.Flush();
|
|
361
|
+
|
|
347
362
|
// Delete
|
|
348
363
|
await entity.Delete();
|
|
349
364
|
```
|
|
350
365
|
|
|
366
|
+
##### `SaveReturnMode: 'full' | 'datestamps_only'`
|
|
367
|
+
|
|
368
|
+
By default (`SaveReturnMode: 'full'`), `spCreate*` and `spUpdate*` re-select the full row (`SELECT * FROM [schema].[BaseView] WHERE [PK] = @ID`) after every `INSERT` or `UPDATE` so joined virtual columns, SQL-computed columns, and trigger mutations are immediately reflected in memory.
|
|
369
|
+
|
|
370
|
+
For high-write tables whose primary keys are pre-populated UUIDs (`NewRecord()`) and whose `BaseView` performs expensive joins or carries large `NVARCHAR(MAX)` columns (such as `AIPromptRun`, `AIAgentRun`, `AIAgentRunStep`, `ConversationDetail`, or `AuditLog`), you can configure **`SaveReturnMode: 'datestamps_only'`**:
|
|
371
|
+
|
|
372
|
+
- **Entity-level (`Entity.Configuration`)**: Set `{"SaveReturnMode": "datestamps_only"}` in `Entity.Configuration`. CodeGen emits `spCreate*` and `spUpdate*` routines that query only the primary key(s) and `[__mj_CreatedAt]` / `[__mj_UpdatedAt]` directly from `BaseTable`, completely skipping `BaseView` and its joins.
|
|
373
|
+
- **Per-save override (`EntitySaveOptions.SaveReturnMode`)**: Pass `{ SaveReturnMode: 'datestamps_only' }` to `entity.Save(options)` on any entity. Even if the entity's stored procedure returns the full view row, the data provider projects only the primary key(s) and timestamp columns over the wire and merges them onto the in-memory `BaseEntity` fields without clobbering existing in-memory values.
|
|
374
|
+
- **Why DB timestamps are always returned**: `DatabaseProviderBase.Save()` performs optimistic concurrency checking (`checkRecordSavingInconsistency`) by comparing the entity's in-memory `__mj_UpdatedAt` against the database row before every update. Returning `[__mj_CreatedAt]` and `[__mj_UpdatedAt]` from `BaseTable` (which is a single clustered PK index seek on the row already in the buffer pool) keeps concurrency checking 100% accurate on subsequent saves while avoiding `BaseView` join evaluation and large-column network transfer.
|
|
375
|
+
|
|
351
376
|
#### GetAll() for Spread Operator
|
|
352
377
|
|
|
353
378
|
BaseEntity uses getter/setter properties, so the spread operator will not capture field values. Use `GetAll()` instead.
|