@memberjunction/integration-engine 0.0.1 → 5.8.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 (46) hide show
  1. package/README.md +142 -28
  2. package/dist/BaseIntegrationConnector.d.ts +142 -0
  3. package/dist/BaseIntegrationConnector.d.ts.map +1 -0
  4. package/dist/BaseIntegrationConnector.js +91 -0
  5. package/dist/BaseIntegrationConnector.js.map +1 -0
  6. package/dist/ConnectorFactory.d.ts +30 -0
  7. package/dist/ConnectorFactory.d.ts.map +1 -0
  8. package/dist/ConnectorFactory.js +50 -0
  9. package/dist/ConnectorFactory.js.map +1 -0
  10. package/dist/FieldMappingEngine.d.ts +94 -0
  11. package/dist/FieldMappingEngine.d.ts.map +1 -0
  12. package/dist/FieldMappingEngine.js +250 -0
  13. package/dist/FieldMappingEngine.js.map +1 -0
  14. package/dist/IntegrationOrchestrator.d.ts +126 -0
  15. package/dist/IntegrationOrchestrator.d.ts.map +1 -0
  16. package/dist/IntegrationOrchestrator.js +548 -0
  17. package/dist/IntegrationOrchestrator.js.map +1 -0
  18. package/dist/MatchEngine.d.ts +50 -0
  19. package/dist/MatchEngine.d.ts.map +1 -0
  20. package/dist/MatchEngine.js +124 -0
  21. package/dist/MatchEngine.js.map +1 -0
  22. package/dist/RetryRunner.d.ts +27 -0
  23. package/dist/RetryRunner.d.ts.map +1 -0
  24. package/dist/RetryRunner.js +59 -0
  25. package/dist/RetryRunner.js.map +1 -0
  26. package/dist/WatermarkService.d.ts +44 -0
  27. package/dist/WatermarkService.d.ts.map +1 -0
  28. package/dist/WatermarkService.js +97 -0
  29. package/dist/WatermarkService.js.map +1 -0
  30. package/dist/entity-types.d.ts +90 -0
  31. package/dist/entity-types.d.ts.map +1 -0
  32. package/dist/entity-types.js +13 -0
  33. package/dist/entity-types.js.map +1 -0
  34. package/dist/index.d.ts +14 -0
  35. package/dist/index.d.ts.map +1 -0
  36. package/dist/index.js +16 -0
  37. package/dist/index.js.map +1 -0
  38. package/dist/transforms.d.ts +75 -0
  39. package/dist/transforms.d.ts.map +1 -0
  40. package/dist/transforms.js +2 -0
  41. package/dist/transforms.js.map +1 -0
  42. package/dist/types.d.ts +192 -0
  43. package/dist/types.d.ts.map +1 -0
  44. package/dist/types.js +44 -0
  45. package/dist/types.js.map +1 -0
  46. package/package.json +29 -7
package/README.md CHANGED
@@ -1,45 +1,159 @@
1
1
  # @memberjunction/integration-engine
2
2
 
3
- ## ⚠️ IMPORTANT NOTICE ⚠️
3
+ MemberJunction Integration Engine — orchestration, field mapping, and connector framework for synchronizing external systems with MJ entities.
4
+
5
+ ## Architecture
6
+
7
+ The integration engine follows a pipeline architecture:
4
8
 
5
- **This package is created solely for the purpose of setting up OIDC (OpenID Connect) trusted publishing with npm.**
9
+ ```
10
+ External System → Connector → FieldMappingEngine → MatchEngine → MJ Entity Persistence
11
+ ```
6
12
 
7
- This is **NOT** a functional package and contains **NO** code or functionality beyond the OIDC setup configuration.
13
+ ### Core Components
14
+
15
+ | Component | Responsibility |
16
+ |-----------|---------------|
17
+ | `BaseIntegrationConnector` | Abstract base for external system connectors |
18
+ | `ConnectorFactory` | Resolves connector instances via MJGlobal.ClassFactory |
19
+ | `FieldMappingEngine` | Applies field-level transformations from external→MJ fields |
20
+ | `MatchEngine` | Determines Create/Update/Delete/Skip for each record |
21
+ | `WatermarkService` | Manages incremental sync watermarks |
22
+ | `IntegrationOrchestrator` | Top-level coordinator that runs end-to-end sync |
23
+
24
+ ## Quick Start
25
+
26
+ ```typescript
27
+ import { IntegrationOrchestrator } from '@memberjunction/integration-engine';
28
+
29
+ const orchestrator = new IntegrationOrchestrator();
30
+ const result = await orchestrator.RunSync(companyIntegrationID, contextUser, 'Manual');
31
+
32
+ console.log(`Processed: ${result.RecordsProcessed}`);
33
+ console.log(`Created: ${result.RecordsCreated}, Updated: ${result.RecordsUpdated}`);
34
+ console.log(`Errors: ${result.RecordsErrored}`);
35
+ ```
36
+
37
+ ## Creating a Custom Connector
38
+
39
+ ```typescript
40
+ import { RegisterClass } from '@memberjunction/global';
41
+ import {
42
+ BaseIntegrationConnector,
43
+ ConnectionTestResult,
44
+ ExternalObjectSchema,
45
+ ExternalFieldSchema,
46
+ FetchContext,
47
+ FetchBatchResult,
48
+ } from '@memberjunction/integration-engine';
8
49
 
9
- ## Purpose
50
+ @RegisterClass(BaseIntegrationConnector, 'HubSpotConnector')
51
+ export class HubSpotConnector extends BaseIntegrationConnector {
52
+ async TestConnection(companyIntegration, contextUser): Promise<ConnectionTestResult> {
53
+ // Test HubSpot API connectivity
54
+ }
10
55
 
11
- This package exists to:
12
- 1. Configure OIDC trusted publishing for the package name `@memberjunction/integration-engine`
13
- 2. Enable secure, token-less publishing from CI/CD workflows
14
- 3. Establish provenance for packages published under this name
56
+ async DiscoverObjects(companyIntegration, contextUser): Promise<ExternalObjectSchema[]> {
57
+ // Return available HubSpot objects (Contacts, Companies, Deals, etc.)
58
+ }
15
59
 
16
- ## What is OIDC Trusted Publishing?
60
+ async DiscoverFields(companyIntegration, objectName, contextUser): Promise<ExternalFieldSchema[]> {
61
+ // Return fields for a specific HubSpot object
62
+ }
17
63
 
18
- OIDC trusted publishing allows package maintainers to publish packages directly from their CI/CD workflows without needing to manage npm access tokens. Instead, it uses OpenID Connect to establish trust between the CI/CD provider (like GitHub Actions) and npm.
64
+ async FetchChanges(ctx: FetchContext): Promise<FetchBatchResult> {
65
+ // Fetch changed records using HubSpot's API with watermark-based pagination
66
+ }
67
+ }
68
+ ```
19
69
 
20
- ## Setup Instructions
70
+ ## Transform Types
21
71
 
22
- To properly configure OIDC trusted publishing for this package:
72
+ The `FieldMappingEngine` supports 9 transform types configured via JSON in the `TransformPipeline` field:
23
73
 
24
- 1. Go to [npmjs.com](https://www.npmjs.com/) and navigate to your package settings
25
- 2. Configure the trusted publisher (e.g., GitHub Actions)
26
- 3. Specify the repository and workflow that should be allowed to publish
27
- 4. Use the configured workflow to publish your actual package
74
+ | Type | Description | Config |
75
+ |------|-------------|--------|
76
+ | `direct` | Pass-through with optional default | `{ DefaultValue?: unknown }` |
77
+ | `regex` | Regex find/replace | `{ Pattern, Replacement, Flags? }` |
78
+ | `split` | Split string, extract by index | `{ Delimiter, Index }` |
79
+ | `combine` | Merge multiple fields | `{ SourceFields, Separator }` |
80
+ | `lookup` | Case-insensitive value mapping | `{ Map, Default? }` |
81
+ | `format` | Date/number formatting | `{ FormatString, FormatType }` |
82
+ | `coerce` | Type conversion | `{ TargetType: 'string'|'number'|'boolean'|'date' }` |
83
+ | `substring` | Extract portion of string | `{ Start, Length? }` |
84
+ | `custom` | JavaScript expression | `{ Expression }` |
28
85
 
29
- ## DO NOT USE THIS PACKAGE
86
+ ### Transform Pipeline Example
30
87
 
31
- This package is a placeholder for OIDC configuration only. It:
32
- - Contains no executable code
33
- - Provides no functionality
34
- - Should not be installed as a dependency
35
- - Exists only for administrative purposes
88
+ ```json
89
+ [
90
+ { "Type": "regex", "Config": { "Pattern": "[^0-9]", "Replacement": "", "Flags": "g" } },
91
+ { "Type": "substring", "Config": { "Start": 0, "Length": 3 } }
92
+ ]
93
+ ```
36
94
 
37
- ## More Information
95
+ This pipeline strips non-numeric characters then extracts the first 3 digits (e.g., area code from phone number).
38
96
 
39
- For more details about npm's trusted publishing feature, see:
40
- - [npm Trusted Publishing Documentation](https://docs.npmjs.com/generating-provenance-statements)
41
- - [GitHub Actions OIDC Documentation](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect)
97
+ ### Error Handling
42
98
 
43
- ---
99
+ Each transform step can specify `OnError`:
100
+ - `"Fail"` (default) — throws, halting the record
101
+ - `"Skip"` — skips the field entirely
102
+ - `"Null"` — sets the field to null
44
103
 
45
- **Maintained for OIDC setup purposes only**
104
+ ## API Reference
105
+
106
+ ### Notifications
107
+
108
+ After a sync completes or fails, an optional `onNotification` callback receives a `SyncNotification` with pre-formatted subject and body text suitable for email delivery:
109
+
110
+ ```typescript
111
+ const result = await orchestrator.RunSync(
112
+ companyIntegrationID, contextUser, 'Scheduled',
113
+ undefined, // onProgress
114
+ (notification) => {
115
+ if (notification.Severity !== 'Info') {
116
+ emailService.send({
117
+ to: 'ops@example.com',
118
+ subject: notification.Subject,
119
+ text: notification.Body,
120
+ });
121
+ }
122
+ }
123
+ );
124
+ ```
125
+
126
+ **SyncNotification** properties:
127
+ - `Event`: `'SyncCompleted'` | `'SyncCompletedWithErrors'` | `'SyncFailed'`
128
+ - `Severity`: `'Info'` | `'Warning'` | `'Error'`
129
+ - `Subject` / `Body`: human-readable text ready for email
130
+ - `Result`: the full `SyncResult` for programmatic access
131
+
132
+ ### IntegrationOrchestrator
133
+
134
+ - `RunSync(companyIntegrationID, contextUser, triggerType?, onProgress?, onNotification?)` — Executes a full sync run
135
+
136
+ ### FieldMappingEngine
137
+
138
+ - `Apply(records, fieldMaps, entityName)` — Maps external records to MJ entity fields
139
+
140
+ ### MatchEngine
141
+
142
+ - `Resolve(records, entityMap, fieldMaps, contextUser)` — Resolves Create/Update/Delete/Skip
143
+
144
+ ### WatermarkService
145
+
146
+ - `Load(entityMapID, contextUser)` — Loads current watermark
147
+ - `Update(entityMapID, newValue, contextUser)` — Updates or creates watermark
148
+
149
+ ### ConnectorFactory
150
+
151
+ - `Resolve(integration, sourceTypes)` — Creates connector instance via ClassFactory
152
+
153
+ ### BaseIntegrationConnector (abstract)
154
+
155
+ - `TestConnection(companyIntegration, contextUser)` — Tests external system connectivity
156
+ - `DiscoverObjects(companyIntegration, contextUser)` — Lists available external objects
157
+ - `DiscoverFields(companyIntegration, objectName, contextUser)` — Lists fields on an object
158
+ - `FetchChanges(ctx)` — Fetches a batch of changed records
159
+ - `GetDefaultFieldMappings(objectName, entityName)` — Suggests default mappings
@@ -0,0 +1,142 @@
1
+ import type { UserInfo } from '@memberjunction/core';
2
+ import type { MJCompanyIntegrationEntity } from '@memberjunction/core-entities';
3
+ import type { ExternalRecord, DefaultFieldMapping, SourceSchemaInfo } from './types.js';
4
+ /** Result of testing a connection to an external system */
5
+ export interface ConnectionTestResult {
6
+ /** Whether the connection was successful */
7
+ Success: boolean;
8
+ /** Human-readable status message */
9
+ Message: string;
10
+ /** Server or API version reported by the external system */
11
+ ServerVersion?: string;
12
+ }
13
+ /** Schema description of an object/table in an external system */
14
+ export interface ExternalObjectSchema {
15
+ /** API name of the object (e.g., "Contact", "Account") */
16
+ Name: string;
17
+ /** Human-readable label */
18
+ Label: string;
19
+ /** Whether this object supports incremental sync via watermarks */
20
+ SupportsIncrementalSync: boolean;
21
+ /** Whether this object can be created/updated from MJ (push) */
22
+ SupportsWrite: boolean;
23
+ }
24
+ /** Schema description of a single field on an external object */
25
+ export interface ExternalFieldSchema {
26
+ /** API name of the field */
27
+ Name: string;
28
+ /** Human-readable label */
29
+ Label: string;
30
+ /** Field data type in the external system */
31
+ DataType: string;
32
+ /** Whether the field is required */
33
+ IsRequired: boolean;
34
+ /** Whether the field is a unique identifier */
35
+ IsUniqueKey: boolean;
36
+ /** Whether the field is read-only */
37
+ IsReadOnly: boolean;
38
+ }
39
+ /** Context passed to FetchChanges for incremental data retrieval */
40
+ export interface FetchContext {
41
+ /** The company integration entity providing connection details */
42
+ CompanyIntegration: MJCompanyIntegrationEntity;
43
+ /** External object name to fetch from */
44
+ ObjectName: string;
45
+ /** Current watermark value for incremental fetch, or null for full fetch */
46
+ WatermarkValue: string | null;
47
+ /** Maximum number of records to fetch in a single batch */
48
+ BatchSize: number;
49
+ /** User context for authorization */
50
+ ContextUser: UserInfo;
51
+ }
52
+ /** Result of a FetchChanges call, containing a batch of records */
53
+ export interface FetchBatchResult {
54
+ /** Records retrieved in this batch */
55
+ Records: ExternalRecord[];
56
+ /** Whether there are more records to fetch after this batch */
57
+ HasMore: boolean;
58
+ /** Updated watermark value after this batch */
59
+ NewWatermarkValue?: string;
60
+ }
61
+ /** Configurable timeout values for connector operations */
62
+ export interface OperationTimeouts {
63
+ /** Timeout for TestConnection in milliseconds. Default: 5000 */
64
+ TestConnectionMs: number;
65
+ /** Timeout for DiscoverObjects in milliseconds. Default: 10000 */
66
+ DiscoverObjectsMs: number;
67
+ /** Timeout for DiscoverFields in milliseconds. Default: 10000 */
68
+ DiscoverFieldsMs: number;
69
+ /** Timeout for FetchChanges in milliseconds. Default: 30000 */
70
+ FetchChangesMs: number;
71
+ }
72
+ /** Default timeout values for connector operations */
73
+ export declare const DEFAULT_OPERATION_TIMEOUTS: OperationTimeouts;
74
+ /**
75
+ * Wraps a promise with a timeout. Rejects with a timeout error if the
76
+ * promise does not resolve within the specified duration.
77
+ *
78
+ * @param promise - The promise to wrap
79
+ * @param timeoutMs - Timeout in milliseconds
80
+ * @param operationName - Name of the operation for error messaging
81
+ * @returns The result of the promise
82
+ * @throws Error if the operation times out
83
+ */
84
+ export declare function WithTimeout<T>(promise: Promise<T>, timeoutMs: number, operationName: string): Promise<T>;
85
+ /**
86
+ * Abstract base class for integration connectors.
87
+ * Each external system (HubSpot, Salesforce, etc.) implements this class
88
+ * to provide system-specific data access and discovery.
89
+ */
90
+ export declare abstract class BaseIntegrationConnector {
91
+ /**
92
+ * Tests connectivity to the external system.
93
+ * @param companyIntegration - The company integration entity with connection credentials
94
+ * @param contextUser - User context for authorization
95
+ * @returns Connection test result with success/failure and message
96
+ */
97
+ abstract TestConnection(companyIntegration: MJCompanyIntegrationEntity, contextUser: UserInfo): Promise<ConnectionTestResult>;
98
+ /**
99
+ * Discovers available objects/tables in the external system.
100
+ * @param companyIntegration - The company integration entity with connection credentials
101
+ * @param contextUser - User context for authorization
102
+ * @returns Array of object schemas available for integration
103
+ */
104
+ abstract DiscoverObjects(companyIntegration: MJCompanyIntegrationEntity, contextUser: UserInfo): Promise<ExternalObjectSchema[]>;
105
+ /**
106
+ * Discovers fields on a specific external object.
107
+ * @param companyIntegration - The company integration entity with connection credentials
108
+ * @param objectName - Name of the external object to inspect
109
+ * @param contextUser - User context for authorization
110
+ * @returns Array of field schemas for the specified object
111
+ */
112
+ abstract DiscoverFields(companyIntegration: MJCompanyIntegrationEntity, objectName: string, contextUser: UserInfo): Promise<ExternalFieldSchema[]>;
113
+ /**
114
+ * Fetches a batch of changed records from the external system.
115
+ * Supports incremental fetching via watermarks.
116
+ * @param ctx - Context containing connection info, object name, and watermark
117
+ * @returns Batch of external records with pagination info
118
+ */
119
+ abstract FetchChanges(ctx: FetchContext): Promise<FetchBatchResult>;
120
+ /**
121
+ * Returns suggested default field mappings for an external object to MJ entity.
122
+ * Override in subclasses to provide intelligent defaults.
123
+ * @param _objectName - Name of the external object
124
+ * @param _entityName - Name of the target MJ entity
125
+ * @returns Array of default field mappings (empty by default)
126
+ */
127
+ GetDefaultFieldMappings(_objectName: string, _entityName: string): DefaultFieldMapping[];
128
+ /**
129
+ * Introspects the source system's schema — returns metadata about available
130
+ * objects, their fields, primary keys, and foreign key relationships.
131
+ * Used by the Schema Builder to generate local DDL.
132
+ *
133
+ * Default implementation builds SourceSchemaInfo from DiscoverObjects + DiscoverFields.
134
+ * Override in subclasses for richer metadata (e.g., FK relationships, type details).
135
+ *
136
+ * @param companyIntegration - The company integration entity with connection credentials
137
+ * @param contextUser - User context for authorization
138
+ * @returns Full schema info for all source objects
139
+ */
140
+ IntrospectSchema(companyIntegration: MJCompanyIntegrationEntity, contextUser: UserInfo): Promise<SourceSchemaInfo>;
141
+ }
142
+ //# sourceMappingURL=BaseIntegrationConnector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BaseIntegrationConnector.d.ts","sourceRoot":"","sources":["../src/BaseIntegrationConnector.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,91 @@
1
+ /** Default timeout values for connector operations */
2
+ export const DEFAULT_OPERATION_TIMEOUTS = {
3
+ TestConnectionMs: 5000,
4
+ DiscoverObjectsMs: 10000,
5
+ DiscoverFieldsMs: 10000,
6
+ FetchChangesMs: 30000,
7
+ };
8
+ /**
9
+ * Wraps a promise with a timeout. Rejects with a timeout error if the
10
+ * promise does not resolve within the specified duration.
11
+ *
12
+ * @param promise - The promise to wrap
13
+ * @param timeoutMs - Timeout in milliseconds
14
+ * @param operationName - Name of the operation for error messaging
15
+ * @returns The result of the promise
16
+ * @throws Error if the operation times out
17
+ */
18
+ export async function WithTimeout(promise, timeoutMs, operationName) {
19
+ let timeoutHandle;
20
+ const timeoutPromise = new Promise((_resolve, reject) => {
21
+ timeoutHandle = setTimeout(() => {
22
+ reject(new Error(`Operation '${operationName}' timed out after ${timeoutMs}ms`));
23
+ }, timeoutMs);
24
+ });
25
+ try {
26
+ const result = await Promise.race([promise, timeoutPromise]);
27
+ return result;
28
+ }
29
+ finally {
30
+ if (timeoutHandle !== undefined) {
31
+ clearTimeout(timeoutHandle);
32
+ }
33
+ }
34
+ }
35
+ /**
36
+ * Abstract base class for integration connectors.
37
+ * Each external system (HubSpot, Salesforce, etc.) implements this class
38
+ * to provide system-specific data access and discovery.
39
+ */
40
+ export class BaseIntegrationConnector {
41
+ /**
42
+ * Returns suggested default field mappings for an external object to MJ entity.
43
+ * Override in subclasses to provide intelligent defaults.
44
+ * @param _objectName - Name of the external object
45
+ * @param _entityName - Name of the target MJ entity
46
+ * @returns Array of default field mappings (empty by default)
47
+ */
48
+ GetDefaultFieldMappings(_objectName, _entityName) {
49
+ return [];
50
+ }
51
+ /**
52
+ * Introspects the source system's schema — returns metadata about available
53
+ * objects, their fields, primary keys, and foreign key relationships.
54
+ * Used by the Schema Builder to generate local DDL.
55
+ *
56
+ * Default implementation builds SourceSchemaInfo from DiscoverObjects + DiscoverFields.
57
+ * Override in subclasses for richer metadata (e.g., FK relationships, type details).
58
+ *
59
+ * @param companyIntegration - The company integration entity with connection credentials
60
+ * @param contextUser - User context for authorization
61
+ * @returns Full schema info for all source objects
62
+ */
63
+ async IntrospectSchema(companyIntegration, contextUser) {
64
+ const objects = await this.DiscoverObjects(companyIntegration, contextUser);
65
+ const result = { Objects: [] };
66
+ for (const obj of objects) {
67
+ const fields = await this.DiscoverFields(companyIntegration, obj.Name, contextUser);
68
+ result.Objects.push({
69
+ ExternalName: obj.Name,
70
+ ExternalLabel: obj.Label,
71
+ Fields: fields.map(f => ({
72
+ Name: f.Name,
73
+ Label: f.Label,
74
+ SourceType: f.DataType,
75
+ IsRequired: f.IsRequired,
76
+ MaxLength: null,
77
+ Precision: null,
78
+ Scale: null,
79
+ DefaultValue: null,
80
+ IsPrimaryKey: f.IsUniqueKey,
81
+ IsForeignKey: false,
82
+ ForeignKeyTarget: null,
83
+ })),
84
+ PrimaryKeyFields: fields.filter(f => f.IsUniqueKey).map(f => f.Name),
85
+ Relationships: [],
86
+ });
87
+ }
88
+ return result;
89
+ }
90
+ }
91
+ //# sourceMappingURL=BaseIntegrationConnector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BaseIntegrationConnector.js","sourceRoot":"","sources":["../src/BaseIntegrationConnector.ts"],"names":[],"mappings":"AA8EA,sDAAsD;AACtD,MAAM,CAAC,MAAM,0BAA0B,GAAsB;IACzD,gBAAgB,EAAE,IAAI;IACtB,iBAAiB,EAAE,KAAK;IACxB,gBAAgB,EAAE,KAAK;IACvB,cAAc,EAAE,KAAK;CACxB,CAAC;AAEF;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC7B,OAAmB,EACnB,SAAiB,EACjB,aAAqB;IAErB,IAAI,aAAwD,CAAC;IAE7D,MAAM,cAAc,GAAG,IAAI,OAAO,CAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE;QAC3D,aAAa,GAAG,UAAU,CAAC,GAAG,EAAE;YAC5B,MAAM,CAAC,IAAI,KAAK,CAAC,cAAc,aAAa,qBAAqB,SAAS,IAAI,CAAC,CAAC,CAAC;QACrF,CAAC,EAAE,SAAS,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC;QACD,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC,CAAC;QAC7D,OAAO,MAAM,CAAC;IAClB,CAAC;YAAS,CAAC;QACP,IAAI,aAAa,KAAK,SAAS,EAAE,CAAC;YAC9B,YAAY,CAAC,aAAa,CAAC,CAAC;QAChC,CAAC;IACL,CAAC;AACL,CAAC;AAED;;;;GAIG;AACH,MAAM,OAAgB,wBAAwB;IA4C1C;;;;;;OAMG;IACI,uBAAuB,CAAC,WAAmB,EAAE,WAAmB;QACnE,OAAO,EAAE,CAAC;IACd,CAAC;IAED;;;;;;;;;;;OAWG;IACI,KAAK,CAAC,gBAAgB,CACzB,kBAA8C,EAC9C,WAAqB;QAErB,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,kBAAkB,EAAE,WAAW,CAAC,CAAC;QAC5E,MAAM,MAAM,GAAqB,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;QAEjD,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;YACxB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,kBAAkB,EAAE,GAAG,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;YACpF,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC;gBAChB,YAAY,EAAE,GAAG,CAAC,IAAI;gBACtB,aAAa,EAAE,GAAG,CAAC,KAAK;gBACxB,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;oBACrB,IAAI,EAAE,CAAC,CAAC,IAAI;oBACZ,KAAK,EAAE,CAAC,CAAC,KAAK;oBACd,UAAU,EAAE,CAAC,CAAC,QAAQ;oBACtB,UAAU,EAAE,CAAC,CAAC,UAAU;oBACxB,SAAS,EAAE,IAAI;oBACf,SAAS,EAAE,IAAI;oBACf,KAAK,EAAE,IAAI;oBACX,YAAY,EAAE,IAAI;oBAClB,YAAY,EAAE,CAAC,CAAC,WAAW;oBAC3B,YAAY,EAAE,KAAK;oBACnB,gBAAgB,EAAE,IAAI;iBACzB,CAAC,CAAC;gBACH,gBAAgB,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;gBACpE,aAAa,EAAE,EAAE;aACpB,CAAC,CAAC;QACP,CAAC;QAED,OAAO,MAAM,CAAC;IAClB,CAAC;CACJ"}
@@ -0,0 +1,30 @@
1
+ import type { MJIntegrationEntity } from '@memberjunction/core-entities';
2
+ import type { IIntegrationSourceType } from './entity-types.js';
3
+ import { BaseIntegrationConnector } from './BaseIntegrationConnector.js';
4
+ /**
5
+ * Factory for resolving integration connectors using MJGlobal.ClassFactory.
6
+ * Connectors register themselves via `@RegisterClass(BaseIntegrationConnector, 'DriverClassName')`.
7
+ */
8
+ export declare class ConnectorFactory {
9
+ /**
10
+ * Resolves and instantiates the appropriate connector for an integration.
11
+ * Looks up the DriverClass from the integration's source type and uses
12
+ * MJGlobal.ClassFactory to create the connector instance.
13
+ *
14
+ * @param integration - The integration entity defining which external system to connect to
15
+ * @param sourceTypes - Array of available integration source type entities
16
+ * @returns An instance of the appropriate BaseIntegrationConnector subclass
17
+ * @throws Error if no source type matches or no connector is registered for the driver class
18
+ */
19
+ static Resolve(integration: MJIntegrationEntity, sourceTypes: IIntegrationSourceType[]): BaseIntegrationConnector;
20
+ /**
21
+ * Finds the DriverClass name from the source types that matches the integration's ClassName.
22
+ */
23
+ private static FindDriverClass;
24
+ /**
25
+ * Creates a connector instance via MJGlobal.ClassFactory.
26
+ * First verifies a registration exists to avoid falling back to the abstract base class.
27
+ */
28
+ private static CreateConnectorInstance;
29
+ }
30
+ //# sourceMappingURL=ConnectorFactory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConnectorFactory.d.ts","sourceRoot":"","sources":["../src/ConnectorFactory.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AACzE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAChE,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAEzE;;;GAGG;AACH,qBAAa,gBAAgB;IACzB;;;;;;;;;OASG;WACW,OAAO,CACjB,WAAW,EAAE,mBAAmB,EAChC,WAAW,EAAE,sBAAsB,EAAE,GACtC,wBAAwB;IAK3B;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,eAAe;IAqB9B;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAC,uBAAuB;CAoBzC"}
@@ -0,0 +1,50 @@
1
+ import { MJGlobal } from '@memberjunction/global';
2
+ import { BaseIntegrationConnector } from './BaseIntegrationConnector.js';
3
+ /**
4
+ * Factory for resolving integration connectors using MJGlobal.ClassFactory.
5
+ * Connectors register themselves via `@RegisterClass(BaseIntegrationConnector, 'DriverClassName')`.
6
+ */
7
+ export class ConnectorFactory {
8
+ /**
9
+ * Resolves and instantiates the appropriate connector for an integration.
10
+ * Looks up the DriverClass from the integration's source type and uses
11
+ * MJGlobal.ClassFactory to create the connector instance.
12
+ *
13
+ * @param integration - The integration entity defining which external system to connect to
14
+ * @param sourceTypes - Array of available integration source type entities
15
+ * @returns An instance of the appropriate BaseIntegrationConnector subclass
16
+ * @throws Error if no source type matches or no connector is registered for the driver class
17
+ */
18
+ static Resolve(integration, sourceTypes) {
19
+ const driverClass = ConnectorFactory.FindDriverClass(integration, sourceTypes);
20
+ return ConnectorFactory.CreateConnectorInstance(driverClass);
21
+ }
22
+ /**
23
+ * Finds the DriverClass name from the source types that matches the integration's ClassName.
24
+ */
25
+ static FindDriverClass(integration, sourceTypes) {
26
+ const className = integration.ClassName;
27
+ if (!className) {
28
+ throw new Error(`Integration "${integration.Name}" does not have a ClassName configured.`);
29
+ }
30
+ const sourceType = sourceTypes.find(st => st.DriverClass === className);
31
+ if (!sourceType) {
32
+ throw new Error(`No IntegrationSourceType found with DriverClass "${className}" for integration "${integration.Name}".`);
33
+ }
34
+ return sourceType.DriverClass;
35
+ }
36
+ /**
37
+ * Creates a connector instance via MJGlobal.ClassFactory.
38
+ * First verifies a registration exists to avoid falling back to the abstract base class.
39
+ */
40
+ static CreateConnectorInstance(driverClass) {
41
+ const registration = MJGlobal.Instance.ClassFactory.GetRegistration(BaseIntegrationConnector, driverClass);
42
+ if (!registration) {
43
+ throw new Error(`No connector registered for driver class "${driverClass}". ` +
44
+ `Ensure a class is decorated with @RegisterClass(BaseIntegrationConnector, '${driverClass}').`);
45
+ }
46
+ const instance = MJGlobal.Instance.ClassFactory.CreateInstance(BaseIntegrationConnector, driverClass);
47
+ return instance;
48
+ }
49
+ }
50
+ //# sourceMappingURL=ConnectorFactory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConnectorFactory.js","sourceRoot":"","sources":["../src/ConnectorFactory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAGlD,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAEzE;;;GAGG;AACH,MAAM,OAAO,gBAAgB;IACzB;;;;;;;;;OASG;IACI,MAAM,CAAC,OAAO,CACjB,WAAgC,EAChC,WAAqC;QAErC,MAAM,WAAW,GAAG,gBAAgB,CAAC,eAAe,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;QAC/E,OAAO,gBAAgB,CAAC,uBAAuB,CAAC,WAAW,CAAC,CAAC;IACjE,CAAC;IAED;;OAEG;IACK,MAAM,CAAC,eAAe,CAC1B,WAAgC,EAChC,WAAqC;QAErC,MAAM,SAAS,GAAG,WAAW,CAAC,SAAS,CAAC;QACxC,IAAI,CAAC,SAAS,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CACX,gBAAgB,WAAW,CAAC,IAAI,yCAAyC,CAC5E,CAAC;QACN,CAAC;QAED,MAAM,UAAU,GAAG,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC;QACxE,IAAI,CAAC,UAAU,EAAE,CAAC;YACd,MAAM,IAAI,KAAK,CACX,oDAAoD,SAAS,sBAAsB,WAAW,CAAC,IAAI,IAAI,CAC1G,CAAC;QACN,CAAC;QAED,OAAO,UAAU,CAAC,WAAW,CAAC;IAClC,CAAC;IAED;;;OAGG;IACK,MAAM,CAAC,uBAAuB,CAAC,WAAmB;QACtD,MAAM,YAAY,GAAG,QAAQ,CAAC,QAAQ,CAAC,YAAY,CAAC,eAAe,CAC/D,wBAAwB,EACxB,WAAW,CACd,CAAC;QAEF,IAAI,CAAC,YAAY,EAAE,CAAC;YAChB,MAAM,IAAI,KAAK,CACX,6CAA6C,WAAW,KAAK;gBAC7D,8EAA8E,WAAW,KAAK,CACjG,CAAC;QACN,CAAC;QAED,MAAM,QAAQ,GAAG,QAAQ,CAAC,QAAQ,CAAC,YAAY,CAAC,cAAc,CAC1D,wBAAwB,EACxB,WAAW,CACd,CAAC;QAEF,OAAO,QAAS,CAAC;IACrB,CAAC;CACJ"}
@@ -0,0 +1,94 @@
1
+ import type { ICompanyIntegrationFieldMap } from './entity-types.js';
2
+ import type { ExternalRecord, MappedRecord } from './types.js';
3
+ /**
4
+ * Engine responsible for applying field-level mappings and transformations
5
+ * from external records to MJ entity fields.
6
+ */
7
+ export declare class FieldMappingEngine {
8
+ /**
9
+ * Applies field mappings to a batch of external records, producing mapped records
10
+ * ready for match resolution and persistence.
11
+ *
12
+ * @param records - External records to map
13
+ * @param fieldMaps - Active field map entities defining source→destination mappings
14
+ * @param entityName - Target MJ entity name
15
+ * @returns Array of mapped records with transformed field values
16
+ */
17
+ Apply(records: ExternalRecord[], fieldMaps: ICompanyIntegrationFieldMap[], entityName: string): MappedRecord[];
18
+ /**
19
+ * Maps a single external record through all active field mappings.
20
+ */
21
+ private MapSingleRecord;
22
+ /**
23
+ * Applies a single field mapping, including the full transform pipeline.
24
+ * Returns undefined if the field should be skipped (OnError: Skip).
25
+ */
26
+ private ApplyFieldMapping;
27
+ /**
28
+ * Parses the JSON transform pipeline string into typed TransformStep objects.
29
+ */
30
+ private ParseTransformPipeline;
31
+ /**
32
+ * Executes a single transform step, applying the configured transformation
33
+ * and handling errors according to the step's OnError strategy.
34
+ */
35
+ private ExecuteTransformStep;
36
+ /**
37
+ * Dispatches to the appropriate transform handler based on step type.
38
+ */
39
+ private DispatchTransform;
40
+ /**
41
+ * Handles a transform error according to the OnError strategy.
42
+ */
43
+ private HandleTransformError;
44
+ /**
45
+ * Direct pass-through, applying default value if source is null/undefined.
46
+ */
47
+ private ApplyDirect;
48
+ /**
49
+ * Applies a regex replacement to a string value.
50
+ */
51
+ private ApplyRegex;
52
+ /**
53
+ * Splits a string value and extracts a part by index.
54
+ */
55
+ private ApplySplit;
56
+ /**
57
+ * Combines multiple source fields with a separator.
58
+ */
59
+ private ApplyCombine;
60
+ /**
61
+ * Performs a case-insensitive value lookup/mapping.
62
+ */
63
+ private ApplyLookup;
64
+ /**
65
+ * Applies date/number/string formatting.
66
+ */
67
+ private ApplyFormat;
68
+ /**
69
+ * Basic date formatting supporting ISO and common format tokens.
70
+ */
71
+ private FormatDate;
72
+ /**
73
+ * Coerces a value to the specified target type.
74
+ */
75
+ private ApplyCoerce;
76
+ /**
77
+ * Coerces a value to a number, throwing on NaN.
78
+ */
79
+ private CoerceToNumber;
80
+ /**
81
+ * Coerces a value to boolean, supporting common truthy/falsy strings.
82
+ */
83
+ private CoerceToBoolean;
84
+ /**
85
+ * Extracts a substring from a string value.
86
+ */
87
+ private ApplySubstring;
88
+ /**
89
+ * Evaluates a custom JavaScript expression.
90
+ * The expression has access to `value` (current field value) and `fields` (all record fields).
91
+ */
92
+ private ApplyCustom;
93
+ }
94
+ //# sourceMappingURL=FieldMappingEngine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FieldMappingEngine.d.ts","sourceRoot":"","sources":["../src/FieldMappingEngine.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,mBAAmB,CAAC;AACrE,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAe/D;;;GAGG;AACH,qBAAa,kBAAkB;IAC3B;;;;;;;;OAQG;IACI,KAAK,CACR,OAAO,EAAE,cAAc,EAAE,EACzB,SAAS,EAAE,2BAA2B,EAAE,EACxC,UAAU,EAAE,MAAM,GACnB,YAAY,EAAE;IAKjB;;OAEG;IACH,OAAO,CAAC,eAAe;IAsBvB;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAgBzB;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAY9B;;;OAGG;IACH,OAAO,CAAC,oBAAoB;IAe5B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IA6BzB;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAW5B;;OAEG;IACH,OAAO,CAAC,WAAW;IAOnB;;OAEG;IACH,OAAO,CAAC,UAAU;IAMlB;;OAEG;IACH,OAAO,CAAC,UAAU;IAMlB;;OAEG;IACH,OAAO,CAAC,YAAY;IAKpB;;OAEG;IACH,OAAO,CAAC,WAAW;IASnB;;OAEG;IACH,OAAO,CAAC,WAAW;IAYnB;;OAEG;IACH,OAAO,CAAC,UAAU;IAOlB;;OAEG;IACH,OAAO,CAAC,WAAW;IAenB;;OAEG;IACH,OAAO,CAAC,cAAc;IAQtB;;OAEG;IACH,OAAO,CAAC,eAAe;IAOvB;;OAEG;IACH,OAAO,CAAC,cAAc;IAQtB;;;OAGG;IACH,OAAO,CAAC,WAAW;CActB"}