@memberjunction/global 4.0.0 → 4.2.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/README.md +634 -0
- package/package.json +1 -1
- package/dist/Core.d.ts +0 -29
- package/dist/Core.d.ts.map +0 -1
- package/dist/Core.js +0 -58
- package/dist/Core.js.map +0 -1
- package/dist/generic/QueryCache.d.ts +0 -85
- package/dist/generic/QueryCache.d.ts.map +0 -1
- package/dist/generic/QueryCache.js +0 -198
- package/dist/generic/QueryCache.js.map +0 -1
- package/dist/generic/QueryCacheConfig.d.ts +0 -72
- package/dist/generic/QueryCacheConfig.d.ts.map +0 -1
- package/dist/generic/QueryCacheConfig.js +0 -3
- package/dist/generic/QueryCacheConfig.js.map +0 -1
- package/dist/generic/applicationInfo.d.ts +0 -138
- package/dist/generic/applicationInfo.d.ts.map +0 -1
- package/dist/generic/applicationInfo.js +0 -177
- package/dist/generic/applicationInfo.js.map +0 -1
- package/dist/generic/authEvaluator.d.ts +0 -25
- package/dist/generic/authEvaluator.d.ts.map +0 -1
- package/dist/generic/authEvaluator.js +0 -49
- package/dist/generic/authEvaluator.js.map +0 -1
- package/dist/generic/authTypes.d.ts +0 -193
- package/dist/generic/authTypes.d.ts.map +0 -1
- package/dist/generic/authTypes.js +0 -19
- package/dist/generic/authTypes.js.map +0 -1
- package/dist/generic/baseEngine.d.ts +0 -260
- package/dist/generic/baseEngine.d.ts.map +0 -1
- package/dist/generic/baseEngine.js +0 -510
- package/dist/generic/baseEngine.js.map +0 -1
- package/dist/generic/baseEntity.d.ts +0 -691
- package/dist/generic/baseEntity.d.ts.map +0 -1
- package/dist/generic/baseEntity.js +0 -1688
- package/dist/generic/baseEntity.js.map +0 -1
- package/dist/generic/baseInfo.d.ts +0 -24
- package/dist/generic/baseInfo.d.ts.map +0 -1
- package/dist/generic/baseInfo.js +0 -53
- package/dist/generic/baseInfo.js.map +0 -1
- package/dist/generic/compositeKey.d.ts +0 -206
- package/dist/generic/compositeKey.d.ts.map +0 -1
- package/dist/generic/compositeKey.js +0 -412
- package/dist/generic/compositeKey.js.map +0 -1
- package/dist/generic/databaseProviderBase.d.ts +0 -46
- package/dist/generic/databaseProviderBase.d.ts.map +0 -1
- package/dist/generic/databaseProviderBase.js +0 -14
- package/dist/generic/databaseProviderBase.js.map +0 -1
- package/dist/generic/entityInfo.d.ts +0 -983
- package/dist/generic/entityInfo.d.ts.map +0 -1
- package/dist/generic/entityInfo.js +0 -1401
- package/dist/generic/entityInfo.js.map +0 -1
- package/dist/generic/explorerNavigationItem.d.ts +0 -20
- package/dist/generic/explorerNavigationItem.d.ts.map +0 -1
- package/dist/generic/explorerNavigationItem.js +0 -29
- package/dist/generic/explorerNavigationItem.js.map +0 -1
- package/dist/generic/interfaces.d.ts +0 -610
- package/dist/generic/interfaces.d.ts.map +0 -1
- package/dist/generic/interfaces.js +0 -211
- package/dist/generic/interfaces.js.map +0 -1
- package/dist/generic/libraryInfo.d.ts +0 -40
- package/dist/generic/libraryInfo.d.ts.map +0 -1
- package/dist/generic/libraryInfo.js +0 -56
- package/dist/generic/libraryInfo.js.map +0 -1
- package/dist/generic/logging.d.ts +0 -179
- package/dist/generic/logging.d.ts.map +0 -1
- package/dist/generic/logging.js +0 -382
- package/dist/generic/logging.js.map +0 -1
- package/dist/generic/metadata.d.ts +0 -305
- package/dist/generic/metadata.d.ts.map +0 -1
- package/dist/generic/metadata.js +0 -454
- package/dist/generic/metadata.js.map +0 -1
- package/dist/generic/metadataUtil.d.ts +0 -8
- package/dist/generic/metadataUtil.d.ts.map +0 -1
- package/dist/generic/metadataUtil.js +0 -36
- package/dist/generic/metadataUtil.js.map +0 -1
- package/dist/generic/providerBase.d.ts +0 -546
- package/dist/generic/providerBase.d.ts.map +0 -1
- package/dist/generic/providerBase.js +0 -999
- package/dist/generic/providerBase.js.map +0 -1
- package/dist/generic/queryInfo.d.ts +0 -460
- package/dist/generic/queryInfo.d.ts.map +0 -1
- package/dist/generic/queryInfo.js +0 -633
- package/dist/generic/queryInfo.js.map +0 -1
- package/dist/generic/querySQLFilters.d.ts +0 -54
- package/dist/generic/querySQLFilters.d.ts.map +0 -1
- package/dist/generic/querySQLFilters.js +0 -84
- package/dist/generic/querySQLFilters.js.map +0 -1
- package/dist/generic/runQuery.d.ts +0 -96
- package/dist/generic/runQuery.d.ts.map +0 -1
- package/dist/generic/runQuery.js +0 -66
- package/dist/generic/runQuery.js.map +0 -1
- package/dist/generic/runQuerySQLFilterImplementations.d.ts +0 -51
- package/dist/generic/runQuerySQLFilterImplementations.d.ts.map +0 -1
- package/dist/generic/runQuerySQLFilterImplementations.js +0 -238
- package/dist/generic/runQuerySQLFilterImplementations.js.map +0 -1
- package/dist/generic/runReport.d.ts +0 -25
- package/dist/generic/runReport.d.ts.map +0 -1
- package/dist/generic/runReport.js +0 -42
- package/dist/generic/runReport.js.map +0 -1
- package/dist/generic/securityInfo.d.ts +0 -355
- package/dist/generic/securityInfo.d.ts.map +0 -1
- package/dist/generic/securityInfo.js +0 -425
- package/dist/generic/securityInfo.js.map +0 -1
- package/dist/generic/transactionGroup.d.ts +0 -184
- package/dist/generic/transactionGroup.d.ts.map +0 -1
- package/dist/generic/transactionGroup.js +0 -357
- package/dist/generic/transactionGroup.js.map +0 -1
- package/dist/generic/util.d.ts +0 -81
- package/dist/generic/util.d.ts.map +0 -1
- package/dist/generic/util.js +0 -301
- package/dist/generic/util.js.map +0 -1
- package/dist/views/runView.d.ts +0 -150
- package/dist/views/runView.d.ts.map +0 -1
- package/dist/views/runView.js +0 -100
- package/dist/views/runView.js.map +0 -1
- package/dist/views/viewInfo.d.ts +0 -121
- package/dist/views/viewInfo.d.ts.map +0 -1
- package/dist/views/viewInfo.js +0 -182
- package/dist/views/viewInfo.js.map +0 -1
- package/readme.md +0 -1168
package/readme.md
DELETED
|
@@ -1,1168 +0,0 @@
|
|
|
1
|
-
# @memberjunction/global
|
|
2
|
-
|
|
3
|
-
Core global utilities and coordination library for MemberJunction applications. This package provides essential singleton management, class factory patterns, event coordination, and utility functions that are used throughout the MemberJunction ecosystem.
|
|
4
|
-
|
|
5
|
-
## Installation
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
npm install @memberjunction/global
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
## Overview
|
|
12
|
-
|
|
13
|
-
The `@memberjunction/global` library serves as the foundation for cross-component communication and coordination in MemberJunction applications. It provides:
|
|
14
|
-
|
|
15
|
-
- **Global Singleton Management** - Ensures true singleton instances across module boundaries
|
|
16
|
-
- **Class Factory System** - Dynamic class registration and instantiation with automatic root class detection
|
|
17
|
-
- **Event System** - RxJS-based event bus for component communication
|
|
18
|
-
- **Object Caching** - In-memory object cache for application lifetime
|
|
19
|
-
- **Class Reflection Utilities** - Runtime class hierarchy inspection and analysis
|
|
20
|
-
- **Deep Diff Engine** - Comprehensive object comparison and change tracking
|
|
21
|
-
- **JSON Validator** - Lightweight JSON validation with flexible rules and special syntax
|
|
22
|
-
- **Warning Manager** - Smart warning system with debouncing, batching, and session tracking
|
|
23
|
-
- **Utility Functions** - Common string manipulation, JSON parsing (including recursive nested JSON parsing), pattern matching, and formatting utilities
|
|
24
|
-
|
|
25
|
-
## Core Components
|
|
26
|
-
|
|
27
|
-
### MJGlobal Class
|
|
28
|
-
|
|
29
|
-
The central singleton class that coordinates events and manages components across your application.
|
|
30
|
-
|
|
31
|
-
```typescript
|
|
32
|
-
import { MJGlobal } from '@memberjunction/global';
|
|
33
|
-
|
|
34
|
-
// Get the singleton instance
|
|
35
|
-
const mjGlobal = MJGlobal.Instance;
|
|
36
|
-
|
|
37
|
-
// Register a component
|
|
38
|
-
mjGlobal.RegisterComponent(myComponent);
|
|
39
|
-
|
|
40
|
-
// Raise an event
|
|
41
|
-
mjGlobal.RaiseEvent({
|
|
42
|
-
component: myComponent,
|
|
43
|
-
event: MJEventType.ComponentEvent,
|
|
44
|
-
eventCode: 'CUSTOM_EVENT',
|
|
45
|
-
args: { data: 'example' }
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
// Listen for events
|
|
49
|
-
const subscription = mjGlobal.GetEventListener().subscribe(event => {
|
|
50
|
-
console.log('Event received:', event);
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
// Listen with replay (gets past events too)
|
|
54
|
-
const replaySubscription = mjGlobal.GetEventListener(true).subscribe(event => {
|
|
55
|
-
console.log('Event with replay:', event);
|
|
56
|
-
});
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
### Class Factory System
|
|
60
|
-
|
|
61
|
-
Register and instantiate classes dynamically with automatic root class detection and inheritance chain support.
|
|
62
|
-
|
|
63
|
-
```typescript
|
|
64
|
-
import { RegisterClass, MJGlobal } from '@memberjunction/global';
|
|
65
|
-
|
|
66
|
-
// Define a base class hierarchy
|
|
67
|
-
class BaseProcessor {
|
|
68
|
-
process(data: any): void {
|
|
69
|
-
console.log('Base processing');
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
class SpecialProcessor extends BaseProcessor {
|
|
74
|
-
process(data: any): void {
|
|
75
|
-
console.log('Special processing');
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// Register a subclass - automatically registers with root class (BaseProcessor)
|
|
80
|
-
@RegisterClass(SpecialProcessor, 'custom')
|
|
81
|
-
class CustomProcessor extends SpecialProcessor {
|
|
82
|
-
process(data: any): void {
|
|
83
|
-
console.log('Custom processing');
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// Create instances via the factory
|
|
88
|
-
const factory = MJGlobal.Instance.ClassFactory;
|
|
89
|
-
const processor = factory.CreateInstance<BaseProcessor>(BaseProcessor, 'custom');
|
|
90
|
-
processor.process(data); // Uses CustomProcessor
|
|
91
|
-
|
|
92
|
-
// Key Features:
|
|
93
|
-
// 1. Auto-registers with root class by default (BaseProcessor in this case)
|
|
94
|
-
// 2. Ensures proper priority ordering in inheritance chains
|
|
95
|
-
// 3. Can opt-out with autoRegisterWithRootClass: false
|
|
96
|
-
@RegisterClass(SpecialProcessor, 'special', 0, false, false) // Last param disables auto-root registration
|
|
97
|
-
class DirectRegistration extends SpecialProcessor {
|
|
98
|
-
// This registers directly to SpecialProcessor, not BaseProcessor
|
|
99
|
-
}
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
### Object Cache
|
|
103
|
-
|
|
104
|
-
In-memory caching system for application-lifetime object storage.
|
|
105
|
-
|
|
106
|
-
```typescript
|
|
107
|
-
const cache = MJGlobal.Instance.ObjectCache;
|
|
108
|
-
|
|
109
|
-
// Add an object to cache
|
|
110
|
-
cache.Add('user:123', { id: 123, name: 'John Doe' });
|
|
111
|
-
|
|
112
|
-
// Find an object
|
|
113
|
-
const user = cache.Find<User>('user:123');
|
|
114
|
-
|
|
115
|
-
// Replace an existing object
|
|
116
|
-
cache.Replace('user:123', { id: 123, name: 'Jane Doe' });
|
|
117
|
-
|
|
118
|
-
// Remove from cache
|
|
119
|
-
cache.Remove('user:123');
|
|
120
|
-
|
|
121
|
-
// Clear all cached objects
|
|
122
|
-
cache.Clear();
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
### BaseSingleton Class
|
|
126
|
-
|
|
127
|
-
Abstract base class for creating global singleton instances that persist across module boundaries.
|
|
128
|
-
|
|
129
|
-
```typescript
|
|
130
|
-
import { BaseSingleton } from '@memberjunction/global';
|
|
131
|
-
|
|
132
|
-
export class MyService extends BaseSingleton<MyService> {
|
|
133
|
-
private data: string[] = [];
|
|
134
|
-
|
|
135
|
-
public static get Instance(): MyService {
|
|
136
|
-
return super.getInstance<MyService>();
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
public addData(item: string): void {
|
|
140
|
-
this.data.push(item);
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// Usage anywhere in your app
|
|
145
|
-
const service = MyService.Instance;
|
|
146
|
-
service.addData('example');
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
### Class Reflection Utilities
|
|
150
|
-
|
|
151
|
-
Runtime utilities for inspecting and analyzing class hierarchies.
|
|
152
|
-
|
|
153
|
-
```typescript
|
|
154
|
-
import {
|
|
155
|
-
GetSuperclass,
|
|
156
|
-
GetRootClass,
|
|
157
|
-
IsSubclassOf,
|
|
158
|
-
IsRootClass,
|
|
159
|
-
IsDescendantClassOf,
|
|
160
|
-
GetClassInheritance,
|
|
161
|
-
GetFullClassHierarchy,
|
|
162
|
-
IsClassConstructor,
|
|
163
|
-
GetClassName
|
|
164
|
-
} from '@memberjunction/global';
|
|
165
|
-
|
|
166
|
-
// Example class hierarchy
|
|
167
|
-
class Animal {}
|
|
168
|
-
class Mammal extends Animal {}
|
|
169
|
-
class Dog extends Mammal {}
|
|
170
|
-
class GoldenRetriever extends Dog {}
|
|
171
|
-
|
|
172
|
-
// Get immediate superclass
|
|
173
|
-
const parent = GetSuperclass(GoldenRetriever); // Returns: Dog
|
|
174
|
-
|
|
175
|
-
// Get root class of hierarchy
|
|
176
|
-
const root = GetRootClass(GoldenRetriever); // Returns: Animal
|
|
177
|
-
|
|
178
|
-
// Check inheritance relationships
|
|
179
|
-
IsSubclassOf(GoldenRetriever, Animal); // true (checks entire chain)
|
|
180
|
-
IsDescendantClassOf(GoldenRetriever, Animal); // true (alias for IsSubclassOf)
|
|
181
|
-
IsRootClass(Animal); // true
|
|
182
|
-
IsRootClass(Dog); // false
|
|
183
|
-
|
|
184
|
-
// Get inheritance chain
|
|
185
|
-
const chain = GetClassInheritance(GoldenRetriever);
|
|
186
|
-
// Returns: [
|
|
187
|
-
// { name: 'Dog', reference: Dog },
|
|
188
|
-
// { name: 'Mammal', reference: Mammal },
|
|
189
|
-
// { name: 'Animal', reference: Animal }
|
|
190
|
-
// ]
|
|
191
|
-
|
|
192
|
-
// Get full hierarchy including the class itself
|
|
193
|
-
const fullChain = GetFullClassHierarchy(GoldenRetriever);
|
|
194
|
-
// Returns: [
|
|
195
|
-
// { name: 'GoldenRetriever', reference: GoldenRetriever },
|
|
196
|
-
// { name: 'Dog', reference: Dog },
|
|
197
|
-
// { name: 'Mammal', reference: Mammal },
|
|
198
|
-
// { name: 'Animal', reference: Animal }
|
|
199
|
-
// ]
|
|
200
|
-
|
|
201
|
-
// Utility functions
|
|
202
|
-
IsClassConstructor(Dog); // true
|
|
203
|
-
IsClassConstructor(() => {}); // false
|
|
204
|
-
GetClassName(GoldenRetriever); // "GoldenRetriever"
|
|
205
|
-
```
|
|
206
|
-
|
|
207
|
-
### Deep Diff Engine
|
|
208
|
-
|
|
209
|
-
Comprehensive object comparison and change tracking with hierarchical diff visualization.
|
|
210
|
-
|
|
211
|
-
```typescript
|
|
212
|
-
import { DeepDiffer, DiffChangeType } from '@memberjunction/global';
|
|
213
|
-
|
|
214
|
-
// Create a differ instance
|
|
215
|
-
const differ = new DeepDiffer({
|
|
216
|
-
includeUnchanged: false, // Don't track unchanged values
|
|
217
|
-
maxDepth: 10, // Maximum recursion depth
|
|
218
|
-
maxStringLength: 100, // Truncate long strings
|
|
219
|
-
treatNullAsUndefined: false // Treat null and undefined as distinct (default: false)
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
// Compare two objects
|
|
223
|
-
const oldData = {
|
|
224
|
-
user: { name: 'John', age: 30, role: 'admin' },
|
|
225
|
-
settings: { theme: 'dark', notifications: true },
|
|
226
|
-
tags: ['important', 'active']
|
|
227
|
-
};
|
|
228
|
-
|
|
229
|
-
const newData = {
|
|
230
|
-
user: { name: 'John', age: 31, role: 'superadmin' },
|
|
231
|
-
settings: { theme: 'light', notifications: true, language: 'en' },
|
|
232
|
-
tags: ['important', 'active', 'premium']
|
|
233
|
-
};
|
|
234
|
-
|
|
235
|
-
// Get the diff
|
|
236
|
-
const result = differ.diff(oldData, newData);
|
|
237
|
-
|
|
238
|
-
// Access summary
|
|
239
|
-
console.log(result.summary);
|
|
240
|
-
// { added: 2, removed: 0, modified: 3, unchanged: 3, total: 8 }
|
|
241
|
-
|
|
242
|
-
// Iterate through changes
|
|
243
|
-
result.changes.forEach(change => {
|
|
244
|
-
console.log(`${change.path}: ${change.type} - ${change.description}`);
|
|
245
|
-
});
|
|
246
|
-
// Output:
|
|
247
|
-
// user.age: Modified - Changed from 30 to 31
|
|
248
|
-
// user.role: Modified - Changed from "admin" to "superadmin"
|
|
249
|
-
// settings.theme: Modified - Changed from "dark" to "light"
|
|
250
|
-
// settings.language: Added - Value: "en"
|
|
251
|
-
// tags[2]: Added - Value: "premium"
|
|
252
|
-
|
|
253
|
-
// Filter changes by type
|
|
254
|
-
const additions = result.changes.filter(c => c.type === DiffChangeType.Added);
|
|
255
|
-
const modifications = result.changes.filter(c => c.type === DiffChangeType.Modified);
|
|
256
|
-
|
|
257
|
-
// Update configuration on the fly
|
|
258
|
-
differ.updateConfig({ includeUnchanged: true });
|
|
259
|
-
```
|
|
260
|
-
|
|
261
|
-
#### Treating null as undefined
|
|
262
|
-
|
|
263
|
-
When working with APIs or databases where `null` and `undefined` are used interchangeably, you can enable the `treatNullAsUndefined` option:
|
|
264
|
-
|
|
265
|
-
```typescript
|
|
266
|
-
const differ = new DeepDiffer({ treatNullAsUndefined: true });
|
|
267
|
-
|
|
268
|
-
const oldData = {
|
|
269
|
-
name: null,
|
|
270
|
-
status: 'active',
|
|
271
|
-
oldProp: 'value'
|
|
272
|
-
};
|
|
273
|
-
|
|
274
|
-
const newData = {
|
|
275
|
-
name: 'John', // Will show as "Added" instead of "Modified"
|
|
276
|
-
status: null, // Will show as "Removed" instead of "Modified"
|
|
277
|
-
newProp: 'value'
|
|
278
|
-
};
|
|
279
|
-
|
|
280
|
-
const result = differ.diff(oldData, newData);
|
|
281
|
-
// With treatNullAsUndefined: true
|
|
282
|
-
// - name: Added (not Modified, since null is treated as non-existent)
|
|
283
|
-
// - status: Removed (not Modified, since null is treated as non-existent)
|
|
284
|
-
// - oldProp: Removed
|
|
285
|
-
// - newProp: Added
|
|
286
|
-
```
|
|
287
|
-
|
|
288
|
-
### JSON Validator
|
|
289
|
-
|
|
290
|
-
Lightweight JSON validation with flexible validation rules and special field suffixes.
|
|
291
|
-
|
|
292
|
-
```typescript
|
|
293
|
-
import { JSONValidator } from '@memberjunction/global';
|
|
294
|
-
|
|
295
|
-
// Create validator instance
|
|
296
|
-
const validator = new JSONValidator();
|
|
297
|
-
|
|
298
|
-
// Define validation template with rules
|
|
299
|
-
const template = {
|
|
300
|
-
name: "John Doe", // Required field
|
|
301
|
-
email?: "user@example.com", // Optional field
|
|
302
|
-
settings*: {}, // Required, any content allowed
|
|
303
|
-
tags:[1+]: ["tag1"], // Array with at least 1 item
|
|
304
|
-
age:number: 25, // Must be a number
|
|
305
|
-
username:string:!empty: "johndoe", // Non-empty string
|
|
306
|
-
items:array:[2-5]?: ["A", "B"] // Optional array with 2-5 items
|
|
307
|
-
};
|
|
308
|
-
|
|
309
|
-
// Validate data against template
|
|
310
|
-
const data = {
|
|
311
|
-
name: "Jane Smith",
|
|
312
|
-
tags: ["work", "urgent"],
|
|
313
|
-
age: 30,
|
|
314
|
-
username: "jsmith"
|
|
315
|
-
};
|
|
316
|
-
|
|
317
|
-
const result = validator.validate(data, template);
|
|
318
|
-
if (result.Success) {
|
|
319
|
-
console.log('Validation passed!');
|
|
320
|
-
} else {
|
|
321
|
-
result.Errors.forEach(error => {
|
|
322
|
-
console.log(`${error.Source}: ${error.Message}`);
|
|
323
|
-
});
|
|
324
|
-
}
|
|
325
|
-
```
|
|
326
|
-
|
|
327
|
-
#### Validation Syntax
|
|
328
|
-
|
|
329
|
-
**Field Suffixes:**
|
|
330
|
-
- `?` - Optional field (e.g., `email?`)
|
|
331
|
-
- `*` - Required field with any content/structure (e.g., `payload*`)
|
|
332
|
-
|
|
333
|
-
**Validation Rules (using `:` delimiter):**
|
|
334
|
-
- **Array Length:**
|
|
335
|
-
- `[N+]` - At least N elements (e.g., `tags:[1+]`)
|
|
336
|
-
- `[N-M]` - Between N and M elements (e.g., `items:[2-5]`)
|
|
337
|
-
- `[=N]` - Exactly N elements (e.g., `coordinates:[=2]`)
|
|
338
|
-
|
|
339
|
-
- **Type Checking:**
|
|
340
|
-
- `string` - Must be a string
|
|
341
|
-
- `number` - Must be a number (NaN fails validation)
|
|
342
|
-
- `boolean` - Must be a boolean
|
|
343
|
-
- `object` - Must be an object (not array or null)
|
|
344
|
-
- `array` - Must be an array
|
|
345
|
-
|
|
346
|
-
- **Value Constraints:**
|
|
347
|
-
- `!empty` - Non-empty string, array, or object
|
|
348
|
-
|
|
349
|
-
**Combining Rules:**
|
|
350
|
-
Multiple validation rules can be combined with `:` delimiter:
|
|
351
|
-
```typescript
|
|
352
|
-
{
|
|
353
|
-
// Array of strings with 2+ items
|
|
354
|
-
"tags:array:[2+]": ["important", "urgent"],
|
|
355
|
-
|
|
356
|
-
// Non-empty string
|
|
357
|
-
"username:string:!empty": "johndoe",
|
|
358
|
-
|
|
359
|
-
// Optional number
|
|
360
|
-
"score:number?": 85,
|
|
361
|
-
|
|
362
|
-
// Optional array with 1-3 items
|
|
363
|
-
"options:array:[1-3]?": ["A", "B"]
|
|
364
|
-
}
|
|
365
|
-
```
|
|
366
|
-
|
|
367
|
-
#### Nested Object Validation
|
|
368
|
-
|
|
369
|
-
The validator recursively validates nested objects:
|
|
370
|
-
|
|
371
|
-
```typescript
|
|
372
|
-
const template = {
|
|
373
|
-
user: {
|
|
374
|
-
id:number: 123,
|
|
375
|
-
name:string:!empty: "John",
|
|
376
|
-
roles:array:[1+]: ["admin"]
|
|
377
|
-
},
|
|
378
|
-
settings?: {
|
|
379
|
-
theme: "dark",
|
|
380
|
-
notifications:boolean: true
|
|
381
|
-
}
|
|
382
|
-
};
|
|
383
|
-
```
|
|
384
|
-
|
|
385
|
-
#### Cleaning Validation Syntax
|
|
386
|
-
|
|
387
|
-
The validator can clean validation syntax from JSON objects that may have been returned by AI systems:
|
|
388
|
-
|
|
389
|
-
```typescript
|
|
390
|
-
// AI might return JSON with validation syntax in keys
|
|
391
|
-
const aiResponse = {
|
|
392
|
-
"name?": "John Doe",
|
|
393
|
-
"email:string": "john@example.com",
|
|
394
|
-
"tags:[2+]": ["work", "urgent"],
|
|
395
|
-
"settings*": { theme: "dark" },
|
|
396
|
-
"score:number:!empty": 85
|
|
397
|
-
};
|
|
398
|
-
|
|
399
|
-
// Clean the validation syntax
|
|
400
|
-
const cleaned = validator.cleanValidationSyntax<any>(aiResponse);
|
|
401
|
-
// Returns:
|
|
402
|
-
// {
|
|
403
|
-
// "name": "John Doe",
|
|
404
|
-
// "email": "john@example.com",
|
|
405
|
-
// "tags": ["work", "urgent"],
|
|
406
|
-
// "settings": { theme: "dark" },
|
|
407
|
-
// "score": 85
|
|
408
|
-
// }
|
|
409
|
-
```
|
|
410
|
-
|
|
411
|
-
The `cleanValidationSyntax` method:
|
|
412
|
-
- Recursively processes all object keys
|
|
413
|
-
- Removes validation suffixes (`?`, `*`)
|
|
414
|
-
- Removes validation rules (`:type`, `:[N+]`, `:!empty`, etc.)
|
|
415
|
-
- Preserves the original values unchanged
|
|
416
|
-
- Handles nested objects and arrays
|
|
417
|
-
- Returns a new object with cleaned keys
|
|
418
|
-
|
|
419
|
-
#### Convenience Methods
|
|
420
|
-
|
|
421
|
-
```typescript
|
|
422
|
-
// Validate against JSON string
|
|
423
|
-
const schemaJson = '{"name": "string", "age:number": 0}';
|
|
424
|
-
const result = validator.validateAgainstSchema(data, schemaJson);
|
|
425
|
-
|
|
426
|
-
// Integration with MemberJunction ValidationResult
|
|
427
|
-
// Returns standard ValidationResult with ValidationErrorInfo[]
|
|
428
|
-
// Compatible with existing MJ validation patterns
|
|
429
|
-
```
|
|
430
|
-
|
|
431
|
-
#### Use Cases
|
|
432
|
-
|
|
433
|
-
1. **API Response Validation:**
|
|
434
|
-
```typescript
|
|
435
|
-
const apiResponseTemplate = {
|
|
436
|
-
status:string: "success",
|
|
437
|
-
data*: {}, // Any data structure allowed
|
|
438
|
-
errors:array?: []
|
|
439
|
-
};
|
|
440
|
-
```
|
|
441
|
-
|
|
442
|
-
2. **Configuration Validation:**
|
|
443
|
-
```typescript
|
|
444
|
-
const configTemplate = {
|
|
445
|
-
apiUrl:string:!empty: "https://api.example.com",
|
|
446
|
-
timeout:number: 5000,
|
|
447
|
-
retries:number: 3,
|
|
448
|
-
features:array:[1+]: ["logging"]
|
|
449
|
-
};
|
|
450
|
-
```
|
|
451
|
-
|
|
452
|
-
3. **Form Data Validation:**
|
|
453
|
-
```typescript
|
|
454
|
-
const formTemplate = {
|
|
455
|
-
username:string:!empty: "user",
|
|
456
|
-
email:string: "user@example.com",
|
|
457
|
-
age:number?: 25,
|
|
458
|
-
preferences:object?: {
|
|
459
|
-
notifications:boolean: true
|
|
460
|
-
}
|
|
461
|
-
};
|
|
462
|
-
```
|
|
463
|
-
|
|
464
|
-
## Event Types
|
|
465
|
-
|
|
466
|
-
The library provides predefined event types for common scenarios:
|
|
467
|
-
|
|
468
|
-
```typescript
|
|
469
|
-
export const MJEventType = {
|
|
470
|
-
ComponentRegistered: 'ComponentRegistered',
|
|
471
|
-
ComponentUnregistered: 'ComponentUnregistered',
|
|
472
|
-
ComponentEvent: 'ComponentEvent',
|
|
473
|
-
LoggedIn: 'LoggedIn',
|
|
474
|
-
LoggedOut: 'LoggedOut',
|
|
475
|
-
LoginFailed: 'LoginFailed',
|
|
476
|
-
LogoutFailed: 'LogoutFailed',
|
|
477
|
-
ManualResizeRequest: 'ManualResizeRequest',
|
|
478
|
-
DisplaySimpleNotificationRequest: 'DisplaySimpleNotificationRequest',
|
|
479
|
-
} as const;
|
|
480
|
-
```
|
|
481
|
-
|
|
482
|
-
## Utility Functions
|
|
483
|
-
|
|
484
|
-
### String Manipulation
|
|
485
|
-
|
|
486
|
-
```typescript
|
|
487
|
-
import {
|
|
488
|
-
convertCamelCaseToHaveSpaces,
|
|
489
|
-
stripWhitespace,
|
|
490
|
-
generatePluralName,
|
|
491
|
-
adjustCasing,
|
|
492
|
-
stripTrailingChars,
|
|
493
|
-
replaceAllSpaces
|
|
494
|
-
} from '@memberjunction/global';
|
|
495
|
-
|
|
496
|
-
// Convert camel case to spaces
|
|
497
|
-
convertCamelCaseToHaveSpaces('AIAgentLearningCycle'); // "AI Agent Learning Cycle"
|
|
498
|
-
|
|
499
|
-
// Remove all whitespace
|
|
500
|
-
stripWhitespace(' Hello World '); // "HelloWorld"
|
|
501
|
-
|
|
502
|
-
// Generate plural forms
|
|
503
|
-
generatePluralName('child'); // "children"
|
|
504
|
-
generatePluralName('box'); // "boxes"
|
|
505
|
-
generatePluralName('party'); // "parties"
|
|
506
|
-
|
|
507
|
-
// Adjust casing
|
|
508
|
-
adjustCasing('hello', { capitalizeFirstLetterOnly: true }); // "Hello"
|
|
509
|
-
adjustCasing('world', { capitalizeEntireWord: true }); // "WORLD"
|
|
510
|
-
|
|
511
|
-
// Strip trailing characters
|
|
512
|
-
stripTrailingChars('example.txt', '.txt', false); // "example"
|
|
513
|
-
|
|
514
|
-
// Remove all spaces
|
|
515
|
-
replaceAllSpaces('Hello World'); // "HelloWorld"
|
|
516
|
-
```
|
|
517
|
-
|
|
518
|
-
### JSON Utilities
|
|
519
|
-
|
|
520
|
-
```typescript
|
|
521
|
-
import { CleanJSON, SafeJSONParse, ParseJSONRecursive } from '@memberjunction/global';
|
|
522
|
-
|
|
523
|
-
// Safe JSON parsing with error handling
|
|
524
|
-
const parsed = SafeJSONParse<MyType>('{"key": "value"}', true);
|
|
525
|
-
|
|
526
|
-
// Recursively parse JSON strings within objects
|
|
527
|
-
const input = {
|
|
528
|
-
data: '{"nested": "{\\"deeply\\": \\"nested\\"}"}',
|
|
529
|
-
messages: '[{"content": "{\\"type\\": \\"greeting\\", \\"text\\": \\"Hello\\"}"}]'
|
|
530
|
-
};
|
|
531
|
-
const output = ParseJSONRecursive(input);
|
|
532
|
-
// Returns: {
|
|
533
|
-
// data: { nested: { deeply: "nested" } },
|
|
534
|
-
// messages: [{ content: { type: "greeting", text: "Hello" } }]
|
|
535
|
-
// }
|
|
536
|
-
|
|
537
|
-
// Extract inline JSON from text strings
|
|
538
|
-
const textWithJson = {
|
|
539
|
-
result: 'Action completed: {"status": "success", "count": 42}'
|
|
540
|
-
};
|
|
541
|
-
const extracted = ParseJSONRecursive(textWithJson, { extractInlineJson: true });
|
|
542
|
-
// Returns: {
|
|
543
|
-
// result: "Action completed:",
|
|
544
|
-
// result_: { status: "success", count: 42 }
|
|
545
|
-
// }
|
|
546
|
-
|
|
547
|
-
// Control recursion depth and enable debugging
|
|
548
|
-
const deeplyNested = ParseJSONRecursive(complexData, {
|
|
549
|
-
maxDepth: 50, // Default: 100
|
|
550
|
-
extractInlineJson: true, // Default: false
|
|
551
|
-
debug: true // Default: false, logs parsing steps
|
|
552
|
-
});
|
|
553
|
-
```
|
|
554
|
-
|
|
555
|
-
#### CleanJSON Function
|
|
556
|
-
|
|
557
|
-
The `CleanJSON` function intelligently extracts and cleans JSON from various input formats, including double-escaped strings, strings with embedded JSON, and markdown code blocks. It's particularly useful when dealing with AI-generated responses or data from external systems that may have inconsistent JSON formatting.
|
|
558
|
-
|
|
559
|
-
**Processing Order:**
|
|
560
|
-
1. First attempts to parse the input as valid JSON (preserving embedded content)
|
|
561
|
-
2. If that fails, handles double-escaped characters (`\\n`, `\\"`, etc.)
|
|
562
|
-
3. Only extracts from markdown blocks or inline JSON as a last resort
|
|
563
|
-
|
|
564
|
-
```typescript
|
|
565
|
-
import { CleanJSON } from '@memberjunction/global';
|
|
566
|
-
|
|
567
|
-
// Example 1: Already valid JSON - returns formatted
|
|
568
|
-
const valid = CleanJSON('{"name": "test", "value": 123}');
|
|
569
|
-
// Returns:
|
|
570
|
-
// {
|
|
571
|
-
// "name": "test",
|
|
572
|
-
// "value": 123
|
|
573
|
-
// }
|
|
574
|
-
|
|
575
|
-
// Example 2: Double-escaped JSON string
|
|
576
|
-
const escaped = CleanJSON('{\\"name\\": \\"test\\", \\"value\\": 123}');
|
|
577
|
-
// Returns:
|
|
578
|
-
// {
|
|
579
|
-
// "name": "test",
|
|
580
|
-
// "value": 123
|
|
581
|
-
// }
|
|
582
|
-
|
|
583
|
-
// Example 3: JSON with escaped newlines (common from AI responses)
|
|
584
|
-
const withNewlines = CleanJSON('\\n{\\"mode\\": \\"test\\",\\n\\"data\\": [1, 2, 3]}\\n');
|
|
585
|
-
// Returns:
|
|
586
|
-
// {
|
|
587
|
-
// "mode": "test",
|
|
588
|
-
// "data": [1, 2, 3]
|
|
589
|
-
// }
|
|
590
|
-
|
|
591
|
-
// Example 4: Complex JSON with embedded markdown (preserves the markdown)
|
|
592
|
-
const complexJson = CleanJSON(`{
|
|
593
|
-
"taskComplete": false,
|
|
594
|
-
"message": "Processing complete",
|
|
595
|
-
"nextAction": {
|
|
596
|
-
"type": "design",
|
|
597
|
-
"payload": {
|
|
598
|
-
"outputFormat": "\`\`\`json\\n{\\"componentName\\": \\"Example\\"}\\n\`\`\`"
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
|
-
}`);
|
|
602
|
-
// Returns the JSON with the markdown code block preserved in the outputFormat field
|
|
603
|
-
|
|
604
|
-
// Example 5: Extract JSON from markdown (only when input isn't valid JSON)
|
|
605
|
-
const markdown = CleanJSON('Some text ```json\n{"extracted": true}\n``` more text');
|
|
606
|
-
// Returns:
|
|
607
|
-
// {
|
|
608
|
-
// "extracted": true
|
|
609
|
-
// }
|
|
610
|
-
|
|
611
|
-
// Example 6: Extract inline JSON from mixed text
|
|
612
|
-
const mixed = CleanJSON('Response: {"status": "success", "code": 200} - Done');
|
|
613
|
-
// Returns:
|
|
614
|
-
// {
|
|
615
|
-
// "status": "success",
|
|
616
|
-
// "code": 200
|
|
617
|
-
// }
|
|
618
|
-
|
|
619
|
-
// Example 7: Complex real-world example with nested escaped JSON
|
|
620
|
-
const aiResponse = CleanJSON(`{
|
|
621
|
-
"analysis": "Complete",
|
|
622
|
-
"data": "{\\"users\\": [{\\"name\\": \\"John\\", \\"active\\": true}]}",
|
|
623
|
-
"metadata": {
|
|
624
|
-
"template": "\`\`\`json\\n{\\"format\\": \\"standard\\"}\\n\`\`\`"
|
|
625
|
-
}
|
|
626
|
-
}`);
|
|
627
|
-
// Returns properly formatted JSON with all nested structures intact
|
|
628
|
-
```
|
|
629
|
-
|
|
630
|
-
**Key Features:**
|
|
631
|
-
- **Preserves embedded content**: When the input is valid JSON, markdown blocks and escaped strings within values are preserved
|
|
632
|
-
- **Smart unescaping**: Handles `\\n` → `\n`, `\\"` → `"`, `\\\\` → `\` and other common escape sequences
|
|
633
|
-
- **Markdown extraction**: Extracts JSON from ` ```json ` code blocks when needed
|
|
634
|
-
- **Inline extraction**: Finds JSON objects/arrays within surrounding text
|
|
635
|
-
- **Null safety**: Returns `null` for invalid inputs instead of throwing errors
|
|
636
|
-
|
|
637
|
-
**Common Use Cases:**
|
|
638
|
-
- Processing AI model responses that may contain escaped JSON
|
|
639
|
-
- Cleaning data from external APIs with inconsistent formatting
|
|
640
|
-
- Extracting JSON from log files or debug output
|
|
641
|
-
- Handling JSON strings stored in databases that may be double-escaped
|
|
642
|
-
- Processing user input that may contain JSON in various formats
|
|
643
|
-
|
|
644
|
-
### HTML Conversion
|
|
645
|
-
|
|
646
|
-
```typescript
|
|
647
|
-
import { ConvertMarkdownStringToHtmlList } from '@memberjunction/global';
|
|
648
|
-
|
|
649
|
-
// Convert markdown to HTML list
|
|
650
|
-
const html = ConvertMarkdownStringToHtmlList('Unordered', '- Item 1\n- Item 2\n- Item 3');
|
|
651
|
-
// Returns: <ul><li>Item 1</li><li>Item 2</li><li>Item 3</li></ul>
|
|
652
|
-
```
|
|
653
|
-
|
|
654
|
-
### Warning Manager
|
|
655
|
-
|
|
656
|
-
The Warning Manager provides intelligent warning batching and deduplication for console messages across your application. It tracks warnings per session, groups them by type, and displays them in clean, formatted output after a configurable debounce period.
|
|
657
|
-
|
|
658
|
-
#### Features
|
|
659
|
-
|
|
660
|
-
- **Session-level tracking** - Each warning shown only once per session
|
|
661
|
-
- **Debounced output** - Groups warnings and displays after a configurable quiet period (default 10s)
|
|
662
|
-
- **Multiple warning types** - Supports deprecation warnings and data integrity warnings
|
|
663
|
-
- **Beautiful formatting** - Tree-structured console output with emojis and clear grouping
|
|
664
|
-
- **Configurable** - Runtime API and environment variables for customization
|
|
665
|
-
- **Backward compatible** - `DeprecationWarningManager` alias provided
|
|
666
|
-
|
|
667
|
-
#### Basic Usage
|
|
668
|
-
|
|
669
|
-
```typescript
|
|
670
|
-
import { WarningManager } from '@memberjunction/global';
|
|
671
|
-
|
|
672
|
-
const wm = WarningManager.Instance;
|
|
673
|
-
|
|
674
|
-
// Record deprecation warnings
|
|
675
|
-
wm.RecordEntityDeprecationWarning('LegacyEntity', 'MyComponent::method');
|
|
676
|
-
wm.RecordFieldDeprecationWarning('Users', 'OldField', 'DataLoader::process');
|
|
677
|
-
|
|
678
|
-
// Record field-not-found warnings (data integrity issues)
|
|
679
|
-
wm.RecordFieldNotFoundWarning('Users', 'DeletedColumn', 'BaseEntity::SetMany during import');
|
|
680
|
-
|
|
681
|
-
// Warnings are automatically batched and displayed after debounce period
|
|
682
|
-
// Or manually flush immediately:
|
|
683
|
-
wm.FlushWarnings();
|
|
684
|
-
```
|
|
685
|
-
|
|
686
|
-
#### Example Output
|
|
687
|
-
|
|
688
|
-
```
|
|
689
|
-
⚠️ DEPRECATION WARNINGS - The following entities/fields are deprecated and may be removed in future versions:
|
|
690
|
-
|
|
691
|
-
📦 DEPRECATED ENTITIES:
|
|
692
|
-
• "LegacyEntity" (called from: MyComponent::method)
|
|
693
|
-
|
|
694
|
-
📋 DEPRECATED ENTITY FIELDS:
|
|
695
|
-
└─ "Users"
|
|
696
|
-
└─ OldField (called from: DataLoader::process)
|
|
697
|
-
|
|
698
|
-
💡 Set ShowAll=true in configuration to see every occurrence.
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
⚠️ DATA INTEGRITY WARNINGS - The following fields were not found in entity definitions:
|
|
702
|
-
|
|
703
|
-
📋 MISSING FIELDS:
|
|
704
|
-
└─ "Users"
|
|
705
|
-
└─ DeletedColumn (context: BaseEntity::SetMany during import)
|
|
706
|
-
|
|
707
|
-
💡 These fields exist in your data but not in the entity schema. This may indicate:
|
|
708
|
-
• Schema is out of sync with database
|
|
709
|
-
• Data contains legacy fields that were removed
|
|
710
|
-
• Field names have been changed
|
|
711
|
-
```
|
|
712
|
-
|
|
713
|
-
#### Configuration
|
|
714
|
-
|
|
715
|
-
Configure via runtime API:
|
|
716
|
-
|
|
717
|
-
```typescript
|
|
718
|
-
wm.UpdateConfig({
|
|
719
|
-
DebounceMs: 5000, // Wait 5 seconds after last warning
|
|
720
|
-
ShowAll: false, // Show each warning once per session (default)
|
|
721
|
-
DisableWarnings: false, // Enable warnings (default)
|
|
722
|
-
GroupWarnings: true // Group warnings in tree format (default)
|
|
723
|
-
});
|
|
724
|
-
|
|
725
|
-
// Get current configuration
|
|
726
|
-
const config = wm.GetConfig();
|
|
727
|
-
```
|
|
728
|
-
|
|
729
|
-
All configuration is done via the runtime API.
|
|
730
|
-
|
|
731
|
-
#### Warning Types
|
|
732
|
-
|
|
733
|
-
**Deprecation Warnings** indicate entities or fields that may be removed in future versions:
|
|
734
|
-
- `RecordEntityDeprecationWarning(entityName, callerName)` - Deprecated entity
|
|
735
|
-
- `RecordFieldDeprecationWarning(entityName, fieldName, callerName)` - Deprecated field
|
|
736
|
-
|
|
737
|
-
**Data Integrity Warnings** indicate mismatches between data and schema:
|
|
738
|
-
- `RecordFieldNotFoundWarning(entityName, fieldName, context)` - Field exists in data but not in schema
|
|
739
|
-
|
|
740
|
-
#### Advanced Usage
|
|
741
|
-
|
|
742
|
-
```typescript
|
|
743
|
-
// Reset all tracking (useful for testing)
|
|
744
|
-
wm.Reset();
|
|
745
|
-
|
|
746
|
-
// Backward compatibility - DeprecationWarningManager is an alias
|
|
747
|
-
import { DeprecationWarningManager } from '@memberjunction/global';
|
|
748
|
-
const dwm = DeprecationWarningManager.Instance; // Same as WarningManager.Instance
|
|
749
|
-
```
|
|
750
|
-
|
|
751
|
-
#### Use Cases
|
|
752
|
-
|
|
753
|
-
1. **Framework-level warnings** - Alert developers about deprecated APIs
|
|
754
|
-
2. **Data migration** - Track fields that don't match current schema
|
|
755
|
-
3. **Development debugging** - Identify outdated code patterns
|
|
756
|
-
4. **Testing** - Verify no deprecated features are used
|
|
757
|
-
|
|
758
|
-
### Safe Expression Evaluator
|
|
759
|
-
|
|
760
|
-
Secure boolean expression evaluation for conditional logic without allowing arbitrary code execution:
|
|
761
|
-
|
|
762
|
-
```typescript
|
|
763
|
-
import { SafeExpressionEvaluator } from '@memberjunction/global';
|
|
764
|
-
|
|
765
|
-
// Create evaluator instance
|
|
766
|
-
const evaluator = new SafeExpressionEvaluator();
|
|
767
|
-
|
|
768
|
-
// Simple comparisons
|
|
769
|
-
const result1 = evaluator.evaluate(
|
|
770
|
-
"status == 'active' && score > 80",
|
|
771
|
-
{ status: 'active', score: 95 }
|
|
772
|
-
);
|
|
773
|
-
console.log(result1.success); // true
|
|
774
|
-
console.log(result1.value); // true
|
|
775
|
-
|
|
776
|
-
// Nested property access with dot notation
|
|
777
|
-
const result2 = evaluator.evaluate(
|
|
778
|
-
"user.role == 'admin' && user.permissions.includes('write')",
|
|
779
|
-
{
|
|
780
|
-
user: {
|
|
781
|
-
role: 'admin',
|
|
782
|
-
permissions: ['read', 'write', 'delete']
|
|
783
|
-
}
|
|
784
|
-
}
|
|
785
|
-
);
|
|
786
|
-
console.log(result2.value); // true
|
|
787
|
-
|
|
788
|
-
// Array methods and complex conditions
|
|
789
|
-
const result3 = evaluator.evaluate(
|
|
790
|
-
"items.some(item => item.price > 100) && items.length >= 2",
|
|
791
|
-
{
|
|
792
|
-
items: [
|
|
793
|
-
{ name: 'Item 1', price: 50 },
|
|
794
|
-
{ name: 'Item 2', price: 150 }
|
|
795
|
-
]
|
|
796
|
-
}
|
|
797
|
-
);
|
|
798
|
-
console.log(result3.value); // true
|
|
799
|
-
|
|
800
|
-
// Error handling for invalid expressions
|
|
801
|
-
const result4 = evaluator.evaluate(
|
|
802
|
-
"eval('malicious code')", // Dangerous patterns are blocked
|
|
803
|
-
{ data: 'test' }
|
|
804
|
-
);
|
|
805
|
-
console.log(result4.success); // false
|
|
806
|
-
console.log(result4.error); // "Expression contains forbidden construct: /\beval\s*\(/i"
|
|
807
|
-
|
|
808
|
-
// With diagnostics enabled
|
|
809
|
-
const result5 = evaluator.evaluate(
|
|
810
|
-
"payload.status == 'complete'",
|
|
811
|
-
{ payload: { status: 'complete' } },
|
|
812
|
-
true // Enable diagnostics
|
|
813
|
-
);
|
|
814
|
-
console.log(result5.diagnostics);
|
|
815
|
-
// {
|
|
816
|
-
// expression: "payload.status == 'complete'",
|
|
817
|
-
// context: { payload: { status: 'complete' } },
|
|
818
|
-
// evaluationTime: 2
|
|
819
|
-
// }
|
|
820
|
-
```
|
|
821
|
-
|
|
822
|
-
#### Supported Operations
|
|
823
|
-
|
|
824
|
-
**Comparison Operators:**
|
|
825
|
-
- `==`, `===`, `!=`, `!==`
|
|
826
|
-
- `<`, `>`, `<=`, `>=`
|
|
827
|
-
|
|
828
|
-
**Logical Operators:**
|
|
829
|
-
- `&&`, `||`, `!`
|
|
830
|
-
|
|
831
|
-
**Property Access:**
|
|
832
|
-
- Dot notation: `object.property.nested`
|
|
833
|
-
- Array access: `array[0]`, `array[index]`
|
|
834
|
-
|
|
835
|
-
**Safe Methods:**
|
|
836
|
-
- String: `.length`, `.includes()`, `.startsWith()`, `.endsWith()`, `.toLowerCase()`, `.toUpperCase()`, `.trim()`
|
|
837
|
-
- Array: `.length`, `.includes()`, `.some()`, `.every()`, `.find()`, `.filter()`, `.map()`
|
|
838
|
-
- Type checking: `typeof`, limited `instanceof`
|
|
839
|
-
|
|
840
|
-
**Type Coercion:**
|
|
841
|
-
- `Boolean(value)`
|
|
842
|
-
- String concatenation with `+`
|
|
843
|
-
|
|
844
|
-
#### Security Features
|
|
845
|
-
|
|
846
|
-
The evaluator blocks dangerous patterns including:
|
|
847
|
-
- `eval()`, `Function()`, `new Function()`
|
|
848
|
-
- `require()`, `import` statements
|
|
849
|
-
- Access to `global`, `window`, `document`, `process`
|
|
850
|
-
- Template literals and string interpolation
|
|
851
|
-
- Code blocks with `{}` and `;`
|
|
852
|
-
- `this` keyword usage
|
|
853
|
-
- `constructor`, `prototype`, `__proto__` access
|
|
854
|
-
|
|
855
|
-
#### Use Cases
|
|
856
|
-
|
|
857
|
-
1. **Workflow Conditions:**
|
|
858
|
-
```typescript
|
|
859
|
-
// Evaluate workflow paths
|
|
860
|
-
const canProceed = evaluator.evaluate(
|
|
861
|
-
"order.status == 'approved' && order.total < budget",
|
|
862
|
-
{ order: { status: 'approved', total: 500 }, budget: 1000 }
|
|
863
|
-
).value;
|
|
864
|
-
```
|
|
865
|
-
|
|
866
|
-
2. **Feature Flags:**
|
|
867
|
-
```typescript
|
|
868
|
-
// Check feature availability
|
|
869
|
-
const featureEnabled = evaluator.evaluate(
|
|
870
|
-
"user.tier == 'premium' || user.roles.includes('beta')",
|
|
871
|
-
{ user: { tier: 'standard', roles: ['beta', 'tester'] } }
|
|
872
|
-
).value;
|
|
873
|
-
```
|
|
874
|
-
|
|
875
|
-
3. **Validation Rules:**
|
|
876
|
-
```typescript
|
|
877
|
-
// Dynamic validation
|
|
878
|
-
const isValid = evaluator.evaluate(
|
|
879
|
-
"form.password.length >= 8 && form.password != form.username",
|
|
880
|
-
{ form: { username: 'john', password: 'secretpass123' } }
|
|
881
|
-
).value;
|
|
882
|
-
```
|
|
883
|
-
|
|
884
|
-
4. **Agent Decision Logic:**
|
|
885
|
-
```typescript
|
|
886
|
-
// AI agent path selection
|
|
887
|
-
const shouldDelegate = evaluator.evaluate(
|
|
888
|
-
"confidence < 0.7 || taskComplexity > 8",
|
|
889
|
-
{ confidence: 0.6, taskComplexity: 5 }
|
|
890
|
-
).value;
|
|
891
|
-
```
|
|
892
|
-
|
|
893
|
-
#### Batch Evaluation
|
|
894
|
-
|
|
895
|
-
Evaluate multiple expressions at once:
|
|
896
|
-
|
|
897
|
-
```typescript
|
|
898
|
-
const results = evaluator.evaluateMultiple([
|
|
899
|
-
{ expression: "status == 'active'", name: 'isActive' },
|
|
900
|
-
{ expression: "score > threshold", name: 'passedThreshold' },
|
|
901
|
-
{ expression: "tags.includes('priority')", name: 'isPriority' }
|
|
902
|
-
], {
|
|
903
|
-
status: 'active',
|
|
904
|
-
score: 85,
|
|
905
|
-
threshold: 80,
|
|
906
|
-
tags: ['urgent', 'priority']
|
|
907
|
-
});
|
|
908
|
-
|
|
909
|
-
// Results:
|
|
910
|
-
// {
|
|
911
|
-
// isActive: { success: true, value: true },
|
|
912
|
-
// passedThreshold: { success: true, value: true },
|
|
913
|
-
// isPriority: { success: true, value: true }
|
|
914
|
-
// }
|
|
915
|
-
```
|
|
916
|
-
|
|
917
|
-
### Pattern Matching Utilities
|
|
918
|
-
|
|
919
|
-
Convert string patterns to RegExp objects with support for simple wildcards and full regex syntax:
|
|
920
|
-
|
|
921
|
-
```typescript
|
|
922
|
-
import {
|
|
923
|
-
parsePattern,
|
|
924
|
-
parsePatterns,
|
|
925
|
-
ensureRegExp,
|
|
926
|
-
ensureRegExps,
|
|
927
|
-
matchesAnyPattern,
|
|
928
|
-
matchesAllPatterns
|
|
929
|
-
} from '@memberjunction/global';
|
|
930
|
-
|
|
931
|
-
// Parse simple wildcard patterns
|
|
932
|
-
parsePattern('*AIPrompt*'); // Returns: /AIPrompt/i (case-insensitive)
|
|
933
|
-
parsePattern('spCreate*'); // Returns: /^spCreate/i
|
|
934
|
-
parsePattern('*Run'); // Returns: /Run$/i
|
|
935
|
-
parsePattern('exact'); // Returns: /^exact$/i
|
|
936
|
-
|
|
937
|
-
// Parse regex string patterns
|
|
938
|
-
parsePattern('/spCreate.*Run/i'); // Returns: /spCreate.*Run/i
|
|
939
|
-
parsePattern('/^SELECT.*FROM.*vw/'); // Returns: /^SELECT.*FROM.*vw/
|
|
940
|
-
parsePattern('/INSERT INTO (Users|Roles)/i'); // Returns: /INSERT INTO (Users|Roles)/i
|
|
941
|
-
|
|
942
|
-
// Parse multiple patterns at once
|
|
943
|
-
const patterns = parsePatterns([
|
|
944
|
-
'*User*', // Simple wildcard
|
|
945
|
-
'/^EXEC sp_/i', // Regex string
|
|
946
|
-
'*EntityFieldValue*' // Simple wildcard
|
|
947
|
-
]);
|
|
948
|
-
|
|
949
|
-
// Convert mixed string/RegExp arrays
|
|
950
|
-
const mixed = ['*User*', /^Admin/i, '/DELETE.*WHERE/i'];
|
|
951
|
-
const regexps = ensureRegExps(mixed); // All converted to RegExp objects
|
|
952
|
-
|
|
953
|
-
// Test if text matches any pattern
|
|
954
|
-
const sql = 'SELECT * FROM Users WHERE Active = 1';
|
|
955
|
-
matchesAnyPattern(sql, ['*User*', '*Role*', '/^UPDATE/i']); // true
|
|
956
|
-
|
|
957
|
-
// Test if text matches all patterns
|
|
958
|
-
const filename = 'UserRoleManager.ts';
|
|
959
|
-
matchesAllPatterns(filename, ['*User*', '*Role*', '*.ts']); // true
|
|
960
|
-
```
|
|
961
|
-
|
|
962
|
-
#### Pattern Syntax
|
|
963
|
-
|
|
964
|
-
**Simple Wildcard Patterns** (Recommended for most users):
|
|
965
|
-
- `*` acts as a wildcard matching any characters
|
|
966
|
-
- Case-insensitive by default
|
|
967
|
-
- Examples:
|
|
968
|
-
- `*pattern*` - Contains "pattern" anywhere
|
|
969
|
-
- `pattern*` - Starts with "pattern"
|
|
970
|
-
- `*pattern` - Ends with "pattern"
|
|
971
|
-
- `pattern` - Exact match only
|
|
972
|
-
|
|
973
|
-
**Regex String Patterns** (For advanced users):
|
|
974
|
-
- Must start with `/` to be recognized as regex
|
|
975
|
-
- Optionally end with flags like `/pattern/i`
|
|
976
|
-
- Full JavaScript regex syntax supported
|
|
977
|
-
- Examples:
|
|
978
|
-
- `/^start/i` - Case-insensitive start match
|
|
979
|
-
- `/end$/` - Case-sensitive end match
|
|
980
|
-
- `/(option1|option2)/` - Match alternatives
|
|
981
|
-
|
|
982
|
-
#### Common Use Cases
|
|
983
|
-
|
|
984
|
-
```typescript
|
|
985
|
-
// SQL statement filtering
|
|
986
|
-
const sqlFilters = [
|
|
987
|
-
'*AIPrompt*', // Exclude AI prompt operations
|
|
988
|
-
'/^EXEC sp_/i', // Exclude system stored procedures
|
|
989
|
-
'*EntityFieldValue*' // Exclude field value operations
|
|
990
|
-
];
|
|
991
|
-
|
|
992
|
-
const shouldLog = !matchesAnyPattern(sqlStatement, sqlFilters);
|
|
993
|
-
|
|
994
|
-
// File pattern matching
|
|
995
|
-
const includePatterns = ['*.ts', '*.js', '/^(?!test)/']; // TS/JS files not starting with "test"
|
|
996
|
-
const shouldProcess = matchesAnyPattern(filename, includePatterns);
|
|
997
|
-
|
|
998
|
-
// User input validation
|
|
999
|
-
const allowedFormats = ['*@*.com', '*@*.org', '*@company.net'];
|
|
1000
|
-
const isValidEmail = matchesAnyPattern(email, allowedFormats);
|
|
1001
|
-
```
|
|
1002
|
-
|
|
1003
|
-
### Global Object Store
|
|
1004
|
-
|
|
1005
|
-
Access the global object store for cross-module state sharing:
|
|
1006
|
-
|
|
1007
|
-
```typescript
|
|
1008
|
-
import { GetGlobalObjectStore } from '@memberjunction/global';
|
|
1009
|
-
|
|
1010
|
-
const globalStore = GetGlobalObjectStore();
|
|
1011
|
-
// Returns window object in browser, global in Node.js
|
|
1012
|
-
```
|
|
1013
|
-
|
|
1014
|
-
### Manual Resize Request
|
|
1015
|
-
|
|
1016
|
-
Trigger a manual resize event across components:
|
|
1017
|
-
|
|
1018
|
-
```typescript
|
|
1019
|
-
import { InvokeManualResize } from '@memberjunction/global';
|
|
1020
|
-
|
|
1021
|
-
// Request resize after 50ms delay
|
|
1022
|
-
InvokeManualResize(50, myComponent);
|
|
1023
|
-
```
|
|
1024
|
-
|
|
1025
|
-
## Advanced Usage
|
|
1026
|
-
|
|
1027
|
-
### Class Factory with Root Class Detection
|
|
1028
|
-
|
|
1029
|
-
The Class Factory automatically detects and uses root classes for registration, ensuring proper priority ordering in inheritance hierarchies:
|
|
1030
|
-
|
|
1031
|
-
```typescript
|
|
1032
|
-
class BaseEntity {} // Root class
|
|
1033
|
-
|
|
1034
|
-
class UserEntity extends BaseEntity {}
|
|
1035
|
-
|
|
1036
|
-
// This automatically registers with BaseEntity (the root)
|
|
1037
|
-
@RegisterClass(UserEntity, 'Admin')
|
|
1038
|
-
class AdminUserEntity extends UserEntity {}
|
|
1039
|
-
|
|
1040
|
-
// Also registers with BaseEntity, gets higher priority
|
|
1041
|
-
@RegisterClass(AdminUserEntity, 'SuperAdmin')
|
|
1042
|
-
class SuperAdminEntity extends AdminUserEntity {}
|
|
1043
|
-
|
|
1044
|
-
// All of these create SuperAdminEntity (highest priority)
|
|
1045
|
-
factory.CreateInstance(BaseEntity, 'SuperAdmin'); // ✓ Works
|
|
1046
|
-
factory.CreateInstance(UserEntity, 'SuperAdmin'); // ✓ Works
|
|
1047
|
-
factory.CreateInstance(AdminUserEntity, 'SuperAdmin'); // ✓ Works
|
|
1048
|
-
```
|
|
1049
|
-
|
|
1050
|
-
### Disabling Auto-Root Registration
|
|
1051
|
-
|
|
1052
|
-
Sometimes you want to register at a specific level in the hierarchy:
|
|
1053
|
-
|
|
1054
|
-
```typescript
|
|
1055
|
-
// Register directly to UserEntity, not BaseEntity
|
|
1056
|
-
@RegisterClass(UserEntity, 'Special', 0, false, false) // Last param = false
|
|
1057
|
-
class SpecialUserEntity extends UserEntity {
|
|
1058
|
-
// This only matches when creating from UserEntity, not BaseEntity
|
|
1059
|
-
}
|
|
1060
|
-
|
|
1061
|
-
// Direct registration queries
|
|
1062
|
-
factory.CreateInstance(UserEntity, 'Special'); // ✓ Returns SpecialUserEntity
|
|
1063
|
-
factory.CreateInstance(BaseEntity, 'Special'); // ✗ Returns BaseEntity instance
|
|
1064
|
-
```
|
|
1065
|
-
|
|
1066
|
-
### Class Factory with Parameters
|
|
1067
|
-
|
|
1068
|
-
```typescript
|
|
1069
|
-
// Register a class that requires constructor parameters
|
|
1070
|
-
@RegisterClass(BaseService, 'api')
|
|
1071
|
-
class ApiService extends BaseService {
|
|
1072
|
-
constructor(private apiUrl: string) {
|
|
1073
|
-
super();
|
|
1074
|
-
}
|
|
1075
|
-
}
|
|
1076
|
-
|
|
1077
|
-
// Create with parameters
|
|
1078
|
-
const service = factory.CreateInstance<BaseService>(
|
|
1079
|
-
BaseService,
|
|
1080
|
-
'api',
|
|
1081
|
-
'https://api.example.com'
|
|
1082
|
-
);
|
|
1083
|
-
```
|
|
1084
|
-
|
|
1085
|
-
### Priority-based Registration
|
|
1086
|
-
|
|
1087
|
-
```typescript
|
|
1088
|
-
// Lower priority (registered first)
|
|
1089
|
-
@RegisterClass(BaseHandler, 'data', 10)
|
|
1090
|
-
class BasicDataHandler extends BaseHandler {}
|
|
1091
|
-
|
|
1092
|
-
// Higher priority (overrides BasicDataHandler)
|
|
1093
|
-
@RegisterClass(BaseHandler, 'data', 20)
|
|
1094
|
-
class AdvancedDataHandler extends BaseHandler {}
|
|
1095
|
-
|
|
1096
|
-
// Will create AdvancedDataHandler instance
|
|
1097
|
-
const handler = factory.CreateInstance<BaseHandler>(BaseHandler, 'data');
|
|
1098
|
-
```
|
|
1099
|
-
|
|
1100
|
-
### Inspecting Registrations
|
|
1101
|
-
|
|
1102
|
-
```typescript
|
|
1103
|
-
// Get all registrations for a base class
|
|
1104
|
-
const registrations = factory.GetAllRegistrations(BaseEntity, 'Users');
|
|
1105
|
-
|
|
1106
|
-
// Get registrations by root class
|
|
1107
|
-
const rootRegistrations = factory.GetRegistrationsByRootClass(BaseEntity);
|
|
1108
|
-
|
|
1109
|
-
// Each registration contains:
|
|
1110
|
-
// - BaseClass: The class it's registered to (usually root)
|
|
1111
|
-
// - SubClass: The actual implementation class
|
|
1112
|
-
// - RootClass: The detected root of the hierarchy
|
|
1113
|
-
// - Key: The registration key
|
|
1114
|
-
// - Priority: The priority number
|
|
1115
|
-
```
|
|
1116
|
-
|
|
1117
|
-
### Global Properties
|
|
1118
|
-
|
|
1119
|
-
Store and retrieve global properties:
|
|
1120
|
-
|
|
1121
|
-
```typescript
|
|
1122
|
-
const properties = MJGlobal.Instance.Properties;
|
|
1123
|
-
properties.push({
|
|
1124
|
-
key: 'apiEndpoint',
|
|
1125
|
-
value: 'https://api.example.com'
|
|
1126
|
-
});
|
|
1127
|
-
```
|
|
1128
|
-
|
|
1129
|
-
## Integration with MemberJunction
|
|
1130
|
-
|
|
1131
|
-
This package is a core dependency for most MemberJunction packages. It provides the foundation for:
|
|
1132
|
-
|
|
1133
|
-
- Entity registration and instantiation
|
|
1134
|
-
- Cross-component event communication
|
|
1135
|
-
- Singleton service management
|
|
1136
|
-
- Global state coordination
|
|
1137
|
-
|
|
1138
|
-
When building MemberJunction applications or extensions, use this package to ensure proper integration with the framework's architecture.
|
|
1139
|
-
|
|
1140
|
-
## TypeScript Support
|
|
1141
|
-
|
|
1142
|
-
This package is written in TypeScript and includes full type definitions. All exports are properly typed for excellent IDE support and compile-time type checking.
|
|
1143
|
-
|
|
1144
|
-
## Dependencies
|
|
1145
|
-
|
|
1146
|
-
- **rxjs** (^7.8.1) - For reactive event handling
|
|
1147
|
-
|
|
1148
|
-
## Development
|
|
1149
|
-
|
|
1150
|
-
```bash
|
|
1151
|
-
# Build the package
|
|
1152
|
-
npm run build
|
|
1153
|
-
|
|
1154
|
-
# Start in development mode with hot reload
|
|
1155
|
-
npm run start
|
|
1156
|
-
|
|
1157
|
-
# Run tests (when implemented)
|
|
1158
|
-
npm test
|
|
1159
|
-
```
|
|
1160
|
-
|
|
1161
|
-
## License
|
|
1162
|
-
|
|
1163
|
-
ISC
|
|
1164
|
-
|
|
1165
|
-
## Author
|
|
1166
|
-
|
|
1167
|
-
MemberJunction.com
|
|
1168
|
-
|