@memberjunction/global 5.41.0 → 5.43.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 +89 -0
- package/dist/KeyedSerialTaskQueue.d.ts +60 -0
- package/dist/KeyedSerialTaskQueue.d.ts.map +1 -0
- package/dist/KeyedSerialTaskQueue.js +76 -0
- package/dist/KeyedSerialTaskQueue.js.map +1 -0
- package/dist/SafeExpressionEvaluator.d.ts +5 -0
- package/dist/SafeExpressionEvaluator.d.ts.map +1 -1
- package/dist/SafeExpressionEvaluator.js +26 -11
- package/dist/SafeExpressionEvaluator.js.map +1 -1
- package/dist/__tests__/FieldRulesEvaluator.test.d.ts +2 -0
- package/dist/__tests__/FieldRulesEvaluator.test.d.ts.map +1 -0
- package/dist/__tests__/FieldRulesEvaluator.test.js +167 -0
- package/dist/__tests__/FieldRulesEvaluator.test.js.map +1 -0
- package/dist/__tests__/KeyedSerialTaskQueue.test.d.ts +2 -0
- package/dist/__tests__/KeyedSerialTaskQueue.test.d.ts.map +1 -0
- package/dist/__tests__/KeyedSerialTaskQueue.test.js +91 -0
- package/dist/__tests__/KeyedSerialTaskQueue.test.js.map +1 -0
- package/dist/__tests__/UUIDCompliance.test.js +2 -0
- package/dist/__tests__/UUIDCompliance.test.js.map +1 -1
- package/dist/__tests__/valueMapping.test.d.ts +6 -0
- package/dist/__tests__/valueMapping.test.d.ts.map +1 -0
- package/dist/__tests__/valueMapping.test.js +68 -0
- package/dist/__tests__/valueMapping.test.js.map +1 -0
- package/dist/fieldRules/FieldRulesEvaluator.d.ts +34 -0
- package/dist/fieldRules/FieldRulesEvaluator.d.ts.map +1 -0
- package/dist/fieldRules/FieldRulesEvaluator.js +130 -0
- package/dist/fieldRules/FieldRulesEvaluator.js.map +1 -0
- package/dist/fieldRules/FieldTransformEngine.d.ts +44 -0
- package/dist/fieldRules/FieldTransformEngine.d.ts.map +1 -0
- package/dist/fieldRules/FieldTransformEngine.js +189 -0
- package/dist/fieldRules/FieldTransformEngine.js.map +1 -0
- package/dist/fieldRules/index.d.ts +6 -0
- package/dist/fieldRules/index.d.ts.map +1 -0
- package/dist/fieldRules/index.js +6 -0
- package/dist/fieldRules/index.js.map +1 -0
- package/dist/fieldRules/rules.d.ts +135 -0
- package/dist/fieldRules/rules.d.ts.map +1 -0
- package/dist/fieldRules/rules.js +2 -0
- package/dist/fieldRules/rules.js.map +1 -0
- package/dist/fieldRules/transformRegistry.d.ts +22 -0
- package/dist/fieldRules/transformRegistry.d.ts.map +1 -0
- package/dist/fieldRules/transformRegistry.js +33 -0
- package/dist/fieldRules/transformRegistry.js.map +1 -0
- package/dist/fieldRules/transforms.d.ts +102 -0
- package/dist/fieldRules/transforms.d.ts.map +1 -0
- package/dist/fieldRules/transforms.js +8 -0
- package/dist/fieldRules/transforms.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/valueMapping.d.ts +33 -0
- package/dist/valueMapping.d.ts.map +1 -0
- package/dist/valueMapping.js +104 -0
- package/dist/valueMapping.js.map +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -456,6 +456,26 @@ cache.Remove('user-prefs');
|
|
|
456
456
|
cache.Clear();
|
|
457
457
|
```
|
|
458
458
|
|
|
459
|
+
### KeyedSerialTaskQueue
|
|
460
|
+
|
|
461
|
+
An entity-agnostic primitive for **fire-and-forget work that must serialize per key**. Tasks enqueued under the same key (compared by object identity) run strictly in order — the next can't start until the prior settles — while tasks under different keys run concurrently. Failures are tallied for a later `flush()` and never propagate outward, so the enqueue site is never blocked, nor broken, by a background failure.
|
|
462
|
+
|
|
463
|
+
It is **self-bounding**: only in-flight tasks are retained (they drop out as they settle) and failures accumulate into counters, so a long-lived queue that never flushes does not grow without bound.
|
|
464
|
+
|
|
465
|
+
```typescript
|
|
466
|
+
import { KeyedSerialTaskQueue } from '@memberjunction/global';
|
|
467
|
+
|
|
468
|
+
const queue = new KeyedSerialTaskQueue({ onError: (err, label) => console.error(label, err) });
|
|
469
|
+
|
|
470
|
+
// Same key (the `record` object) → these run in order; different keys → concurrent.
|
|
471
|
+
queue.enqueue(record, () => insert(record));
|
|
472
|
+
queue.enqueue(record, () => update(record), { label: 'update', isOk: (ok) => ok === true });
|
|
473
|
+
|
|
474
|
+
const { failures, rejections } = await queue.flush(); // await in-flight tasks + read/reset the failure tally
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
The canonical consumer is `BaseEntitySaveQueue` in `@memberjunction/core`, which builds the fire-and-forget entity-save pattern (INSERT then chained UPDATE, with the "mutate-after-insert" race made structurally impossible) on top of this primitive.
|
|
478
|
+
|
|
459
479
|
### WarningManager
|
|
460
480
|
|
|
461
481
|
A singleton warning system with session-level deduplication, debounced output, and tree-structured formatting. Tracks deprecation warnings, field-not-found warnings, and redundant load warnings.
|
|
@@ -675,6 +695,75 @@ function RegisterClass(
|
|
|
675
695
|
| `lodash` | Deep comparison, type checking, object utilities |
|
|
676
696
|
| `uuid` | UUID v4 generation |
|
|
677
697
|
|
|
698
|
+
## Field Rules Engine
|
|
699
|
+
|
|
700
|
+
A **framework-agnostic** engine for computing field values from declarative rules and a composable
|
|
701
|
+
transform pipeline. It is the shared substrate behind two MemberJunction features — and is designed so
|
|
702
|
+
*any* code can use it, because it lives here in `global` with zero dependencies beyond this package:
|
|
703
|
+
|
|
704
|
+
| Layer | Package | Use it when |
|
|
705
|
+
|---|---|---|
|
|
706
|
+
| **Pure engine** (this package) | `@memberjunction/global` | You have a **plain record** (`Record<string, unknown>`) — from anywhere — and want to compute/transform field values. No entity, no DB required. |
|
|
707
|
+
| **`EntityFieldRules`** | `@memberjunction/core` | The **target is an MJ entity** — you want metadata validation, automatic type coercion, RunView-backed lookups, and write-back with Record Changes versioning. Builds on this engine. |
|
|
708
|
+
| **`FieldMappingEngine`** | `@memberjunction/integration` | The **other side is a live external system** (its own protocol, auth, match resolution, sync direction). Uses this engine for the per-field transforms. |
|
|
709
|
+
|
|
710
|
+
> One engine, three purpose-built layers. Pick the lowest one that fits: if you only have a dict, use this; if you're updating an entity, use core; if you're syncing an external system, that's integration's job.
|
|
711
|
+
|
|
712
|
+
### Two pieces
|
|
713
|
+
|
|
714
|
+
**1. `FieldTransformEngine` — a transform pipeline over `(value, fields)`**
|
|
715
|
+
|
|
716
|
+
Runs an ordered list of `TransformStep`s. Step types: `direct`, `regex`, `split`, `combine`, `lookup`
|
|
717
|
+
(in-memory map), `format`, `coerce`, `substring`, `custom` (a `(value, fields) => result` expression,
|
|
718
|
+
compiled once and LRU-cached). Per-step `OnError` is `Skip` | `Null` | `Fail` (default `Null` — grace).
|
|
719
|
+
|
|
720
|
+
```ts
|
|
721
|
+
import { FieldTransformEngine } from '@memberjunction/global';
|
|
722
|
+
|
|
723
|
+
const e = new FieldTransformEngine();
|
|
724
|
+
e.ExecutePipeline('19.99', {}, [{ Type: 'coerce', Config: { TargetType: 'number' } }]).Value; // 19.99
|
|
725
|
+
e.Evaluate("fields.First + ' ' + fields.Last", undefined, { First: 'Ada', Last: 'Lovelace' }); // 'Ada Lovelace'
|
|
726
|
+
```
|
|
727
|
+
|
|
728
|
+
**2. `FieldRulesEvaluator` — rules → a per-field diff (no mutation)**
|
|
729
|
+
|
|
730
|
+
A `FieldRule` sets a `TargetField` from a `Source` — `static` | `field` | `formula` | `lookup` —
|
|
731
|
+
optionally through a `Transforms` pipeline, gated by an optional `Condition` (a **safe** boolean
|
|
732
|
+
expression via this package's `SafeExpressionEvaluator`). `ComputeChanges` returns a `FieldChange[]`
|
|
733
|
+
(old → new per rule) **without applying anything** — which is exactly what makes a **dry-run preview**
|
|
734
|
+
possible: the caller decides whether to write.
|
|
735
|
+
|
|
736
|
+
```ts
|
|
737
|
+
import { FieldRulesEvaluator, type FieldRuleSet } from '@memberjunction/global';
|
|
738
|
+
|
|
739
|
+
const ruleSet: FieldRuleSet = {
|
|
740
|
+
Rules: [
|
|
741
|
+
{ TargetField: 'FullName', Source: { Kind: 'formula', Expression: "fields.FirstName + ' ' + fields.LastName" } },
|
|
742
|
+
{ TargetField: 'Tier', Source: { Kind: 'static', Value: 'Gold' }, Condition: 'Revenue > 1000' },
|
|
743
|
+
],
|
|
744
|
+
};
|
|
745
|
+
|
|
746
|
+
// LookupResolver is optional — only needed for `lookup` sources. core's EntityFieldRules supplies a
|
|
747
|
+
// RunView-backed one; here you inject your own (or omit it).
|
|
748
|
+
const changes = await new FieldRulesEvaluator().ComputeChanges(
|
|
749
|
+
{ FirstName: 'Ada', LastName: 'Lovelace', FullName: '', Revenue: 5000, Tier: 'Bronze' },
|
|
750
|
+
ruleSet,
|
|
751
|
+
);
|
|
752
|
+
// → [{ Field: 'FullName', OldValue: '', NewValue: 'Ada Lovelace', Changed: true, Applied: true }, …]
|
|
753
|
+
```
|
|
754
|
+
|
|
755
|
+
### Safety
|
|
756
|
+
|
|
757
|
+
- `Condition` expressions use the **safe** evaluator (blocklisted patterns, no statements).
|
|
758
|
+
- `formula` / `custom` value expressions compile with `new Function` (the same model as the established
|
|
759
|
+
integration `custom` transform) — author them from privileged users/agents, not untrusted input.
|
|
760
|
+
- The engine performs **no I/O**; `lookup` reaches a database only through the resolver you inject.
|
|
761
|
+
|
|
762
|
+
### See also
|
|
763
|
+
|
|
764
|
+
- **`EntityFieldRules`** in [`@memberjunction/core`](../MJCore/README.md#entity-field-rules) — the metadata-aware layer for updating entities (validation, coercion, lookups, apply).
|
|
765
|
+
- **`FieldMappingEngine`** in [`@memberjunction/integration`](../Integration/engine/README.md#field-mapping--the-shared-transform-engine) — external-system field mapping built on this engine.
|
|
766
|
+
|
|
678
767
|
## Related Packages
|
|
679
768
|
|
|
680
769
|
| Package | Relationship |
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Entity-agnostic per-key serial task queue. The lowest-layer primitive behind the
|
|
3
|
+
* fire-and-forget save pattern: tasks enqueued under the same key run strictly in order (the next
|
|
4
|
+
* cannot start until the prior settles), tasks under different keys run concurrently, and failures
|
|
5
|
+
* are captured for a later `flush()` rather than propagated outward. MJGlobal cannot reference
|
|
6
|
+
* `BaseEntity`, so this knows nothing about entities — see `BaseEntitySaveQueue` for the entity façade.
|
|
7
|
+
*
|
|
8
|
+
* Self-bounding: only *in-flight* tasks are retained (they drop out as they settle), and failures are
|
|
9
|
+
* tallied into counters — so a long-lived queue that never flushes does not grow without bound.
|
|
10
|
+
* @module @memberjunction/global
|
|
11
|
+
*/
|
|
12
|
+
/** Aggregate outcome of a {@link KeyedSerialTaskQueue.flush}. */
|
|
13
|
+
export interface SerialTaskFlushResult {
|
|
14
|
+
/** Tasks that rejected (threw) OR resolved a falsy "not ok" value, since the last flush. */
|
|
15
|
+
failures: number;
|
|
16
|
+
/** Tasks that rejected (threw), since the last flush. */
|
|
17
|
+
rejections: number;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* A per-key serial task chain. Tasks for the **same key** (compared by object identity) run one at a
|
|
21
|
+
* time in enqueue order; tasks for **different keys** run concurrently. Enqueuing is fire-and-forget —
|
|
22
|
+
* the returned promise never rejects, and failures are tallied for {@link flush}.
|
|
23
|
+
*
|
|
24
|
+
* This is what makes "mutate after the prior task lands" structural: a task enqueued after another on
|
|
25
|
+
* the same key physically cannot begin until the prior one resolves.
|
|
26
|
+
*/
|
|
27
|
+
export declare class KeyedSerialTaskQueue {
|
|
28
|
+
/** Latest tail promise per key (object identity) — never rejects, so the chain can't break. */
|
|
29
|
+
private tails;
|
|
30
|
+
/** Currently-running tasks (each removes itself on settle), for `flush()` to await. */
|
|
31
|
+
private inFlight;
|
|
32
|
+
/** Failures/rejections since the last flush (counters, not a growing list — keeps memory bounded). */
|
|
33
|
+
private failures;
|
|
34
|
+
private rejections;
|
|
35
|
+
private readonly onError?;
|
|
36
|
+
constructor(opts?: {
|
|
37
|
+
onError?: (err: unknown, label?: string) => void;
|
|
38
|
+
});
|
|
39
|
+
/**
|
|
40
|
+
* Enqueues `task` to run after all prior tasks for `key` have settled. Fire-and-forget: returns
|
|
41
|
+
* the task's result promise, but the caller need not await it — it resolves to the task's value on
|
|
42
|
+
* success or `undefined` on failure, and **never rejects**. Failures (a thrown error, or a resolved
|
|
43
|
+
* value `opts.isOk` deems falsy) are counted for {@link flush} and routed to `onError`.
|
|
44
|
+
*
|
|
45
|
+
* @param key Serialization key (object identity). Same key → serialized; different keys → concurrent.
|
|
46
|
+
* @param task The work to run.
|
|
47
|
+
* @param opts.label Diagnostic label passed to `onError`.
|
|
48
|
+
* @param opts.isOk Treats a resolved value as a failure when it returns false (e.g. `Save()` → `false`).
|
|
49
|
+
*/
|
|
50
|
+
enqueue<T>(key: object, task: () => Promise<T>, opts?: {
|
|
51
|
+
label?: string;
|
|
52
|
+
isOk?: (v: T) => boolean;
|
|
53
|
+
}): Promise<T | undefined>;
|
|
54
|
+
/**
|
|
55
|
+
* Awaits the currently in-flight tasks, then reports and resets the failure tally accumulated since
|
|
56
|
+
* the last flush. Tasks enqueued after this call begins are not awaited here.
|
|
57
|
+
*/
|
|
58
|
+
flush(): Promise<SerialTaskFlushResult>;
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=KeyedSerialTaskQueue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"KeyedSerialTaskQueue.d.ts","sourceRoot":"","sources":["../src/KeyedSerialTaskQueue.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,iEAAiE;AACjE,MAAM,WAAW,qBAAqB;IAClC,4FAA4F;IAC5F,QAAQ,EAAE,MAAM,CAAC;IACjB,yDAAyD;IACzD,UAAU,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;GAOG;AACH,qBAAa,oBAAoB;IAC7B,+FAA+F;IAC/F,OAAO,CAAC,KAAK,CAAwC;IACrD,uFAAuF;IACvF,OAAO,CAAC,QAAQ,CAA4B;IAC5C,sGAAsG;IACtG,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAyC;gBAEtD,IAAI,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;KAAE;IAIvE;;;;;;;;;;OAUG;IACI,OAAO,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IA8BnI;;;OAGG;IACU,KAAK,IAAI,OAAO,CAAC,qBAAqB,CAAC;CAOvD"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Entity-agnostic per-key serial task queue. The lowest-layer primitive behind the
|
|
3
|
+
* fire-and-forget save pattern: tasks enqueued under the same key run strictly in order (the next
|
|
4
|
+
* cannot start until the prior settles), tasks under different keys run concurrently, and failures
|
|
5
|
+
* are captured for a later `flush()` rather than propagated outward. MJGlobal cannot reference
|
|
6
|
+
* `BaseEntity`, so this knows nothing about entities — see `BaseEntitySaveQueue` for the entity façade.
|
|
7
|
+
*
|
|
8
|
+
* Self-bounding: only *in-flight* tasks are retained (they drop out as they settle), and failures are
|
|
9
|
+
* tallied into counters — so a long-lived queue that never flushes does not grow without bound.
|
|
10
|
+
* @module @memberjunction/global
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* A per-key serial task chain. Tasks for the **same key** (compared by object identity) run one at a
|
|
14
|
+
* time in enqueue order; tasks for **different keys** run concurrently. Enqueuing is fire-and-forget —
|
|
15
|
+
* the returned promise never rejects, and failures are tallied for {@link flush}.
|
|
16
|
+
*
|
|
17
|
+
* This is what makes "mutate after the prior task lands" structural: a task enqueued after another on
|
|
18
|
+
* the same key physically cannot begin until the prior one resolves.
|
|
19
|
+
*/
|
|
20
|
+
export class KeyedSerialTaskQueue {
|
|
21
|
+
constructor(opts) {
|
|
22
|
+
/** Latest tail promise per key (object identity) — never rejects, so the chain can't break. */
|
|
23
|
+
this.tails = new WeakMap();
|
|
24
|
+
/** Currently-running tasks (each removes itself on settle), for `flush()` to await. */
|
|
25
|
+
this.inFlight = new Set();
|
|
26
|
+
/** Failures/rejections since the last flush (counters, not a growing list — keeps memory bounded). */
|
|
27
|
+
this.failures = 0;
|
|
28
|
+
this.rejections = 0;
|
|
29
|
+
this.onError = opts?.onError;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Enqueues `task` to run after all prior tasks for `key` have settled. Fire-and-forget: returns
|
|
33
|
+
* the task's result promise, but the caller need not await it — it resolves to the task's value on
|
|
34
|
+
* success or `undefined` on failure, and **never rejects**. Failures (a thrown error, or a resolved
|
|
35
|
+
* value `opts.isOk` deems falsy) are counted for {@link flush} and routed to `onError`.
|
|
36
|
+
*
|
|
37
|
+
* @param key Serialization key (object identity). Same key → serialized; different keys → concurrent.
|
|
38
|
+
* @param task The work to run.
|
|
39
|
+
* @param opts.label Diagnostic label passed to `onError`.
|
|
40
|
+
* @param opts.isOk Treats a resolved value as a failure when it returns false (e.g. `Save()` → `false`).
|
|
41
|
+
*/
|
|
42
|
+
enqueue(key, task, opts) {
|
|
43
|
+
const prior = this.tails.get(key) ?? Promise.resolve();
|
|
44
|
+
// Run the task exactly once, after `prior` settles, collapsing to a never-rejecting outcome.
|
|
45
|
+
const settled = prior.then(() => task(), () => task()).then((value) => ({ value: value, ok: opts?.isOk ? !!opts.isOk(value) : true, rejected: false, err: undefined }), (err) => ({ value: undefined, ok: false, rejected: true, err }));
|
|
46
|
+
// Never-rejecting tail: tally failures + route to onError. Serves as the per-key chain link.
|
|
47
|
+
const tail = settled.then((o) => {
|
|
48
|
+
if (!o.ok) {
|
|
49
|
+
this.failures++;
|
|
50
|
+
if (o.rejected) {
|
|
51
|
+
this.rejections++;
|
|
52
|
+
}
|
|
53
|
+
const err = o.rejected ? o.err : new Error(`task resolved not-ok${opts?.label ? `: ${opts.label}` : ''}`);
|
|
54
|
+
this.onError?.(err, opts?.label);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
this.tails.set(key, tail);
|
|
58
|
+
// Track only while in flight — self-remove on settle so the set stays bounded by concurrency.
|
|
59
|
+
let tracked;
|
|
60
|
+
tracked = tail.finally(() => this.inFlight.delete(tracked));
|
|
61
|
+
this.inFlight.add(tracked);
|
|
62
|
+
return settled.then((o) => (o.ok ? o.value : undefined));
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Awaits the currently in-flight tasks, then reports and resets the failure tally accumulated since
|
|
66
|
+
* the last flush. Tasks enqueued after this call begins are not awaited here.
|
|
67
|
+
*/
|
|
68
|
+
async flush() {
|
|
69
|
+
await Promise.all([...this.inFlight]);
|
|
70
|
+
const result = { failures: this.failures, rejections: this.rejections };
|
|
71
|
+
this.failures = 0;
|
|
72
|
+
this.rejections = 0;
|
|
73
|
+
return result;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=KeyedSerialTaskQueue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"KeyedSerialTaskQueue.js","sourceRoot":"","sources":["../src/KeyedSerialTaskQueue.ts"],"names":[],"mappings":"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"}
|
|
@@ -66,6 +66,11 @@ export declare class SafeExpressionEvaluator {
|
|
|
66
66
|
* @private
|
|
67
67
|
*/
|
|
68
68
|
private static readonly DANGEROUS_PATTERNS;
|
|
69
|
+
/**
|
|
70
|
+
* Cache for compiled expressions to improve performance on repeated evaluations
|
|
71
|
+
* @private
|
|
72
|
+
*/
|
|
73
|
+
private readonly _compiledExpressionCache;
|
|
69
74
|
/**
|
|
70
75
|
* Safe methods that can be called on objects
|
|
71
76
|
* @private
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SafeExpressionEvaluator.d.ts","sourceRoot":"","sources":["../src/SafeExpressionEvaluator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;
|
|
1
|
+
{"version":3,"file":"SafeExpressionEvaluator.d.ts","sourceRoot":"","sources":["../src/SafeExpressionEvaluator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH;;GAEG;AACH,MAAM,WAAW,0BAA0B;IACvC,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE;QACV,UAAU,EAAE,MAAM,CAAC;QACnB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAC7B,cAAc,EAAE,MAAM,CAAC;KAC1B,CAAC;CACL;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,qBAAa,uBAAuB;IAChC;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,kBAAkB,CA4BxC;IAEF;;;OAGG;IACH,OAAO,CAAC,QAAQ,CAAC,wBAAwB,CAAuD;IAEhG;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAoBlC;IAEF;;;;;;;;OAQG;IACI,QAAQ,CACX,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC5B,iBAAiB,GAAE,OAAe,GACnC,0BAA0B;IAwE7B;;;;;;;;OAQG;IACH,OAAO,CAAC,kBAAkB;IAgC1B;;;;;;;;OAQG;IACH,OAAO,CAAC,iBAAiB;IAiBzB;;;;;;;;OAQG;IACH,OAAO,CAAC,uBAAuB;IAW/B;;;;;;;;OAQG;IACH,OAAO,CAAC,UAAU;IAwDlB;;;;;;;OAOG;IACI,gBAAgB,CACnB,WAAW,EAAE,KAAK,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAC,CAAC,EACvD,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAC7B,MAAM,CAAC,MAAM,EAAE,0BAA0B,CAAC;CAUhD;AAED;;GAEG;AACH,eAAO,MAAM,0BAA0B,yBAAgC,CAAC"}
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* @author MemberJunction.com
|
|
10
10
|
* @since 2.76.0
|
|
11
11
|
*/
|
|
12
|
+
import { MJLruCache } from './MJLruCache.js';
|
|
12
13
|
/**
|
|
13
14
|
* Safe expression evaluator that prevents arbitrary code execution while
|
|
14
15
|
* supporting common boolean expressions and property access patterns.
|
|
@@ -48,6 +49,13 @@
|
|
|
48
49
|
* ```
|
|
49
50
|
*/
|
|
50
51
|
export class SafeExpressionEvaluator {
|
|
52
|
+
constructor() {
|
|
53
|
+
/**
|
|
54
|
+
* Cache for compiled expressions to improve performance on repeated evaluations
|
|
55
|
+
* @private
|
|
56
|
+
*/
|
|
57
|
+
this._compiledExpressionCache = new MJLruCache({ maxSize: 1000 });
|
|
58
|
+
}
|
|
51
59
|
/**
|
|
52
60
|
* Patterns that indicate potentially dangerous code
|
|
53
61
|
* @private
|
|
@@ -136,17 +144,24 @@ export class SafeExpressionEvaluator {
|
|
|
136
144
|
// Create evaluation function
|
|
137
145
|
const contextKeys = Object.keys(safeContext);
|
|
138
146
|
const contextValues = contextKeys.map(key => safeContext[key]);
|
|
139
|
-
//
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
147
|
+
// Check cache for compiled expression
|
|
148
|
+
const cacheKey = `${expression}|${contextKeys.join(',')}`;
|
|
149
|
+
let evaluator = this._compiledExpressionCache.Get(cacheKey);
|
|
150
|
+
if (!evaluator) {
|
|
151
|
+
// Build function body with strict mode
|
|
152
|
+
const functionBody = `
|
|
153
|
+
"use strict";
|
|
154
|
+
try {
|
|
155
|
+
return Boolean(${expression});
|
|
156
|
+
} catch (e) {
|
|
157
|
+
throw new Error('Expression evaluation failed: ' + e.message);
|
|
158
|
+
}
|
|
159
|
+
`;
|
|
160
|
+
// Create and execute function
|
|
161
|
+
evaluator = new Function(...contextKeys, functionBody);
|
|
162
|
+
// Store in cache
|
|
163
|
+
this._compiledExpressionCache.Set(cacheKey, evaluator);
|
|
164
|
+
}
|
|
150
165
|
const result = evaluator(...contextValues);
|
|
151
166
|
return {
|
|
152
167
|
success: true,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SafeExpressionEvaluator.js","sourceRoot":"","sources":["../src/SafeExpressionEvaluator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;
|
|
1
|
+
{"version":3,"file":"SafeExpressionEvaluator.js","sourceRoot":"","sources":["../src/SafeExpressionEvaluator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FieldRulesEvaluator.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/FieldRulesEvaluator.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { FieldRulesEvaluator } from '../fieldRules/FieldRulesEvaluator.js';
|
|
3
|
+
import { FieldTransformEngine } from '../fieldRules/FieldTransformEngine.js';
|
|
4
|
+
import { RegisterFieldTransform, GetFieldTransform } from '../fieldRules/transformRegistry.js';
|
|
5
|
+
describe('FieldTransformEngine — plugin registry (keeps libs out of global)', () => {
|
|
6
|
+
it('dispatches a non-built-in transform to a registered plugin', () => {
|
|
7
|
+
const seen = [];
|
|
8
|
+
RegisterFieldTransform('jsonpath', (value, _fields, config) => {
|
|
9
|
+
seen.push({ value, config });
|
|
10
|
+
return 'plugin-result';
|
|
11
|
+
});
|
|
12
|
+
expect(GetFieldTransform('JSONPath')).toBeTypeOf('function'); // case-insensitive
|
|
13
|
+
const out = new FieldTransformEngine().ExecutePipeline('{"a":1}', {}, [{ Type: 'jsonpath', Config: { Path: '$.a' } }]);
|
|
14
|
+
expect(out.Value).toBe('plugin-result');
|
|
15
|
+
expect(seen[0]).toEqual({ value: '{"a":1}', config: { Path: '$.a' } });
|
|
16
|
+
});
|
|
17
|
+
it('errors clearly for an unregistered plugin transform (OnError=Fail)', () => {
|
|
18
|
+
expect(() => new FieldTransformEngine().ExecutePipeline('x', {}, [{ Type: 'xpath', Config: { Path: '/a' }, OnError: 'Fail' }]))
|
|
19
|
+
.toThrow(/Unknown transform type 'xpath'/);
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
describe('FieldRulesEvaluator — entityDocument source (injected render infra)', () => {
|
|
23
|
+
it('renders the record via the injected EntityDocumentResolver', async () => {
|
|
24
|
+
const seen = [];
|
|
25
|
+
const evaluator = new FieldRulesEvaluator({ EntityDocumentResolver: async (d) => { seen.push(d); return 'RENDERED DOC'; } });
|
|
26
|
+
const [c] = await evaluator.ComputeChanges({ Name: 'Acme', Doc: '' }, { Rules: [{ TargetField: 'Doc', Source: { Kind: 'entityDocument', EntityDocument: { EntityDocumentID: 'ed-1' } } }] });
|
|
27
|
+
expect(c.NewValue).toBe('RENDERED DOC');
|
|
28
|
+
expect(seen[0]).toEqual({ EntityDocumentID: 'ed-1', Record: { Name: 'Acme', Doc: '' } });
|
|
29
|
+
});
|
|
30
|
+
it('errors clearly when no EntityDocumentResolver is provided', async () => {
|
|
31
|
+
const [c] = await new FieldRulesEvaluator().ComputeChanges({ X: '' }, { Rules: [{ TargetField: 'X', Source: { Kind: 'entityDocument' } }] });
|
|
32
|
+
expect(c.Applied).toBe(false);
|
|
33
|
+
expect(c.Error).toMatch(/no EntityDocumentResolver/);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
describe('FieldRulesEvaluator — prompt sources', () => {
|
|
37
|
+
it('passes the WHOLE record to the prompt by default and uses its output', async () => {
|
|
38
|
+
const seen = [];
|
|
39
|
+
const evaluator = new FieldRulesEvaluator({ PromptResolver: async (p) => { seen.push(p); return 'A concise summary.'; } });
|
|
40
|
+
const [c] = await evaluator.ComputeChanges({ Name: 'Acme', Notes: 'long notes…', Summary: '' }, { Rules: [{ TargetField: 'Summary', Source: { Kind: 'prompt', Prompt: { PromptID: 'p-1' } } }] });
|
|
41
|
+
expect(c.NewValue).toBe('A concise summary.');
|
|
42
|
+
expect(seen[0].PromptID).toBe('p-1');
|
|
43
|
+
expect(seen[0].Data).toEqual({ Name: 'Acme', Notes: 'long notes…', Summary: '' }); // whole record
|
|
44
|
+
});
|
|
45
|
+
it('shapes the prompt data when Data is provided (field/formula/static resolved per entry)', async () => {
|
|
46
|
+
let captured = {};
|
|
47
|
+
const evaluator = new FieldRulesEvaluator({ PromptResolver: async (p) => { captured = p.Data; return 'ok'; } });
|
|
48
|
+
await evaluator.ComputeChanges({ First: 'Ada', Last: 'Lovelace', Bio: '' }, { Rules: [{ TargetField: 'Bio', Source: { Kind: 'prompt', Prompt: { PromptID: 'p-2', Data: {
|
|
49
|
+
fullName: { Kind: 'formula', Expression: "fields.First + ' ' + fields.Last" },
|
|
50
|
+
role: { Kind: 'static', Value: 'mathematician' },
|
|
51
|
+
} } } }] });
|
|
52
|
+
expect(captured).toEqual({ fullName: 'Ada Lovelace', role: 'mathematician' });
|
|
53
|
+
});
|
|
54
|
+
it('errors clearly when a prompt rule is used with no PromptResolver', async () => {
|
|
55
|
+
const [c] = await new FieldRulesEvaluator().ComputeChanges({ X: '' }, { Rules: [{ TargetField: 'X', Source: { Kind: 'prompt', Prompt: { PromptID: 'p' } } }] });
|
|
56
|
+
expect(c.Applied).toBe(false);
|
|
57
|
+
expect(c.Error).toMatch(/no PromptResolver/);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
describe('FieldRulesEvaluator.ComputeChanges', () => {
|
|
61
|
+
const run = async (record, ruleSet, resolver) => new FieldRulesEvaluator({ LookupResolver: resolver }).ComputeChanges(record, ruleSet);
|
|
62
|
+
describe('value sources', () => {
|
|
63
|
+
it('static: sets a literal value and flags the change', async () => {
|
|
64
|
+
const [c] = await run({ Status: 'Active' }, { Rules: [{ TargetField: 'Status', Source: { Kind: 'static', Value: 'Inactive' } }] });
|
|
65
|
+
expect(c).toMatchObject({ Field: 'Status', OldValue: 'Active', NewValue: 'Inactive', Changed: true, Applied: true });
|
|
66
|
+
});
|
|
67
|
+
it('static: equal value is Applied but not Changed', async () => {
|
|
68
|
+
const [c] = await run({ Status: 'Active' }, { Rules: [{ TargetField: 'Status', Source: { Kind: 'static', Value: 'Active' } }] });
|
|
69
|
+
expect(c).toMatchObject({ Changed: false, Applied: true });
|
|
70
|
+
});
|
|
71
|
+
it('field: copies another field', async () => {
|
|
72
|
+
const [c] = await run({ A: 'x', B: 'y' }, { Rules: [{ TargetField: 'A', Source: { Kind: 'field', Field: 'B' } }] });
|
|
73
|
+
expect(c.NewValue).toBe('y');
|
|
74
|
+
});
|
|
75
|
+
it('formula: computes from record fields', async () => {
|
|
76
|
+
const [c] = await run({ FirstName: 'Ada', LastName: 'Lovelace', FullName: '' }, { Rules: [{ TargetField: 'FullName', Source: { Kind: 'formula', Expression: "fields.FirstName + ' ' + fields.LastName" } }] });
|
|
77
|
+
expect(c.NewValue).toBe('Ada Lovelace');
|
|
78
|
+
});
|
|
79
|
+
it('lookup: resolves via the injected resolver and passes through the matched value', async () => {
|
|
80
|
+
const seen = [];
|
|
81
|
+
const [c] = await run({ CompanyName: 'Acme', CompanyID: null }, { Rules: [{ TargetField: 'CompanyID', Source: { Kind: 'lookup', Lookup: { Entity: 'Accounts', MatchField: 'Name', MatchValue: { Kind: 'field', Field: 'CompanyName' }, ReturnField: 'ID' } } }] }, async (l) => { seen.push(l); return 'acct-123'; });
|
|
82
|
+
expect(c.NewValue).toBe('acct-123');
|
|
83
|
+
expect(seen[0]).toEqual({ Entity: 'Accounts', MatchField: 'Name', MatchValue: 'Acme', ReturnField: 'ID' });
|
|
84
|
+
});
|
|
85
|
+
it('lookup: falls back to Default when the resolver returns undefined', async () => {
|
|
86
|
+
const [c] = await run({ Name: 'x', RegionID: null }, { Rules: [{ TargetField: 'RegionID', Source: { Kind: 'lookup', Lookup: { Entity: 'Regions', MatchField: 'Name', MatchValue: { Kind: 'static', Value: 'Nowhere' }, ReturnField: 'ID', Default: 'unknown' } } }] }, async () => undefined);
|
|
87
|
+
expect(c.NewValue).toBe('unknown');
|
|
88
|
+
});
|
|
89
|
+
it('lookup: errors clearly when no resolver is provided', async () => {
|
|
90
|
+
const [c] = await run({ X: null }, { Rules: [{ TargetField: 'X', Source: { Kind: 'lookup', Lookup: { Entity: 'E', MatchField: 'F', MatchValue: { Kind: 'static', Value: 1 }, ReturnField: 'ID' } } }] });
|
|
91
|
+
expect(c.Applied).toBe(false);
|
|
92
|
+
expect(c.Error).toMatch(/no LookupResolver/);
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
describe('conditions', () => {
|
|
96
|
+
it('applies the rule only when the condition is true', async () => {
|
|
97
|
+
const ruleSet = { Rules: [{ TargetField: 'Tier', Source: { Kind: 'static', Value: 'Gold' }, Condition: 'Revenue > 1000' }] };
|
|
98
|
+
const [hot] = await run({ Revenue: 5000, Tier: 'Bronze' }, ruleSet);
|
|
99
|
+
const [cold] = await run({ Revenue: 10, Tier: 'Bronze' }, ruleSet);
|
|
100
|
+
expect(hot).toMatchObject({ Applied: true, NewValue: 'Gold' });
|
|
101
|
+
expect(cold).toMatchObject({ Applied: false, SkipReason: expect.stringMatching(/condition/) });
|
|
102
|
+
});
|
|
103
|
+
it('reports a condition expression error without throwing', async () => {
|
|
104
|
+
const [c] = await run({ X: 1 }, { Rules: [{ TargetField: 'X', Source: { Kind: 'static', Value: 2 }, Condition: 'this is not valid ===' }] });
|
|
105
|
+
expect(c.Applied).toBe(false);
|
|
106
|
+
expect(c.Error).toMatch(/condition/);
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
describe('transform pipeline on the computed value', () => {
|
|
110
|
+
it('chains transforms (formula → coerce → custom)', async () => {
|
|
111
|
+
const [c] = await run({ Price: '19.99', Total: 0 }, { Rules: [{
|
|
112
|
+
TargetField: 'Total',
|
|
113
|
+
Source: { Kind: 'field', Field: 'Price' },
|
|
114
|
+
Transforms: [
|
|
115
|
+
{ Type: 'coerce', Config: { TargetType: 'number' } },
|
|
116
|
+
{ Type: 'custom', Config: { Expression: 'value * 2' } },
|
|
117
|
+
],
|
|
118
|
+
}] });
|
|
119
|
+
expect(c.NewValue).toBeCloseTo(39.98);
|
|
120
|
+
});
|
|
121
|
+
it('OnError=Null nulls the field when a transform throws', async () => {
|
|
122
|
+
const [c] = await run({ Amount: 'not-a-number', Out: 'x' }, { Rules: [{ TargetField: 'Out', Source: { Kind: 'field', Field: 'Amount' }, Transforms: [{ Type: 'coerce', Config: { TargetType: 'number' }, OnError: 'Null' }] }] });
|
|
123
|
+
expect(c.NewValue).toBeNull();
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
it('computes ALL rules independently (multi-rule preview)', async () => {
|
|
127
|
+
const changes = await run({ A: 1, B: 2, C: 3 }, { Rules: [
|
|
128
|
+
{ TargetField: 'A', Source: { Kind: 'static', Value: 10 } },
|
|
129
|
+
{ TargetField: 'B', Source: { Kind: 'static', Value: 2 } }, // unchanged
|
|
130
|
+
{ TargetField: 'C', Source: { Kind: 'static', Value: 30 }, Condition: 'A == 1' },
|
|
131
|
+
] });
|
|
132
|
+
expect(changes.map((c) => [c.Field, c.Changed, c.Applied])).toEqual([
|
|
133
|
+
['A', true, true],
|
|
134
|
+
['B', false, true],
|
|
135
|
+
['C', true, true],
|
|
136
|
+
]);
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
describe('FieldTransformEngine (each transform type)', () => {
|
|
140
|
+
const e = new FieldTransformEngine();
|
|
141
|
+
const step = (value, ...steps) => e.ExecutePipeline(value, {}, steps).Value;
|
|
142
|
+
it('regex / split / substring', () => {
|
|
143
|
+
expect(step('a-b-c', { Type: 'regex', Config: { Pattern: '-', Replacement: '_', Flags: 'g' } })).toBe('a_b_c');
|
|
144
|
+
expect(step('a,b,c', { Type: 'split', Config: { Delimiter: ',', Index: 1 } })).toBe('b');
|
|
145
|
+
expect(step('hello', { Type: 'substring', Config: { Start: 1, Length: 3 } })).toBe('ell');
|
|
146
|
+
});
|
|
147
|
+
it('combine pulls from fields', () => {
|
|
148
|
+
expect(e.ExecutePipeline(null, { A: 'x', B: 'y' }, [{ Type: 'combine', Config: { SourceFields: ['A', 'B'], Separator: '-' } }]).Value).toBe('x-y');
|
|
149
|
+
});
|
|
150
|
+
it('lookup (in-memory map, case-insensitive) + default', () => {
|
|
151
|
+
expect(step('ACTIVE', { Type: 'lookup', Config: { Map: { active: 1 }, Default: 0 } })).toBe(1);
|
|
152
|
+
expect(step('other', { Type: 'lookup', Config: { Map: { active: 1 }, Default: 0 } })).toBe(0);
|
|
153
|
+
});
|
|
154
|
+
it('coerce number/boolean/string', () => {
|
|
155
|
+
expect(step('42', { Type: 'coerce', Config: { TargetType: 'number' } })).toBe(42);
|
|
156
|
+
expect(step('yes', { Type: 'coerce', Config: { TargetType: 'boolean' } })).toBe(true);
|
|
157
|
+
expect(step(7, { Type: 'coerce', Config: { TargetType: 'string' } })).toBe('7');
|
|
158
|
+
});
|
|
159
|
+
it('OnError=Skip drops the field; OnError=Fail propagates', () => {
|
|
160
|
+
expect(e.ExecutePipeline('x', {}, [{ Type: 'coerce', Config: { TargetType: 'number' }, OnError: 'Skip' }]).Skipped).toBe(true);
|
|
161
|
+
expect(() => e.ExecutePipeline('x', {}, [{ Type: 'coerce', Config: { TargetType: 'number' }, OnError: 'Fail' }])).toThrow();
|
|
162
|
+
});
|
|
163
|
+
it('a malformed custom expression compiles once and is reported', () => {
|
|
164
|
+
expect(() => e.ExecutePipeline('x', {}, [{ Type: 'custom', Config: { Expression: ')(' }, OnError: 'Fail' }])).toThrow();
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
//# sourceMappingURL=FieldRulesEvaluator.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FieldRulesEvaluator.test.js","sourceRoot":"","sources":["../../src/__tests__/FieldRulesEvaluator.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"KeyedSerialTaskQueue.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/KeyedSerialTaskQueue.test.ts"],"names":[],"mappings":""}
|