@memberjunction/core-entities 5.37.0 → 5.39.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/custom/MJQueryEntityExtended.d.ts +73 -0
- package/dist/custom/MJQueryEntityExtended.d.ts.map +1 -0
- package/dist/custom/MJQueryEntityExtended.js +195 -0
- package/dist/custom/MJQueryEntityExtended.js.map +1 -0
- package/dist/custom/ResourcePermissions/ResourcePermissionEngine.js +2 -2
- package/dist/custom/ResourcePermissions/ResourcePermissionEngine.js.map +1 -1
- package/dist/engines/QueryEngine.d.ts +28 -7
- package/dist/engines/QueryEngine.d.ts.map +1 -1
- package/dist/engines/QueryEngine.js +40 -1
- package/dist/engines/QueryEngine.js.map +1 -1
- package/dist/engines/UserInfoEngine.d.ts +9 -0
- package/dist/engines/UserInfoEngine.d.ts.map +1 -1
- package/dist/engines/UserInfoEngine.js +12 -0
- package/dist/engines/UserInfoEngine.js.map +1 -1
- package/dist/engines/conversations.d.ts +42 -2
- package/dist/engines/conversations.d.ts.map +1 -1
- package/dist/engines/conversations.js +49 -6
- package/dist/engines/conversations.js.map +1 -1
- package/dist/engines/interactive-forms.d.ts +132 -0
- package/dist/engines/interactive-forms.d.ts.map +1 -0
- package/dist/engines/interactive-forms.js +212 -0
- package/dist/engines/interactive-forms.js.map +1 -0
- package/dist/generated/entity_subclasses.d.ts +843 -104
- package/dist/generated/entity_subclasses.d.ts.map +1 -1
- package/dist/generated/entity_subclasses.js +1257 -138
- package/dist/generated/entity_subclasses.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
- package/readme.md +3 -3
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import { BaseEngine } from "@memberjunction/core";
|
|
2
|
+
import { NormalizeUUID, UUIDsEqual } from "@memberjunction/global";
|
|
3
|
+
/**
|
|
4
|
+
* Cache of MemberJunction interactive-form metadata: the form-role
|
|
5
|
+
* `MJ: Components` rows and their `MJ: Entity Form Overrides`. Designed
|
|
6
|
+
* to be the single source of truth for **both** the Form Studio
|
|
7
|
+
* authoring surface and the runtime form resolver.
|
|
8
|
+
*
|
|
9
|
+
* ## Why this engine exists
|
|
10
|
+
*
|
|
11
|
+
* Before this engine, three independent paths competed:
|
|
12
|
+
*
|
|
13
|
+
* 1. The cockpit's `loadExistingForms()` — RunView, no cache, manual
|
|
14
|
+
* reload after every mutation. Routinely raced BaseEntity events
|
|
15
|
+
* and produced "couldn't load form X" toasts after delete because
|
|
16
|
+
* the event handler's reload happened before the post-delete
|
|
17
|
+
* reload could land.
|
|
18
|
+
* 2. `FormResolverService` — RunView per render to find the right
|
|
19
|
+
* override for an (entity, user) pair. ~50ms cold latency on every
|
|
20
|
+
* Explorer form open.
|
|
21
|
+
* 3. `ComponentMetadataEngine` — caches library / registry catalog
|
|
22
|
+
* data but **deliberately omits** `MJ: Components` themselves
|
|
23
|
+
* because the full Component table includes ~150MB of `Specification`
|
|
24
|
+
* JSON across non-form types (Skip artifacts, dashboards, etc.).
|
|
25
|
+
*
|
|
26
|
+
* This engine threads the needle: load `Type='Form'` Components only
|
|
27
|
+
* (small dataset — a few dozen per typical deployment, ~5MB max) plus
|
|
28
|
+
* **all** `EntityFormOverride` rows (tiny). Specification is included
|
|
29
|
+
* because the cockpit + Skip rendering both need it. Loaded as
|
|
30
|
+
* `entity_object` so callers can call `.Save()` / `.Delete()` on the
|
|
31
|
+
* cached instances directly.
|
|
32
|
+
*
|
|
33
|
+
* ## Reactivity for free
|
|
34
|
+
*
|
|
35
|
+
* BaseEngine handles the reactive plumbing — there is **no manual
|
|
36
|
+
* invalidation code in this engine**:
|
|
37
|
+
*
|
|
38
|
+
* - The `Configs` array passed to `Load()` tells BaseEngine which
|
|
39
|
+
* entities to subscribe to via the global MJEventType.ComponentEvent
|
|
40
|
+
* bus. Save / delete / remote-invalidate events for `MJ: Components`
|
|
41
|
+
* or `MJ: Entity Form Overrides` automatically refresh the matching
|
|
42
|
+
* in-memory array (or apply an in-place mutation when possible).
|
|
43
|
+
* - Each property has a lazy `BehaviorSubject` exposed via
|
|
44
|
+
* `ObserveProperty(propertyName)`. Subscribers receive the current
|
|
45
|
+
* array immediately and re-receive it on every mutation.
|
|
46
|
+
* - Convenience getters `Forms$` and `Overrides$` wrap
|
|
47
|
+
* `ObserveProperty` for ergonomic Angular `async`-pipe consumption.
|
|
48
|
+
*
|
|
49
|
+
* ## Lazy load pattern
|
|
50
|
+
*
|
|
51
|
+
* Always called as `await InteractiveFormsEngine.Instance.Config(false)`
|
|
52
|
+
* before reading state. BaseEngine.Config is a no-op when already loaded
|
|
53
|
+
* (forceRefresh=false), so callers can sprinkle the call at every entry
|
|
54
|
+
* point without worrying about cost — first caller pays the load
|
|
55
|
+
* (~1 RunView per entity), everyone else gets cache hits. Users who
|
|
56
|
+
* never touch Form Studio pay nothing.
|
|
57
|
+
*
|
|
58
|
+
* ## Where to use it
|
|
59
|
+
*
|
|
60
|
+
* - Form Studio cockpit (`form-builder-resource.component.ts`): replace
|
|
61
|
+
* `loadExistingForms()` / `loadVersionsForActiveForm()` with
|
|
62
|
+
* subscriptions to `Forms$`. Mutations elsewhere (agent saves,
|
|
63
|
+
* other browser tabs via remote-invalidate, etc.) refresh the
|
|
64
|
+
* UI automatically.
|
|
65
|
+
* - `FormResolverService`: replace per-resolution RunView with
|
|
66
|
+
* `GetActiveOverrideForEntity()` in-memory lookup. Sub-ms instead
|
|
67
|
+
* of ~50ms.
|
|
68
|
+
* - Form Builder agent's deterministic Builder: no explicit
|
|
69
|
+
* refresh needed after `Create`/`Modify Interactive Form` — the
|
|
70
|
+
* BaseEntity events those actions raise drive the cache update.
|
|
71
|
+
*/
|
|
72
|
+
export class InteractiveFormsEngine extends BaseEngine {
|
|
73
|
+
constructor() {
|
|
74
|
+
super(...arguments);
|
|
75
|
+
this._forms = [];
|
|
76
|
+
this._overrides = [];
|
|
77
|
+
}
|
|
78
|
+
/** Standard singleton accessor — never construct directly. */
|
|
79
|
+
static get Instance() {
|
|
80
|
+
return super.getInstance();
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Lazy-load the form Component + override caches. Safe to call from
|
|
84
|
+
* every entry point — no-op if already loaded (unless `forceRefresh`).
|
|
85
|
+
*/
|
|
86
|
+
async Config(forceRefresh, contextUser, provider) {
|
|
87
|
+
const c = [
|
|
88
|
+
{
|
|
89
|
+
Type: 'entity',
|
|
90
|
+
EntityName: 'MJ: Components',
|
|
91
|
+
PropertyName: '_forms',
|
|
92
|
+
Filter: "Type='Form'",
|
|
93
|
+
CacheLocal: true,
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
Type: 'entity',
|
|
97
|
+
EntityName: 'MJ: Entity Form Overrides',
|
|
98
|
+
PropertyName: '_overrides',
|
|
99
|
+
CacheLocal: true,
|
|
100
|
+
},
|
|
101
|
+
];
|
|
102
|
+
await this.Load(c, provider, forceRefresh, contextUser);
|
|
103
|
+
}
|
|
104
|
+
// ─── Read-side accessors ────────────────────────────────────────────────
|
|
105
|
+
/** All cached form-role Components. */
|
|
106
|
+
get Forms() {
|
|
107
|
+
return this._forms ?? [];
|
|
108
|
+
}
|
|
109
|
+
/** All cached EntityFormOverride rows (all scopes — caller filters). */
|
|
110
|
+
get Overrides() {
|
|
111
|
+
return this._overrides ?? [];
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* RxJS Observable of the forms array. Emits the current array on
|
|
115
|
+
* subscribe (BehaviorSubject semantics) and re-emits on every save /
|
|
116
|
+
* delete / remote-invalidate that affects `MJ: Components`. Use this
|
|
117
|
+
* in Angular components for auto-refreshing UIs:
|
|
118
|
+
*
|
|
119
|
+
* ```ts
|
|
120
|
+
* forms$ = InteractiveFormsEngine.Instance.Forms$.pipe(
|
|
121
|
+
* map(forms => forms.filter(f => f.Type === 'Form'))
|
|
122
|
+
* );
|
|
123
|
+
* ```
|
|
124
|
+
*/
|
|
125
|
+
get Forms$() {
|
|
126
|
+
return this.ObserveProperty('_forms');
|
|
127
|
+
}
|
|
128
|
+
/** RxJS Observable of the overrides array — same reactivity contract as `Forms$`. */
|
|
129
|
+
get Overrides$() {
|
|
130
|
+
return this.ObserveProperty('_overrides');
|
|
131
|
+
}
|
|
132
|
+
// ─── Convenience queries ────────────────────────────────────────────────
|
|
133
|
+
/**
|
|
134
|
+
* Return the override rows for a specific user (User-scope only).
|
|
135
|
+
* Roles + Global overrides are filtered out — they're per-policy,
|
|
136
|
+
* not per-user. Use {@link GetActiveOverrideForEntity} when you need
|
|
137
|
+
* the resolver-style lookup that considers all scopes.
|
|
138
|
+
*/
|
|
139
|
+
GetUserOverrides(userID) {
|
|
140
|
+
if (!userID)
|
|
141
|
+
return [];
|
|
142
|
+
return this.Overrides.filter(o => o.Scope === 'User' && o.UserID && UUIDsEqual(o.UserID, userID));
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Resolver-style lookup matching the runtime form-resolver's scope
|
|
146
|
+
* priority: User > Role > Global. Returns the highest-priority
|
|
147
|
+
* Active override for the given (entity, user, roles) tuple, or null
|
|
148
|
+
* if none match. Within the same scope, lower `Priority` wins (the
|
|
149
|
+
* resolver convention — Priority is sort key, not boost).
|
|
150
|
+
*
|
|
151
|
+
* @param entityID The target `MJ: Entities.ID`.
|
|
152
|
+
* @param userID The current user's `MJ: Users.ID`.
|
|
153
|
+
* @param roleIDs The current user's role IDs (used to match Role-scope rows).
|
|
154
|
+
*/
|
|
155
|
+
GetActiveOverrideForEntity(entityID, userID, roleIDs) {
|
|
156
|
+
if (!entityID)
|
|
157
|
+
return null;
|
|
158
|
+
const candidates = this.Overrides.filter(o => o.EntityID && UUIDsEqual(o.EntityID, entityID)
|
|
159
|
+
&& o.Status === 'Active'
|
|
160
|
+
&& ((o.Scope === 'User' && o.UserID && userID && UUIDsEqual(o.UserID, userID)) ||
|
|
161
|
+
(o.Scope === 'Role' && o.RoleID && roleIDs.some(r => UUIDsEqual(r, o.RoleID))) ||
|
|
162
|
+
(o.Scope === 'Global')));
|
|
163
|
+
if (candidates.length === 0)
|
|
164
|
+
return null;
|
|
165
|
+
// Sort by scope priority then row priority (low first).
|
|
166
|
+
const scoreScope = (s) => {
|
|
167
|
+
if (s === 'User')
|
|
168
|
+
return 0;
|
|
169
|
+
if (s === 'Role')
|
|
170
|
+
return 1;
|
|
171
|
+
if (s === 'Global')
|
|
172
|
+
return 2;
|
|
173
|
+
return 3;
|
|
174
|
+
};
|
|
175
|
+
candidates.sort((a, b) => {
|
|
176
|
+
const sa = scoreScope(a.Scope);
|
|
177
|
+
const sb = scoreScope(b.Scope);
|
|
178
|
+
if (sa !== sb)
|
|
179
|
+
return sa - sb;
|
|
180
|
+
return (a.Priority ?? 0) - (b.Priority ?? 0);
|
|
181
|
+
});
|
|
182
|
+
return candidates[0];
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* All Components in a given Name lineage. The cockpit's version rail
|
|
186
|
+
* uses this — every version of "MembersDemo" has the same Component.Name,
|
|
187
|
+
* differentiated by Version + VersionSequence.
|
|
188
|
+
*/
|
|
189
|
+
GetLineageByName(name) {
|
|
190
|
+
if (!name)
|
|
191
|
+
return [];
|
|
192
|
+
const n = name.trim().toLowerCase();
|
|
193
|
+
return this.Forms
|
|
194
|
+
.filter(c => (c.Name ?? '').trim().toLowerCase() === n)
|
|
195
|
+
.sort((a, b) => (b.VersionSequence ?? 0) - (a.VersionSequence ?? 0));
|
|
196
|
+
}
|
|
197
|
+
/** Find a form by Component ID. O(N) — N is small (form count, not all Components). */
|
|
198
|
+
FindFormByID(id) {
|
|
199
|
+
if (!id)
|
|
200
|
+
return undefined;
|
|
201
|
+
const normID = NormalizeUUID(id);
|
|
202
|
+
return this.Forms.find(c => NormalizeUUID(c.ID) === normID);
|
|
203
|
+
}
|
|
204
|
+
/** Find an override row by its primary key. */
|
|
205
|
+
FindOverrideByID(id) {
|
|
206
|
+
if (!id)
|
|
207
|
+
return undefined;
|
|
208
|
+
const normID = NormalizeUUID(id);
|
|
209
|
+
return this.Overrides.find(o => NormalizeUUID(o.ID) === normID);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
//# sourceMappingURL=interactive-forms.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interactive-forms.js","sourceRoot":"","sources":["../../src/engines/interactive-forms.ts"],"names":[],"mappings":"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"}
|