@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.
Files changed (32) hide show
  1. package/dist/custom/MJQueryEntityExtended.d.ts +73 -0
  2. package/dist/custom/MJQueryEntityExtended.d.ts.map +1 -0
  3. package/dist/custom/MJQueryEntityExtended.js +195 -0
  4. package/dist/custom/MJQueryEntityExtended.js.map +1 -0
  5. package/dist/custom/ResourcePermissions/ResourcePermissionEngine.js +2 -2
  6. package/dist/custom/ResourcePermissions/ResourcePermissionEngine.js.map +1 -1
  7. package/dist/engines/QueryEngine.d.ts +28 -7
  8. package/dist/engines/QueryEngine.d.ts.map +1 -1
  9. package/dist/engines/QueryEngine.js +40 -1
  10. package/dist/engines/QueryEngine.js.map +1 -1
  11. package/dist/engines/UserInfoEngine.d.ts +9 -0
  12. package/dist/engines/UserInfoEngine.d.ts.map +1 -1
  13. package/dist/engines/UserInfoEngine.js +12 -0
  14. package/dist/engines/UserInfoEngine.js.map +1 -1
  15. package/dist/engines/conversations.d.ts +42 -2
  16. package/dist/engines/conversations.d.ts.map +1 -1
  17. package/dist/engines/conversations.js +49 -6
  18. package/dist/engines/conversations.js.map +1 -1
  19. package/dist/engines/interactive-forms.d.ts +132 -0
  20. package/dist/engines/interactive-forms.d.ts.map +1 -0
  21. package/dist/engines/interactive-forms.js +212 -0
  22. package/dist/engines/interactive-forms.js.map +1 -0
  23. package/dist/generated/entity_subclasses.d.ts +843 -104
  24. package/dist/generated/entity_subclasses.d.ts.map +1 -1
  25. package/dist/generated/entity_subclasses.js +1257 -138
  26. package/dist/generated/entity_subclasses.js.map +1 -1
  27. package/dist/index.d.ts +2 -0
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +2 -0
  30. package/dist/index.js.map +1 -1
  31. package/package.json +5 -5
  32. 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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"}