@memberjunction/global 5.42.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.
Files changed (42) hide show
  1. package/README.md +89 -0
  2. package/dist/KeyedSerialTaskQueue.d.ts +60 -0
  3. package/dist/KeyedSerialTaskQueue.d.ts.map +1 -0
  4. package/dist/KeyedSerialTaskQueue.js +76 -0
  5. package/dist/KeyedSerialTaskQueue.js.map +1 -0
  6. package/dist/__tests__/FieldRulesEvaluator.test.d.ts +2 -0
  7. package/dist/__tests__/FieldRulesEvaluator.test.d.ts.map +1 -0
  8. package/dist/__tests__/FieldRulesEvaluator.test.js +167 -0
  9. package/dist/__tests__/FieldRulesEvaluator.test.js.map +1 -0
  10. package/dist/__tests__/KeyedSerialTaskQueue.test.d.ts +2 -0
  11. package/dist/__tests__/KeyedSerialTaskQueue.test.d.ts.map +1 -0
  12. package/dist/__tests__/KeyedSerialTaskQueue.test.js +91 -0
  13. package/dist/__tests__/KeyedSerialTaskQueue.test.js.map +1 -0
  14. package/dist/fieldRules/FieldRulesEvaluator.d.ts +34 -0
  15. package/dist/fieldRules/FieldRulesEvaluator.d.ts.map +1 -0
  16. package/dist/fieldRules/FieldRulesEvaluator.js +130 -0
  17. package/dist/fieldRules/FieldRulesEvaluator.js.map +1 -0
  18. package/dist/fieldRules/FieldTransformEngine.d.ts +44 -0
  19. package/dist/fieldRules/FieldTransformEngine.d.ts.map +1 -0
  20. package/dist/fieldRules/FieldTransformEngine.js +189 -0
  21. package/dist/fieldRules/FieldTransformEngine.js.map +1 -0
  22. package/dist/fieldRules/index.d.ts +6 -0
  23. package/dist/fieldRules/index.d.ts.map +1 -0
  24. package/dist/fieldRules/index.js +6 -0
  25. package/dist/fieldRules/index.js.map +1 -0
  26. package/dist/fieldRules/rules.d.ts +135 -0
  27. package/dist/fieldRules/rules.d.ts.map +1 -0
  28. package/dist/fieldRules/rules.js +2 -0
  29. package/dist/fieldRules/rules.js.map +1 -0
  30. package/dist/fieldRules/transformRegistry.d.ts +22 -0
  31. package/dist/fieldRules/transformRegistry.d.ts.map +1 -0
  32. package/dist/fieldRules/transformRegistry.js +33 -0
  33. package/dist/fieldRules/transformRegistry.js.map +1 -0
  34. package/dist/fieldRules/transforms.d.ts +102 -0
  35. package/dist/fieldRules/transforms.d.ts.map +1 -0
  36. package/dist/fieldRules/transforms.js +8 -0
  37. package/dist/fieldRules/transforms.js.map +1 -0
  38. package/dist/index.d.ts +2 -0
  39. package/dist/index.d.ts.map +1 -1
  40. package/dist/index.js +2 -0
  41. package/dist/index.js.map +1 -1
  42. package/package.json +1 -1
@@ -0,0 +1,130 @@
1
+ import { SafeExpressionEvaluator } from '../SafeExpressionEvaluator.js';
2
+ import { FieldTransformEngine } from './FieldTransformEngine.js';
3
+ /**
4
+ * Computes the field changes a {@link FieldRuleSet} would make to a record — WITHOUT mutating anything.
5
+ *
6
+ * Returning a list of {@link FieldChange} (old → new per rule) is what makes a dry-run preview possible:
7
+ * the caller decides whether to apply. Conditions are evaluated with the safe expression evaluator;
8
+ * value formulas + the transform pipeline run through the shared {@link FieldTransformEngine}; entity
9
+ * lookups go through the injected resolver. The engine never touches a database itself.
10
+ */
11
+ export class FieldRulesEvaluator {
12
+ constructor(options) {
13
+ this.transforms = new FieldTransformEngine();
14
+ this.conditions = new SafeExpressionEvaluator();
15
+ this.lookupResolver = options?.LookupResolver;
16
+ this.promptResolver = options?.PromptResolver;
17
+ this.entityDocumentResolver = options?.EntityDocumentResolver;
18
+ }
19
+ /** Computes one {@link FieldChange} per rule for a record (a row of field name → current value). */
20
+ async ComputeChanges(record, ruleSet) {
21
+ const changes = [];
22
+ for (const rule of ruleSet.Rules) {
23
+ changes.push(await this.evaluateRule(record, rule));
24
+ }
25
+ return changes;
26
+ }
27
+ async evaluateRule(record, rule) {
28
+ const oldValue = record[rule.TargetField];
29
+ const unchanged = (extra) => ({
30
+ Field: rule.TargetField, OldValue: oldValue, NewValue: oldValue, Changed: false, Applied: false, ...extra,
31
+ });
32
+ const condition = this.checkCondition(record, rule);
33
+ if (condition.error)
34
+ return unchanged({ Error: condition.error });
35
+ if (!condition.passed)
36
+ return unchanged({ SkipReason: 'condition evaluated false' });
37
+ let value;
38
+ try {
39
+ value = await this.resolveSource(record, rule.Source);
40
+ }
41
+ catch (err) {
42
+ return unchanged({ Error: `source: ${this.message(err)}` });
43
+ }
44
+ if (rule.Transforms?.length) {
45
+ try {
46
+ const result = this.transforms.ExecutePipeline(value, record, rule.Transforms);
47
+ if (result.Skipped)
48
+ return unchanged({ SkipReason: 'transform skipped the field' });
49
+ value = result.Value;
50
+ }
51
+ catch (err) {
52
+ return unchanged({ Error: `transform: ${this.message(err)}` });
53
+ }
54
+ }
55
+ return {
56
+ Field: rule.TargetField,
57
+ OldValue: oldValue,
58
+ NewValue: value,
59
+ Changed: !this.equal(oldValue, value),
60
+ Applied: true,
61
+ };
62
+ }
63
+ checkCondition(record, rule) {
64
+ if (!rule.Condition) {
65
+ return { passed: true };
66
+ }
67
+ const result = this.conditions.evaluate(rule.Condition, record);
68
+ if (!result.success) {
69
+ return { passed: false, error: `condition: ${result.error ?? 'invalid expression'}` };
70
+ }
71
+ return { passed: result.value === true };
72
+ }
73
+ async resolveSource(record, source) {
74
+ switch (source.Kind) {
75
+ case 'static':
76
+ return source.Value;
77
+ case 'field':
78
+ return record[source.Field];
79
+ case 'formula':
80
+ return this.transforms.Evaluate(source.Expression, undefined, record);
81
+ case 'lookup': {
82
+ if (!this.lookupResolver) {
83
+ throw new Error('a lookup rule was used but no LookupResolver was provided');
84
+ }
85
+ const matchValue = await this.resolveSource(record, source.Lookup.MatchValue);
86
+ const resolved = await this.lookupResolver({
87
+ Entity: source.Lookup.Entity,
88
+ MatchField: source.Lookup.MatchField,
89
+ MatchValue: matchValue,
90
+ ReturnField: source.Lookup.ReturnField,
91
+ });
92
+ return resolved === undefined ? (source.Lookup.Default ?? null) : resolved;
93
+ }
94
+ case 'prompt': {
95
+ if (!this.promptResolver) {
96
+ throw new Error('a prompt rule was used but no PromptResolver was provided');
97
+ }
98
+ // By default hand the WHOLE record to the prompt; otherwise resolve each shaped Data entry
99
+ // through the same source machinery (so Data values can be fields, formulas, statics, …).
100
+ let data = record;
101
+ if (source.Prompt.Data) {
102
+ data = {};
103
+ for (const [key, valueSource] of Object.entries(source.Prompt.Data)) {
104
+ data[key] = await this.resolveSource(record, valueSource);
105
+ }
106
+ }
107
+ return this.promptResolver({ PromptID: source.Prompt.PromptID, Data: data });
108
+ }
109
+ case 'entityDocument': {
110
+ if (!this.entityDocumentResolver) {
111
+ throw new Error('an entityDocument rule was used but no EntityDocumentResolver was provided');
112
+ }
113
+ return this.entityDocumentResolver({ EntityDocumentID: source.EntityDocument?.EntityDocumentID, Record: record });
114
+ }
115
+ }
116
+ }
117
+ equal(a, b) {
118
+ if (a === b)
119
+ return true;
120
+ if (a instanceof Date && b instanceof Date)
121
+ return a.getTime() === b.getTime();
122
+ if (a == null && b == null)
123
+ return true;
124
+ return false;
125
+ }
126
+ message(err) {
127
+ return err instanceof Error ? err.message : String(err);
128
+ }
129
+ }
130
+ //# sourceMappingURL=FieldRulesEvaluator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FieldRulesEvaluator.js","sourceRoot":"","sources":["../../src/fieldRules/FieldRulesEvaluator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ import type { TransformStep, TransformStepResult } from './transforms.js';
2
+ /** Signature of a compiled custom/formula expression: `(value, fields) => result`. */
3
+ export type CompiledExpression = (value: unknown, fields: Record<string, unknown>) => unknown;
4
+ /**
5
+ * Framework-agnostic engine that runs a field-value transform pipeline over a single `(value, fields)`
6
+ * pair. Lifted from the MemberJunction integration field-mapping engine so the integration sync AND the
7
+ * rules-based bulk-update processor share one implementation of every transform type.
8
+ *
9
+ * Holds an LRU cache of compiled custom/formula expressions (an identical expression compiles once, not
10
+ * once per record). A failed compile is cached as the `Error` it threw and re-thrown from cache, so a
11
+ * malformed expression is compiled exactly once across a whole batch.
12
+ */
13
+ export declare class FieldTransformEngine {
14
+ private readonly compiledExpressionCache;
15
+ /**
16
+ * Runs `value` through every step of `steps`, returning the final value (or `Skipped` when an
17
+ * OnError='Skip' step drops the field). `fields` is the full record, available to combine/custom steps.
18
+ */
19
+ ExecutePipeline(value: unknown, fields: Record<string, unknown>, steps: TransformStep[]): TransformStepResult;
20
+ /**
21
+ * Executes a single transform step, applying the configured transformation and handling errors
22
+ * according to the step's OnError strategy (default 'Null' — grace: null the field, keep going).
23
+ */
24
+ ExecuteStep(step: TransformStep, value: unknown, fields: Record<string, unknown>): TransformStepResult;
25
+ /**
26
+ * Evaluates a value-producing expression against the record. Reuses the compiled-expression cache.
27
+ * The expression sees the current field value as `value` and all record fields as `fields`
28
+ * (e.g. `fields.FirstName + ' ' + fields.LastName`).
29
+ */
30
+ Evaluate(expression: string, value: unknown, fields: Record<string, unknown>): unknown;
31
+ private dispatch;
32
+ private handleError;
33
+ private applyDirect;
34
+ private applyRegex;
35
+ private applySplit;
36
+ private applyCombine;
37
+ private applyLookup;
38
+ private applyFormat;
39
+ private applyCoerce;
40
+ private applySubstring;
41
+ private getCompiled;
42
+ private compile;
43
+ }
44
+ //# sourceMappingURL=FieldTransformEngine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FieldTransformEngine.d.ts","sourceRoot":"","sources":["../../src/fieldRules/FieldTransformEngine.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACR,aAAa,EAEb,mBAAmB,EAUtB,MAAM,cAAc,CAAC;AAEtB,sFAAsF;AACtF,MAAM,MAAM,kBAAkB,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC;AAE9F;;;;;;;;GAQG;AACH,qBAAa,oBAAoB;IAC7B,OAAO,CAAC,QAAQ,CAAC,uBAAuB,CAAwD;IAEhG;;;OAGG;IACI,eAAe,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,EAAE,aAAa,EAAE,GAAG,mBAAmB;IAYpH;;;OAGG;IACI,WAAW,CAAC,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,mBAAmB;IAS7G;;;;OAIG;IACI,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO;IAI7F,OAAO,CAAC,QAAQ;IAoChB,OAAO,CAAC,WAAW;IAWnB,OAAO,CAAC,WAAW;IAInB,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,WAAW;IAMnB,OAAO,CAAC,WAAW;IAoBnB,OAAO,CAAC,WAAW;IA6BnB,OAAO,CAAC,cAAc;IAKtB,OAAO,CAAC,WAAW;IAYnB,OAAO,CAAC,OAAO;CAQlB"}
@@ -0,0 +1,189 @@
1
+ import { MJLruCache } from '../MJLruCache.js';
2
+ import { GetFieldTransform } from './transformRegistry.js';
3
+ /**
4
+ * Framework-agnostic engine that runs a field-value transform pipeline over a single `(value, fields)`
5
+ * pair. Lifted from the MemberJunction integration field-mapping engine so the integration sync AND the
6
+ * rules-based bulk-update processor share one implementation of every transform type.
7
+ *
8
+ * Holds an LRU cache of compiled custom/formula expressions (an identical expression compiles once, not
9
+ * once per record). A failed compile is cached as the `Error` it threw and re-thrown from cache, so a
10
+ * malformed expression is compiled exactly once across a whole batch.
11
+ */
12
+ export class FieldTransformEngine {
13
+ constructor() {
14
+ this.compiledExpressionCache = new MJLruCache();
15
+ }
16
+ /**
17
+ * Runs `value` through every step of `steps`, returning the final value (or `Skipped` when an
18
+ * OnError='Skip' step drops the field). `fields` is the full record, available to combine/custom steps.
19
+ */
20
+ ExecutePipeline(value, fields, steps) {
21
+ let current = value;
22
+ for (const step of steps) {
23
+ const result = this.ExecuteStep(step, current, fields);
24
+ if (result.Skipped) {
25
+ return result;
26
+ }
27
+ current = result.Value;
28
+ }
29
+ return { Value: current, Skipped: false };
30
+ }
31
+ /**
32
+ * Executes a single transform step, applying the configured transformation and handling errors
33
+ * according to the step's OnError strategy (default 'Null' — grace: null the field, keep going).
34
+ */
35
+ ExecuteStep(step, value, fields) {
36
+ const onError = step.OnError ?? 'Null';
37
+ try {
38
+ return { Value: this.dispatch(step, value, fields), Skipped: false };
39
+ }
40
+ catch (err) {
41
+ return this.handleError(onError, err);
42
+ }
43
+ }
44
+ /**
45
+ * Evaluates a value-producing expression against the record. Reuses the compiled-expression cache.
46
+ * The expression sees the current field value as `value` and all record fields as `fields`
47
+ * (e.g. `fields.FirstName + ' ' + fields.LastName`).
48
+ */
49
+ Evaluate(expression, value, fields) {
50
+ return this.getCompiled(expression)(value, fields);
51
+ }
52
+ dispatch(step, value, fields) {
53
+ switch (step.Type) {
54
+ case 'direct':
55
+ return this.applyDirect(value, step.Config);
56
+ case 'regex':
57
+ return this.applyRegex(value, step.Config);
58
+ case 'split':
59
+ return this.applySplit(value, step.Config);
60
+ case 'combine':
61
+ return this.applyCombine(fields, step.Config);
62
+ case 'lookup':
63
+ return this.applyLookup(value, step.Config);
64
+ case 'format':
65
+ return this.applyFormat(value, step.Config);
66
+ case 'coerce':
67
+ return this.applyCoerce(value, step.Config);
68
+ case 'substring':
69
+ return this.applySubstring(value, step.Config);
70
+ case 'custom':
71
+ try {
72
+ return this.Evaluate(step.Config.Expression, value, fields);
73
+ }
74
+ catch (err) {
75
+ throw new Error(`Custom transform expression failed: ${err instanceof Error ? err.message : String(err)}`);
76
+ }
77
+ default: {
78
+ // Not a built-in — dispatch to a registered plugin (e.g. 'jsonpath' / 'xpath', whose
79
+ // libraries must not be deps of this package). See {@link RegisterFieldTransform}.
80
+ const plugin = GetFieldTransform(step.Type);
81
+ if (plugin) {
82
+ return plugin(value, fields, step.Config);
83
+ }
84
+ throw new Error(`Unknown transform type '${step.Type}'. If it is a plugin transform (e.g. 'jsonpath'/'xpath'), import the package that registers it before running.`);
85
+ }
86
+ }
87
+ }
88
+ handleError(onError, err) {
89
+ switch (onError) {
90
+ case 'Skip':
91
+ return { Value: undefined, Skipped: true };
92
+ case 'Null':
93
+ return { Value: null, Skipped: false };
94
+ case 'Fail':
95
+ throw err;
96
+ }
97
+ }
98
+ applyDirect(value, config) {
99
+ return value == null && config.DefaultValue !== undefined ? config.DefaultValue : value;
100
+ }
101
+ applyRegex(value, config) {
102
+ return String(value ?? '').replace(new RegExp(config.Pattern, config.Flags ?? ''), config.Replacement);
103
+ }
104
+ applySplit(value, config) {
105
+ return String(value ?? '').split(config.Delimiter)[config.Index] ?? null;
106
+ }
107
+ applyCombine(fields, config) {
108
+ return config.SourceFields.map((f) => String(fields[f] ?? '')).join(config.Separator);
109
+ }
110
+ applyLookup(value, config) {
111
+ const lowerKey = String(value ?? '').toLowerCase();
112
+ const entry = Object.entries(config.Map).find(([key]) => key.toLowerCase() === lowerKey);
113
+ return entry ? entry[1] : (config.Default ?? null);
114
+ }
115
+ applyFormat(value, config) {
116
+ if (config.FormatType === 'date') {
117
+ const dateVal = value instanceof Date ? value : new Date(String(value));
118
+ if (isNaN(dateVal.getTime())) {
119
+ throw new Error(`Cannot format "${String(value)}" as a date`);
120
+ }
121
+ return config.FormatString.toLowerCase() === 'iso' || config.FormatString === 'ISO8601'
122
+ ? dateVal.toISOString()
123
+ : dateVal.toLocaleDateString('en-US');
124
+ }
125
+ if (config.FormatType === 'number') {
126
+ const numVal = Number(value);
127
+ if (!Number.isFinite(numVal)) {
128
+ throw new Error(`Cannot format "${String(value)}" as a number`);
129
+ }
130
+ return numVal.toFixed(Number(config.FormatString) || 0);
131
+ }
132
+ return String(value ?? '');
133
+ }
134
+ applyCoerce(value, config) {
135
+ switch (config.TargetType) {
136
+ case 'string':
137
+ return String(value ?? '');
138
+ case 'number': {
139
+ const num = Number(value);
140
+ if (isNaN(num)) {
141
+ throw new Error(`Cannot coerce "${String(value)}" to number`);
142
+ }
143
+ return num;
144
+ }
145
+ case 'boolean': {
146
+ if (typeof value === 'boolean')
147
+ return value;
148
+ if (typeof value === 'number')
149
+ return value !== 0;
150
+ const s = String(value).toLowerCase().trim();
151
+ return s === 'true' || s === '1' || s === 'yes';
152
+ }
153
+ case 'date': {
154
+ const d = new Date(String(value));
155
+ if (isNaN(d.getTime())) {
156
+ throw new Error(`Cannot coerce "${String(value)}" to date`);
157
+ }
158
+ return d;
159
+ }
160
+ default:
161
+ throw new Error(`Unknown coerce target type: ${config.TargetType}`);
162
+ }
163
+ }
164
+ applySubstring(value, config) {
165
+ const s = String(value ?? '');
166
+ return config.Length !== undefined ? s.substring(config.Start, config.Start + config.Length) : s.substring(config.Start);
167
+ }
168
+ getCompiled(expression) {
169
+ let cached = this.compiledExpressionCache.Get(expression);
170
+ if (cached === undefined) {
171
+ cached = this.compile(expression);
172
+ this.compiledExpressionCache.Set(expression, cached);
173
+ }
174
+ if (cached instanceof Error) {
175
+ throw cached;
176
+ }
177
+ return cached;
178
+ }
179
+ compile(expression) {
180
+ try {
181
+ // eslint-disable-next-line @typescript-eslint/no-implied-eval
182
+ return new Function('value', 'fields', `return (${expression});`);
183
+ }
184
+ catch (err) {
185
+ return err instanceof Error ? err : new Error(String(err));
186
+ }
187
+ }
188
+ }
189
+ //# sourceMappingURL=FieldTransformEngine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FieldTransformEngine.js","sourceRoot":"","sources":["../../src/fieldRules/FieldTransformEngine.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export * from './transforms.js';
2
+ export * from './transformRegistry.js';
3
+ export * from './FieldTransformEngine.js';
4
+ export * from './rules.js';
5
+ export * from './FieldRulesEvaluator.js';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/fieldRules/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,qBAAqB,CAAC;AACpC,cAAc,wBAAwB,CAAC;AACvC,cAAc,SAAS,CAAC;AACxB,cAAc,uBAAuB,CAAC"}
@@ -0,0 +1,6 @@
1
+ export * from './transforms.js';
2
+ export * from './transformRegistry.js';
3
+ export * from './FieldTransformEngine.js';
4
+ export * from './rules.js';
5
+ export * from './FieldRulesEvaluator.js';
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/fieldRules/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,qBAAqB,CAAC;AACpC,cAAc,wBAAwB,CAAC;AACvC,cAAc,SAAS,CAAC;AACxB,cAAc,uBAAuB,CAAC"}
@@ -0,0 +1,135 @@
1
+ import type { TransformStep } from './transforms.js';
2
+ /**
3
+ * How a field rule produces its base value (before any transform pipeline runs):
4
+ * - `static` — a literal constant
5
+ * - `field` — copy another field's current value
6
+ * - `formula` — a JS value expression over the record (`fields.X`), e.g. `fields.FirstName + ' ' + fields.LastName`
7
+ * - `lookup` — resolve a value from another entity by matching a key (requires a LookupResolver)
8
+ * - `prompt` — run an AI prompt over the record and use its output (requires a PromptResolver). By
9
+ * default the WHOLE record is passed as the prompt's data; supply `Data` to send a narrower / shaped
10
+ * context (e.g. a rendered entity-document of the record). Powerful but one model call PER record —
11
+ * use the dry-run preview + bounded concurrency, and prefer it for enrichment/summarization fields.
12
+ */
13
+ export type FieldRuleValueSource = {
14
+ Kind: 'static';
15
+ Value: unknown;
16
+ } | {
17
+ Kind: 'field';
18
+ Field: string;
19
+ } | {
20
+ Kind: 'formula';
21
+ Expression: string;
22
+ } | {
23
+ Kind: 'lookup';
24
+ Lookup: FieldRuleLookup;
25
+ } | {
26
+ Kind: 'prompt';
27
+ Prompt: FieldRulePrompt;
28
+ } | {
29
+ Kind: 'entityDocument';
30
+ EntityDocument?: FieldRuleEntityDocument;
31
+ };
32
+ /**
33
+ * Renders the record as text via an MJ Entity Document template (the same templates used for
34
+ * embeddings/RAG), using the existing rendering infrastructure through an injected
35
+ * {@link EntityDocumentResolver}. Most useful inside a `prompt` source's `Data` — hand the LLM a rich,
36
+ * curated rendering of the record instead of raw fields.
37
+ */
38
+ export interface FieldRuleEntityDocument {
39
+ /** Which Entity Document template to render. Omit to use the entity's default/primary document. */
40
+ EntityDocumentID?: string;
41
+ }
42
+ /** An AI prompt source: run a prompt over the record and use its output as the field value. */
43
+ export interface FieldRulePrompt {
44
+ /** The AI prompt to run (its ID). */
45
+ PromptID: string;
46
+ /**
47
+ * Optional shaping of the data handed to the prompt. Omit to pass the whole record. Each entry's
48
+ * value is computed like any other source (`field` / `formula` / `static`), so you can pass selected
49
+ * fields, derived values, or a pre-rendered entity-document string under named keys.
50
+ */
51
+ Data?: Record<string, FieldRuleValueSource>;
52
+ }
53
+ /** An entity lookup: find a record where MatchField === (computed) MatchValue, return ReturnField. */
54
+ export interface FieldRuleLookup {
55
+ /** Entity to look in (e.g. 'Accounts'). */
56
+ Entity: string;
57
+ /** Field on the lookup entity to match against. */
58
+ MatchField: string;
59
+ /** How to compute the value to match (static / field / formula). */
60
+ MatchValue: FieldRuleValueSource;
61
+ /** Field to return from the matched record. */
62
+ ReturnField: string;
63
+ /** Value to use when no record matches. */
64
+ Default?: unknown;
65
+ }
66
+ /** A single rule: set TargetField from Source (optionally transformed), gated by an optional Condition. */
67
+ export interface FieldRule {
68
+ /** The field on the record to set. */
69
+ TargetField: string;
70
+ /** How to produce the base value. */
71
+ Source: FieldRuleValueSource;
72
+ /** Optional transform pipeline applied to the computed value. */
73
+ Transforms?: TransformStep[];
74
+ /** Optional boolean expression over the record — the rule only applies when it evaluates true. */
75
+ Condition?: string;
76
+ }
77
+ /** An ordered set of field rules applied to each record. */
78
+ export interface FieldRuleSet {
79
+ Rules: FieldRule[];
80
+ }
81
+ /** The computed outcome of one rule for one record — the unit of a dry-run preview. */
82
+ export interface FieldChange {
83
+ /** The target field. */
84
+ Field: string;
85
+ /** The record's current value. */
86
+ OldValue: unknown;
87
+ /** The computed new value (equals OldValue when not Applied). */
88
+ NewValue: unknown;
89
+ /** True when NewValue differs from OldValue. */
90
+ Changed: boolean;
91
+ /** True when the rule produced a value to write (condition passed, no skip/error). */
92
+ Applied: boolean;
93
+ /** Why the rule did not apply (condition false / transform skipped). */
94
+ SkipReason?: string;
95
+ /** Set when computing the value errored (condition or source/transform). */
96
+ Error?: string;
97
+ }
98
+ /** A lookup with its MatchValue already resolved to a concrete value, handed to the LookupResolver. */
99
+ export interface ResolvedLookup {
100
+ Entity: string;
101
+ MatchField: string;
102
+ MatchValue: unknown;
103
+ ReturnField: string;
104
+ }
105
+ /**
106
+ * Resolves an entity lookup to a value. Injected into the evaluator so the engine stays framework-pure
107
+ * (the bulk-update processor supplies a RunView-backed resolver; unit tests supply a stub).
108
+ * Should return the matched record's ReturnField value, or `undefined` when nothing matches.
109
+ */
110
+ export type LookupResolver = (lookup: ResolvedLookup) => Promise<unknown>;
111
+ /** A prompt source with its data already resolved to concrete values, handed to the PromptResolver. */
112
+ export interface ResolvedPrompt {
113
+ PromptID: string;
114
+ /** The data context for the prompt run (the whole record by default, or the shaped `Data` map). */
115
+ Data: Record<string, unknown>;
116
+ }
117
+ /**
118
+ * Runs an AI prompt and returns its output value. Injected into the evaluator so the engine itself never
119
+ * depends on the AI stack — the bulk-update processor supplies a real `AIPromptRunner`-backed resolver;
120
+ * unit tests supply a stub. This is the same dependency-inversion seam as {@link LookupResolver}.
121
+ */
122
+ export type PromptResolver = (prompt: ResolvedPrompt) => Promise<unknown>;
123
+ /** An entity-document source with its record handed to the {@link EntityDocumentResolver} to render. */
124
+ export interface ResolvedEntityDocument {
125
+ EntityDocumentID?: string;
126
+ /** The record's field values, to render through the Entity Document template. */
127
+ Record: Record<string, unknown>;
128
+ }
129
+ /**
130
+ * Renders a record to text via the existing Entity Document / Template infrastructure. Injected so the
131
+ * engine never depends on `@memberjunction/templates` or the AI/vector stack — the bulk-update processor
132
+ * supplies a resolver backed by the real render infra. Same dependency-inversion seam as the others.
133
+ */
134
+ export type EntityDocumentResolver = (doc: ResolvedEntityDocument) => Promise<string>;
135
+ //# sourceMappingURL=rules.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rules.d.ts","sourceRoot":"","sources":["../../src/fieldRules/rules.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAElD;;;;;;;;;;GAUG;AACH,MAAM,MAAM,oBAAoB,GAC1B;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,GAClC;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAChC;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GACvC;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,eAAe,CAAA;CAAE,GAC3C;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,eAAe,CAAA;CAAE,GAC3C;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,cAAc,CAAC,EAAE,uBAAuB,CAAA;CAAE,CAAC;AAE3E;;;;;GAKG;AACH,MAAM,WAAW,uBAAuB;IACpC,mGAAmG;IACnG,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,+FAA+F;AAC/F,MAAM,WAAW,eAAe;IAC5B,qCAAqC;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;CAC/C;AAED,sGAAsG;AACtG,MAAM,WAAW,eAAe;IAC5B,2CAA2C;IAC3C,MAAM,EAAE,MAAM,CAAC;IACf,mDAAmD;IACnD,UAAU,EAAE,MAAM,CAAC;IACnB,oEAAoE;IACpE,UAAU,EAAE,oBAAoB,CAAC;IACjC,+CAA+C;IAC/C,WAAW,EAAE,MAAM,CAAC;IACpB,2CAA2C;IAC3C,OAAO,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,2GAA2G;AAC3G,MAAM,WAAW,SAAS;IACtB,sCAAsC;IACtC,WAAW,EAAE,MAAM,CAAC;IACpB,qCAAqC;IACrC,MAAM,EAAE,oBAAoB,CAAC;IAC7B,iEAAiE;IACjE,UAAU,CAAC,EAAE,aAAa,EAAE,CAAC;IAC7B,kGAAkG;IAClG,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,4DAA4D;AAC5D,MAAM,WAAW,YAAY;IACzB,KAAK,EAAE,SAAS,EAAE,CAAC;CACtB;AAED,uFAAuF;AACvF,MAAM,WAAW,WAAW;IACxB,wBAAwB;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,kCAAkC;IAClC,QAAQ,EAAE,OAAO,CAAC;IAClB,iEAAiE;IACjE,QAAQ,EAAE,OAAO,CAAC;IAClB,gDAAgD;IAChD,OAAO,EAAE,OAAO,CAAC;IACjB,sFAAsF;IACtF,OAAO,EAAE,OAAO,CAAC;IACjB,wEAAwE;IACxE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4EAA4E;IAC5E,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,uGAAuG;AACvG,MAAM,WAAW,cAAc;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;CACvB;AAED;;;;GAIG;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,MAAM,EAAE,cAAc,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AAE1E,uGAAuG;AACvG,MAAM,WAAW,cAAc;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,mGAAmG;IACnG,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AAED;;;;GAIG;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,MAAM,EAAE,cAAc,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AAE1E,wGAAwG;AACxG,MAAM,WAAW,sBAAsB;IACnC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,iFAAiF;IACjF,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED;;;;GAIG;AACH,MAAM,MAAM,sBAAsB,GAAG,CAAC,GAAG,EAAE,sBAAsB,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=rules.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rules.js","sourceRoot":"","sources":["../../src/fieldRules/rules.ts"],"names":[],"mappings":""}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * A pluggable transform handler: `(value, fields, config) => result`. Same call shape as the built-in
3
+ * transforms, so a plugin is indistinguishable from a built-in at the call site.
4
+ */
5
+ export type FieldTransformPlugin = (value: unknown, fields: Record<string, unknown>, config: unknown) => unknown;
6
+ /**
7
+ * Registers a transform plugin by type name so the {@link FieldTransformEngine} can dispatch a
8
+ * non-built-in transform to it.
9
+ *
10
+ * This is how heavier transforms (e.g. `jsonpath`, `xpath`) are added WITHOUT making their libraries
11
+ * (jsonpath-plus, xpath, @xmldom/xmldom, …) dependencies of this foundational package: a separate
12
+ * package owns the libs, implements the handler, and calls this at module-load to register it. Any
13
+ * `FieldTransformEngine` in the process (integration sync, rules-based bulk update, …) then supports the
14
+ * transform — the engine stays lib-free, the plugin owns the deps. Idempotent per type (last wins).
15
+ *
16
+ * @param type - The transform `Type` string (case-insensitive), e.g. `'jsonpath'`.
17
+ * @param plugin - The handler.
18
+ */
19
+ export declare function RegisterFieldTransform(type: string, plugin: FieldTransformPlugin): void;
20
+ /** Returns the registered plugin for a transform type, or `undefined` if none is registered. */
21
+ export declare function GetFieldTransform(type: string): FieldTransformPlugin | undefined;
22
+ //# sourceMappingURL=transformRegistry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transformRegistry.d.ts","sourceRoot":"","sources":["../../src/fieldRules/transformRegistry.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,OAAO,KAAK,OAAO,CAAC;AAejH;;;;;;;;;;;;GAYG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,oBAAoB,GAAG,IAAI,CAEvF;AAED,gGAAgG;AAChG,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,oBAAoB,GAAG,SAAS,CAEhF"}
@@ -0,0 +1,33 @@
1
+ import { GetGlobalObjectStore } from '../util.js';
2
+ /** Global-object-store key so registrations survive bundlers that duplicate this module. */
3
+ const STORE_KEY = '__mj_FieldTransformPlugins__';
4
+ function registry() {
5
+ const store = (GetGlobalObjectStore() ?? globalThis);
6
+ let map = store[STORE_KEY];
7
+ if (!map) {
8
+ map = new Map();
9
+ store[STORE_KEY] = map;
10
+ }
11
+ return map;
12
+ }
13
+ /**
14
+ * Registers a transform plugin by type name so the {@link FieldTransformEngine} can dispatch a
15
+ * non-built-in transform to it.
16
+ *
17
+ * This is how heavier transforms (e.g. `jsonpath`, `xpath`) are added WITHOUT making their libraries
18
+ * (jsonpath-plus, xpath, @xmldom/xmldom, …) dependencies of this foundational package: a separate
19
+ * package owns the libs, implements the handler, and calls this at module-load to register it. Any
20
+ * `FieldTransformEngine` in the process (integration sync, rules-based bulk update, …) then supports the
21
+ * transform — the engine stays lib-free, the plugin owns the deps. Idempotent per type (last wins).
22
+ *
23
+ * @param type - The transform `Type` string (case-insensitive), e.g. `'jsonpath'`.
24
+ * @param plugin - The handler.
25
+ */
26
+ export function RegisterFieldTransform(type, plugin) {
27
+ registry().set(type.trim().toLowerCase(), plugin);
28
+ }
29
+ /** Returns the registered plugin for a transform type, or `undefined` if none is registered. */
30
+ export function GetFieldTransform(type) {
31
+ return registry().get(type.trim().toLowerCase());
32
+ }
33
+ //# sourceMappingURL=transformRegistry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transformRegistry.js","sourceRoot":"","sources":["../../src/fieldRules/transformRegistry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAQ/C,4FAA4F;AAC5F,MAAM,SAAS,GAAG,8BAA8B,CAAC;AAEjD,SAAS,QAAQ;IACb,MAAM,KAAK,GAAG,CAAC,oBAAoB,EAAE,IAAK,UAAiD,CAA4B,CAAC;IACxH,IAAI,GAAG,GAAG,KAAK,CAAC,SAAS,CAAkD,CAAC;IAC5E,IAAI,CAAC,GAAG,EAAE,CAAC;QACP,GAAG,GAAG,IAAI,GAAG,EAAgC,CAAC;QAC9C,KAAK,CAAC,SAAS,CAAC,GAAG,GAAG,CAAC;IAC3B,CAAC;IACD,OAAO,GAAG,CAAC;AACf,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,sBAAsB,CAAC,IAAY,EAAE,MAA4B;IAC7E,QAAQ,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,MAAM,CAAC,CAAC;AACtD,CAAC;AAED,gGAAgG;AAChG,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC1C,OAAO,QAAQ,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC;AACrD,CAAC"}