@memberjunction/connector-impexium 1.0.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.
@@ -0,0 +1,226 @@
1
+ import { type UserInfo } from '@memberjunction/core';
2
+ import type { MJCompanyIntegrationEntity, MJIntegrationObjectEntity } from '@memberjunction/core-entities';
3
+ import { BaseRESTIntegrationConnector, type RESTAuthContext, type RESTResponse, type PaginationState, type PaginationType, type ConnectionTestResult, type FetchContext, type FetchBatchResult, type CreateRecordContext, type UpdateRecordContext, type DeleteRecordContext, type CRUDResult, type SourceSchemaInfo } from '@memberjunction/integration-engine';
4
+ /**
5
+ * re:Members AMS (Impexium) connector — extends BaseRESTIntegrationConnector (REST/JSON over HTTP).
6
+ * See the design note above for the full protocol surface. Registered under the TS class symbol so the
7
+ * sandbox verification ladder / T1 three-way check stay green; the OpenAppPublish step rewrites the key.
8
+ */
9
+ export declare class ImpexiumConnector extends BaseRESTIntegrationConnector {
10
+ /** Cached auth context (with token expiry) for the lifetime of a sync run; re-minted on expiry. */
11
+ private cachedAuth;
12
+ /** Watermark value in effect for the current FetchChanges call — emitted as the incremental param. */
13
+ private currentWatermark;
14
+ /** Verbatim MJ: Integrations.Name. The T1 three-way name check statically parses this literal. */
15
+ get IntegrationName(): string;
16
+ get SupportsCreate(): boolean;
17
+ get SupportsUpdate(): boolean;
18
+ get SupportsDelete(): boolean;
19
+ /**
20
+ * Discovery is NON-authoritative: DiscoverObjects / IntrospectSchema are cache-driven (they re-read
21
+ * persisted ACTIVE Declared metadata, NOT a live full-gamut enumeration). Impexium exposes no
22
+ * describe-all / complete-gamut endpoint (GET Setup/customfields only enumerates the tenant's custom
23
+ * FIELD catalog, not a whole object/field gamut). Absence in a refresh proves nothing → never
24
+ * deactivate. Matches Configuration.DiscoveryIsAuthoritative=false in the frozen contract.
25
+ */
26
+ get DiscoveryIsAuthoritative(): boolean;
27
+ /**
28
+ * Sample-union field enrichment (the connector standard). Declared metadata is authoritative but
29
+ * incomplete — it misses a tenant's CUSTOM / undeclared columns (Impexium's per-tenant custom fields).
30
+ * Wire the required enrichment: take the base cache-driven schema, then per object UNION the declared
31
+ * field set with what a live read actually returns (`mergeDeclaredWithSampledFields` — never-shrink,
32
+ * declared-wins, capacities widened). Best-effort + parallel: a per-object sample failure leaves that
33
+ * object's declared fields untouched. Overrides IntrospectSchema (NEVER DiscoverFields — that would
34
+ * recurse into DiscoverFieldsViaFetch's fallback).
35
+ */
36
+ IntrospectSchema(companyIntegration: MJCompanyIntegrationEntity, contextUser: UserInfo): Promise<SourceSchemaInfo>;
37
+ /** True when any cached IntegrationObject satisfies the predicate. Fail-safe read-only ([]→false). */
38
+ private anyObjectDeclares;
39
+ /**
40
+ * Authenticate against the RAW tenant host. Candidate A (default) runs the app + user handshake and
41
+ * caches the Access-Token + App-Token; candidate B ('apikey') resolves a single x-api-key. The cached
42
+ * context is re-minted when its tokens near expiry. NEVER inline crypto — the handshake is a plain
43
+ * credential POST returning tokens.
44
+ */
45
+ protected Authenticate(companyIntegration: MJCompanyIntegrationEntity, contextUser: UserInfo): Promise<RESTAuthContext>;
46
+ /**
47
+ * Attaches the auth headers per model: candidate A sends BOTH tokens (Access-Token + App-Token,
48
+ * header names Configuration-overridable); candidate B sends the single x-api-key.
49
+ */
50
+ protected BuildHeaders(auth: RESTAuthContext): Record<string, string>;
51
+ /** Per-tenant host resolved in Authenticate — ZERO host baked in code. */
52
+ protected GetBaseURL(_companyIntegration: MJCompanyIntegrationEntity, auth: RESTAuthContext): string;
53
+ /** HTTP transport (fetch). Owns the wire boundary; test subclasses override this to capture requests. */
54
+ protected MakeHTTPRequest(auth: RESTAuthContext, url: string, method: string, headers: Record<string, string>, body?: unknown): Promise<RESTResponse>;
55
+ /**
56
+ * Strips Impexium's list envelope `{ pageNumber, dataList: T[] }` to the dataList array
57
+ * (ResponseDataKey='dataList'). Falls back to a bare array, the universal dataList key, or a single
58
+ * detail object. The FULL source record passes through (no field filtering).
59
+ */
60
+ protected NormalizeResponse(rawBody: unknown, responseDataKey: string | null): Record<string, unknown>[];
61
+ /**
62
+ * Page-in-path pagination: the envelope carries NO hasMore/totalCount field, so termination is
63
+ * empty-dataList. HasMore is true iff the current page returned records; the loop advances the page
64
+ * number (rendered into the path by BuildPaginatedURL) and stops when a page comes back empty.
65
+ */
66
+ protected ExtractPaginationInfo(rawBody: unknown, paginationType: PaginationType, currentPage: number, _currentOffset: number, _pageSize: number): PaginationState;
67
+ /**
68
+ * Impexium puts the page number IN THE PATH (a {Page Number}/{pageNumber}/{Page} segment), NEVER as a
69
+ * `?page=` query param. Substitute the page into that path segment and return — no query pagination.
70
+ */
71
+ protected BuildPaginatedURL(basePath: string, obj: MJIntegrationObjectEntity, page: number, _offset: number, _cursor?: string, _effectivePageSize?: number): string;
72
+ /**
73
+ * Injects the incremental watermark filter when a sync is active. The param NAME is fully
74
+ * METADATA-DRIVEN: it comes from the IO's IncrementalWatermarkField (changedSince / purchasedSince /
75
+ * registeredSince / cancelledSince) and is emitted only when SupportsIncrementalSync=true AND a
76
+ * watermark value is in context. Runs on top of the base's DefaultQueryParams handling.
77
+ */
78
+ protected AppendDefaultQueryParams(url: string, obj: MJIntegrationObjectEntity): string;
79
+ /**
80
+ * Fetch a batch. Flat top-level objects delegate to the base GET loop (which uses the BuildPaginatedURL
81
+ * / ExtractPaginationInfo / AppendDefaultQueryParams overrides above for page-in-path + incremental).
82
+ * Nested objects (whose frozen paths carry space-containing parent placeholders the base's `\w+`
83
+ * template detector can't resolve) are walked here: resolve the parent sibling IO from the preceding
84
+ * path RESOURCE segment, iterate synced parent records (bounded + keyset-resumable), substitute, then
85
+ * page the leaf. The watermark advances (partial-failure-safe) only on a fully-drained final batch.
86
+ */
87
+ FetchChanges(ctx: FetchContext): Promise<FetchBatchResult>;
88
+ /**
89
+ * Walks a nested object's access path: resolve the PRIMARY parent token → sibling IO (by the path
90
+ * segment preceding it), load the synced parent record IDs, process a bounded, keyset-resumable batch
91
+ * of parents, substitute + page each leaf. Surfaces a FetchWarning (never a silent empty) when the
92
+ * parent can't be resolved (Courses / tasks Users are not emitted IOs), when no parents are synced yet,
93
+ * or when a secondary parent token remains unresolved.
94
+ */
95
+ private fetchWithParents;
96
+ /**
97
+ * Drains ALL pages of a single resolved (parent-substituted) leaf path via page-in-path pagination,
98
+ * or a single request when the object is non-paginated. Stops on an empty dataList; skips gracefully
99
+ * on 403/404 (permission-gated / absent sub-resource). Injects the incremental watermark param.
100
+ */
101
+ private fetchLeafPages;
102
+ /**
103
+ * Create. OVERRIDDEN because Impexium's create paths carry PARENT template vars ({code}/{id}/
104
+ * {Record Number}) filled from the record's own attributes — the generic base path leaves them literal.
105
+ * The body shape + loud-on-empty-id BuildCreatedResult are preserved. Top-level creates with no path
106
+ * vars (Individuals, Organizations, Tasks, Requests) behave exactly as the generic path would.
107
+ */
108
+ CreateRecord(ctx: CreateRecordContext): Promise<CRUDResult>;
109
+ /**
110
+ * Update. OVERRIDDEN so parent path vars are filled from attributes and the path id ({id}/{Task Number}/
111
+ * {memberRecordNumber}/…) is filled from the ExternalID when UpdateIDLocation='path'. When
112
+ * UpdateIDLocation='body', the id is not path-substituted (the record body carries it).
113
+ */
114
+ UpdateRecord(ctx: UpdateRecordContext): Promise<CRUDResult>;
115
+ /**
116
+ * Delete. OVERRIDDEN so the path id ({Category Code}) is filled from the ExternalID when
117
+ * DeleteIDLocation='path'. NOTE (documented limitation, flagged for T10 live verification): the only
118
+ * two delete-capable IOs (Categories, Links) are narrow sub-resources whose path ALSO carries a parent
119
+ * segment ({Record Number}) that the framework's DeleteRecordContext does not surface — a single-part
120
+ * ExternalID fills only the target id, so a live delete of these sub-resources needs the parent encoded
121
+ * in a composite ExternalID. Links additionally identifies the target via the request BODY
122
+ * (DeleteIDLocation='body'), which the delete context does not carry.
123
+ */
124
+ DeleteRecord(ctx: DeleteRecordContext): Promise<CRUDResult>;
125
+ /**
126
+ * Reads the created record's id from the Impexium create response. The universal envelope returns the
127
+ * new record under `id`; some sub-resource creates return the record with no server id
128
+ * (CreateIDLocation='n/a') — those route to BuildCreatedResult, which fails loudly on an empty id.
129
+ */
130
+ protected ExtractIDFromResponse(response: RESTResponse, idLocation: string | null): string | undefined;
131
+ /**
132
+ * Tests connectivity by authenticating and reading one page of Individuals. A 2xx confirms the auth
133
+ * flow + tenant host are valid; 401/403 → auth failure; anything else → error. Never mutates.
134
+ */
135
+ TestConnection(companyIntegration: MJCompanyIntegrationEntity, contextUser: UserInfo): Promise<ConnectionTestResult>;
136
+ /**
137
+ * Runs the candidate-A app + user handshake: POST {AppId, AppKey, AppName, Email, Password} to the auth
138
+ * path and read the Access-Token + App-Token (+ optional expiry) from the response. UNVERIFIED at plan
139
+ * time — the endpoint path, request field names, response field names and TTL are all overridable, so a
140
+ * live/sandbox probe can settle them without a code change. Plain credential POST; no crypto.
141
+ */
142
+ private mintTokens;
143
+ /** Derives the token expiry from the handshake response, falling back to the configured/default TTL. */
144
+ private resolveTokenExpiry;
145
+ /** Parses the connection config, preferring the linked Credential entity over the Configuration JSON. */
146
+ private parseConfig;
147
+ /** Loads a credential row and parses its Values JSON to a plain map. */
148
+ private loadCredentialValues;
149
+ /** Builds the typed config from a raw case-insensitive map, applying defaults + validating HostUrl. */
150
+ private buildConfig;
151
+ /** Resolves the per-tenant host (no trailing slash) — never a hardcoded host. */
152
+ private resolveHost;
153
+ /** All `{...}` placeholders in a path (space-tolerant), unwrapped. */
154
+ private allTokens;
155
+ /** True for the page placeholder (`{Page Number}` / `{pageNumber}` / `{Page}`) — space/case tolerant. */
156
+ private isPageToken;
157
+ /** Parent (non-page) placeholders in a path. */
158
+ private parentTokens;
159
+ /** True when the path still carries a page placeholder. */
160
+ private hasPageToken;
161
+ /** Substitutes the page placeholder (space/case-tolerant) with the page number. */
162
+ private substitutePagePlaceholder;
163
+ /**
164
+ * Resolves the parent object name for a template var from the path segment IMMEDIATELY preceding the
165
+ * `{token}` (Impexium's REST convention: the parent resource name precedes its id), matched against a
166
+ * sibling IntegrationObject by name. Returns null when no emitted sibling matches (e.g. Courses, Users).
167
+ * Protected so tests can resolve a parent without a populated engine cache.
168
+ */
169
+ protected resolveParentObjectName(apiPath: string, token: string, integrationID: string): string | null;
170
+ /**
171
+ * Loads the synced parent record IDs from the local MJ database (via the object's EntityMap → the target
172
+ * entity's PK column). Protected so tests can inject fixed parent IDs without a live RunView.
173
+ */
174
+ protected loadParentRecordIDs(parentObjectName: string, integrationID: string, contextUser: UserInfo): Promise<string[]>;
175
+ /** Fills a CreateAPIPath's parent template vars from the record's own attributes (case/space-insensitive). */
176
+ private substitutePathVarsFromAttributes;
177
+ /**
178
+ * Fills a write (update/delete) path: parent vars from attributes first, then the remaining path id from
179
+ * the (possibly composite) ExternalID when idLocation is 'path'. When idLocation is 'body'/'header', the
180
+ * id is NOT path-substituted (the record body / a header carries it).
181
+ */
182
+ private substituteWritePath;
183
+ /** Looks up an attribute by name, matching case- and space-insensitively (`{Record Number}` → recordNumber). */
184
+ private lookupAttr;
185
+ /** Joins a base URL and a path (mirrors the base's private BuildFullURL). */
186
+ private joinURL;
187
+ /** Appends the metadata-driven incremental watermark query param when active. */
188
+ private appendIncrementalParam;
189
+ /** 403/404 are skippable (permission-gated object / absent sub-resource) — degrade to zero rows. */
190
+ private isSkippableStatus;
191
+ /** Throws a descriptive error on a non-2xx (non-skippable) status. */
192
+ private ensureOk;
193
+ /** PK field names in Sequence order; empty when the object declares no PK (→ content-hash identity). */
194
+ private pkFieldNames;
195
+ /**
196
+ * Builds an ExternalRecord with the FULL source record in Fields (full-record pass-through — the
197
+ * framework's custom-column capture diffs keys(Fields) against the field maps). ExternalID is the
198
+ * composite PK when every component is present + non-empty, else a deterministic content hash (which is
199
+ * also stamped into a single empty PK column so PK-less rows remain storable + idempotent). Mirrors the
200
+ * base's ToExternalRecord (which is private) for the parent-iterated path.
201
+ */
202
+ private buildExternalRecord;
203
+ /** Builds a warning-only fetch result (surfaces a silent-empty as a structured SyncWarning). */
204
+ private warnResult;
205
+ /**
206
+ * The next watermark for a changed-since object is the time this sync STARTED (captured before any
207
+ * fetch), so the next incremental picks up everything modified since — a slight overlap is idempotent.
208
+ * Impexium's list responses carry no reliable single modified-date field to derive it from, so wall-clock
209
+ * start is the safe, monotonic choice. Only ever advances forward.
210
+ */
211
+ private computeNewWatermark;
212
+ /** Formats a date as the Impexium changed-since format `yyyy-MM-ddTHH:mm:ss` (UTC). */
213
+ private formatWatermark;
214
+ /** Returns the first present, non-empty string value among the given keys. */
215
+ private firstString;
216
+ /** Returns the first present finite number among the given keys. */
217
+ private firstNumber;
218
+ /** Reads an `id`-like value as a string; undefined when absent/empty. */
219
+ private readId;
220
+ /** Stable total order over ID strings — numeric when ALL are integer-like, else lexical. */
221
+ private sortIdsStable;
222
+ /** Total-order comparator matching sortIdsStable, used for keyset resume (id > AfterKeyValue). */
223
+ private compareIds;
224
+ }
225
+ /** Tree-shaking prevention — import and call from the package entry point. */
226
+ export declare function LoadImpexiumConnector(): void;
@@ -0,0 +1,874 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { RegisterClass } from '@memberjunction/global';
8
+ import { Metadata, RunView } from '@memberjunction/core';
9
+ import { IntegrationEngineBase } from '@memberjunction/integration-engine-base';
10
+ import { BaseIntegrationConnector, BaseRESTIntegrationConnector, computeContentHash, serializeKeyValue, } from '@memberjunction/integration-engine';
11
+ import { mergeDeclaredWithSampledFields } from '@memberjunction/connector-schema-merge';
12
+ // ─── Constants ──────────────────────────────────────────────────────────────
13
+ /** Verbatim MJ: Integrations.Name — part of the T1 three-way name invariant. */
14
+ const INTEGRATION_NAME = 're:Members AMS';
15
+ /**
16
+ * Auth handshake defaults (candidate A). UNVERIFIED at plan time — every one is Configuration-overridable
17
+ * so a live/sandbox probe can correct the exact path / header / field names WITHOUT touching this code.
18
+ */
19
+ const DEFAULT_AUTH_PATH = '/api/v1/authenticate';
20
+ const DEFAULT_ACCESS_TOKEN_HEADER = 'Access-Token';
21
+ const DEFAULT_APP_TOKEN_HEADER = 'App-Token';
22
+ const DEFAULT_API_KEY_HEADER = 'x-api-key';
23
+ const DEFAULT_REQUEST_TIMEOUT_MS = 60_000;
24
+ /** Fallback token lifetime when the handshake response carries no explicit expiry (conservative). */
25
+ const DEFAULT_TOKEN_TTL_MS = 20 * 60 * 1_000;
26
+ /** Re-mint skew window: re-mint when within this many ms of expiry. */
27
+ const TOKEN_REFRESH_BUFFER_MS = 60_000;
28
+ /** The universal list envelope key. Consistent across all 55 list-shaped responses per the frozen contract. */
29
+ const DATA_LIST_KEY = 'dataList';
30
+ /** Per-call cap on how many PARENTS a nested object iterates before yielding a keyset-resumable batch. */
31
+ const PARENT_BATCH_SIZE = 10;
32
+ /** Hard safety cap on page-loop iterations (termination is normally empty-page). */
33
+ const MAX_LEAF_PAGES = 10_000;
34
+ // ─── ImpexiumConnector ────────────────────────────────────────────────────────
35
+ /**
36
+ * re:Members AMS (Impexium) connector — extends BaseRESTIntegrationConnector (REST/JSON over HTTP).
37
+ * See the design note above for the full protocol surface. Registered under the TS class symbol so the
38
+ * sandbox verification ladder / T1 three-way check stay green; the OpenAppPublish step rewrites the key.
39
+ */
40
+ let ImpexiumConnector = class ImpexiumConnector extends BaseRESTIntegrationConnector {
41
+ constructor() {
42
+ super(...arguments);
43
+ /** Cached auth context (with token expiry) for the lifetime of a sync run; re-minted on expiry. */
44
+ this.cachedAuth = null;
45
+ }
46
+ // ── Identity (T1 three-way invariant) ────────────────────────────────────
47
+ /** Verbatim MJ: Integrations.Name. The T1 three-way name check statically parses this literal. */
48
+ get IntegrationName() {
49
+ return 're:Members AMS';
50
+ }
51
+ // ── Capability getters (kept in lockstep with the per-operation metadata columns) ──
52
+ //
53
+ // Write capability FOLLOWS the per-operation CRUD columns on the cached IntegrationObjects (Declared
54
+ // metadata): an object is create-capable iff it declares both CreateAPIPath + CreateMethod, etc. No
55
+ // hardcoded `true`; no 501 stubs. With no write metadata the surface is read-only.
56
+ get SupportsCreate() {
57
+ return this.anyObjectDeclares(o => !!o.CreateAPIPath && !!o.CreateMethod);
58
+ }
59
+ get SupportsUpdate() {
60
+ return this.anyObjectDeclares(o => !!o.UpdateAPIPath && !!o.UpdateMethod);
61
+ }
62
+ get SupportsDelete() {
63
+ return this.anyObjectDeclares(o => !!o.DeleteAPIPath && !!o.DeleteMethod);
64
+ }
65
+ /**
66
+ * Discovery is NON-authoritative: DiscoverObjects / IntrospectSchema are cache-driven (they re-read
67
+ * persisted ACTIVE Declared metadata, NOT a live full-gamut enumeration). Impexium exposes no
68
+ * describe-all / complete-gamut endpoint (GET Setup/customfields only enumerates the tenant's custom
69
+ * FIELD catalog, not a whole object/field gamut). Absence in a refresh proves nothing → never
70
+ * deactivate. Matches Configuration.DiscoveryIsAuthoritative=false in the frozen contract.
71
+ */
72
+ get DiscoveryIsAuthoritative() {
73
+ return false;
74
+ }
75
+ /**
76
+ * Sample-union field enrichment (the connector standard). Declared metadata is authoritative but
77
+ * incomplete — it misses a tenant's CUSTOM / undeclared columns (Impexium's per-tenant custom fields).
78
+ * Wire the required enrichment: take the base cache-driven schema, then per object UNION the declared
79
+ * field set with what a live read actually returns (`mergeDeclaredWithSampledFields` — never-shrink,
80
+ * declared-wins, capacities widened). Best-effort + parallel: a per-object sample failure leaves that
81
+ * object's declared fields untouched. Overrides IntrospectSchema (NEVER DiscoverFields — that would
82
+ * recurse into DiscoverFieldsViaFetch's fallback).
83
+ */
84
+ async IntrospectSchema(companyIntegration, contextUser) {
85
+ const info = await super.IntrospectSchema(companyIntegration, contextUser);
86
+ await Promise.all(info.Objects.map(async (obj) => {
87
+ try {
88
+ const sampled = await this.DiscoverFieldsViaFetch(companyIntegration, obj.ExternalName, contextUser);
89
+ obj.Fields = mergeDeclaredWithSampledFields(obj.Fields, sampled);
90
+ }
91
+ catch {
92
+ /* best-effort — a sample failure leaves the declared fields as-is */
93
+ }
94
+ }));
95
+ return info;
96
+ }
97
+ /** True when any cached IntegrationObject satisfies the predicate. Fail-safe read-only ([]→false). */
98
+ anyObjectDeclares(pred) {
99
+ try {
100
+ const integration = IntegrationEngineBase.Instance.GetIntegrationByName(INTEGRATION_NAME);
101
+ if (!integration)
102
+ return false;
103
+ return IntegrationEngineBase.Instance.GetActiveIntegrationObjects(integration.ID).some(pred);
104
+ }
105
+ catch {
106
+ return false;
107
+ }
108
+ }
109
+ // ── Abstract REST hooks ──────────────────────────────────────────────────
110
+ /**
111
+ * Authenticate against the RAW tenant host. Candidate A (default) runs the app + user handshake and
112
+ * caches the Access-Token + App-Token; candidate B ('apikey') resolves a single x-api-key. The cached
113
+ * context is re-minted when its tokens near expiry. NEVER inline crypto — the handshake is a plain
114
+ * credential POST returning tokens.
115
+ */
116
+ async Authenticate(companyIntegration, contextUser) {
117
+ if (this.cachedAuth && this.cachedAuth.ExpiresAtMs > Date.now() + TOKEN_REFRESH_BUFFER_MS) {
118
+ return this.cachedAuth;
119
+ }
120
+ const config = await this.parseConfig(companyIntegration, contextUser);
121
+ const host = this.resolveHost(config);
122
+ if (config.AuthModel === 'apikey') {
123
+ // Candidate B — single x-api-key. No handshake round-trip. Recorded as the cheaply swappable
124
+ // fallback; NOT silently assumed correct for the raw host (see design note).
125
+ this.cachedAuth = {
126
+ HostUrl: host,
127
+ AuthModel: 'apikey',
128
+ ApiKey: config.ApiKey,
129
+ AccessTokenHeader: config.AccessTokenHeader ?? DEFAULT_ACCESS_TOKEN_HEADER,
130
+ AppTokenHeader: config.AppTokenHeader ?? DEFAULT_APP_TOKEN_HEADER,
131
+ ApiKeyHeader: config.ApiKeyHeader ?? DEFAULT_API_KEY_HEADER,
132
+ ExpiresAtMs: Number.MAX_SAFE_INTEGER,
133
+ };
134
+ return this.cachedAuth;
135
+ }
136
+ // Candidate A — app + user handshake (UNVERIFIED; see design note). Tokens cached with expiry.
137
+ const tokens = await this.mintTokens(config, host);
138
+ this.cachedAuth = {
139
+ HostUrl: host,
140
+ AuthModel: 'app-user',
141
+ AccessToken: tokens.accessToken,
142
+ AppToken: tokens.appToken,
143
+ AccessTokenHeader: config.AccessTokenHeader ?? DEFAULT_ACCESS_TOKEN_HEADER,
144
+ AppTokenHeader: config.AppTokenHeader ?? DEFAULT_APP_TOKEN_HEADER,
145
+ ApiKeyHeader: config.ApiKeyHeader ?? DEFAULT_API_KEY_HEADER,
146
+ ExpiresAtMs: tokens.expiresAtMs,
147
+ };
148
+ return this.cachedAuth;
149
+ }
150
+ /**
151
+ * Attaches the auth headers per model: candidate A sends BOTH tokens (Access-Token + App-Token,
152
+ * header names Configuration-overridable); candidate B sends the single x-api-key.
153
+ */
154
+ BuildHeaders(auth) {
155
+ const ctx = auth;
156
+ const headers = {
157
+ 'Content-Type': 'application/json',
158
+ 'Accept': 'application/json',
159
+ };
160
+ if (ctx.AuthModel === 'apikey') {
161
+ if (ctx.ApiKey)
162
+ headers[ctx.ApiKeyHeader] = ctx.ApiKey;
163
+ return headers;
164
+ }
165
+ if (ctx.AccessToken)
166
+ headers[ctx.AccessTokenHeader] = ctx.AccessToken;
167
+ if (ctx.AppToken)
168
+ headers[ctx.AppTokenHeader] = ctx.AppToken;
169
+ return headers;
170
+ }
171
+ /** Per-tenant host resolved in Authenticate — ZERO host baked in code. */
172
+ GetBaseURL(_companyIntegration, auth) {
173
+ return auth.HostUrl;
174
+ }
175
+ /** HTTP transport (fetch). Owns the wire boundary; test subclasses override this to capture requests. */
176
+ async MakeHTTPRequest(auth, url, method, headers, body) {
177
+ const timeoutMs = auth?.TimeoutMs;
178
+ const init = {
179
+ method,
180
+ headers,
181
+ signal: AbortSignal.timeout(typeof timeoutMs === 'number' ? timeoutMs : DEFAULT_REQUEST_TIMEOUT_MS),
182
+ };
183
+ if (body !== undefined && method !== 'GET') {
184
+ init.body = typeof body === 'string' ? body : JSON.stringify(body);
185
+ }
186
+ const response = await fetch(url, init);
187
+ const respHeaders = {};
188
+ response.headers.forEach((v, k) => { respHeaders[k.toLowerCase()] = v; });
189
+ const text = await response.text();
190
+ let parsed = null;
191
+ if (text.length > 0) {
192
+ try {
193
+ parsed = JSON.parse(text);
194
+ }
195
+ catch {
196
+ parsed = text;
197
+ }
198
+ }
199
+ return { Status: response.status, Body: parsed, Headers: respHeaders };
200
+ }
201
+ /**
202
+ * Strips Impexium's list envelope `{ pageNumber, dataList: T[] }` to the dataList array
203
+ * (ResponseDataKey='dataList'). Falls back to a bare array, the universal dataList key, or a single
204
+ * detail object. The FULL source record passes through (no field filtering).
205
+ */
206
+ NormalizeResponse(rawBody, responseDataKey) {
207
+ if (rawBody == null)
208
+ return [];
209
+ if (Array.isArray(rawBody))
210
+ return rawBody;
211
+ if (typeof rawBody !== 'object')
212
+ return [];
213
+ const body = rawBody;
214
+ const key = responseDataKey ?? DATA_LIST_KEY;
215
+ const arr = body[key];
216
+ if (Array.isArray(arr))
217
+ return arr;
218
+ // Universal-envelope fallback (some IOs declare a null ResponseDataKey but still list under dataList).
219
+ if (Array.isArray(body[DATA_LIST_KEY]))
220
+ return body[DATA_LIST_KEY];
221
+ // Single-object detail response (get-one).
222
+ return [body];
223
+ }
224
+ /**
225
+ * Page-in-path pagination: the envelope carries NO hasMore/totalCount field, so termination is
226
+ * empty-dataList. HasMore is true iff the current page returned records; the loop advances the page
227
+ * number (rendered into the path by BuildPaginatedURL) and stops when a page comes back empty.
228
+ */
229
+ ExtractPaginationInfo(rawBody, paginationType, currentPage, _currentOffset, _pageSize) {
230
+ if (paginationType !== 'PageNumber')
231
+ return { HasMore: false };
232
+ const records = this.NormalizeResponse(rawBody, DATA_LIST_KEY);
233
+ return { HasMore: records.length > 0, NextPage: currentPage + 1 };
234
+ }
235
+ /**
236
+ * Impexium puts the page number IN THE PATH (a {Page Number}/{pageNumber}/{Page} segment), NEVER as a
237
+ * `?page=` query param. Substitute the page into that path segment and return — no query pagination.
238
+ */
239
+ BuildPaginatedURL(basePath, obj, page, _offset, _cursor, _effectivePageSize) {
240
+ if (obj.PaginationType !== 'PageNumber')
241
+ return basePath;
242
+ return this.substitutePagePlaceholder(basePath, page);
243
+ }
244
+ /**
245
+ * Injects the incremental watermark filter when a sync is active. The param NAME is fully
246
+ * METADATA-DRIVEN: it comes from the IO's IncrementalWatermarkField (changedSince / purchasedSince /
247
+ * registeredSince / cancelledSince) and is emitted only when SupportsIncrementalSync=true AND a
248
+ * watermark value is in context. Runs on top of the base's DefaultQueryParams handling.
249
+ */
250
+ AppendDefaultQueryParams(url, obj) {
251
+ return this.appendIncrementalParam(super.AppendDefaultQueryParams(url, obj), obj);
252
+ }
253
+ // ── FetchChanges override (page-in-path + nested parent access-path walk + watermark) ──
254
+ /**
255
+ * Fetch a batch. Flat top-level objects delegate to the base GET loop (which uses the BuildPaginatedURL
256
+ * / ExtractPaginationInfo / AppendDefaultQueryParams overrides above for page-in-path + incremental).
257
+ * Nested objects (whose frozen paths carry space-containing parent placeholders the base's `\w+`
258
+ * template detector can't resolve) are walked here: resolve the parent sibling IO from the preceding
259
+ * path RESOURCE segment, iterate synced parent records (bounded + keyset-resumable), substitute, then
260
+ * page the leaf. The watermark advances (partial-failure-safe) only on a fully-drained final batch.
261
+ */
262
+ async FetchChanges(ctx) {
263
+ const obj = this.GetCachedObject(ctx.CompanyIntegration.IntegrationID, ctx.ObjectName);
264
+ // NOTE: Impexium sub-resources (/Individuals/{id}/Addresses, /Emails, /Notes, /CustomFields, an
265
+ // Organization's /Services, /tasks, …) are RESTful read+write collections — GET lists them, POST
266
+ // creates. Their deployed CreateAPIPath legitimately EQUALS the read APIPath (same URL, different
267
+ // verb), so an "APIPath == write-path" test does NOT identify a write-only object and must NOT be
268
+ // used to skip the read. These objects sync via the normal (parent-chained) read path below.
269
+ this.currentWatermark = ctx.WatermarkValue ?? undefined;
270
+ // Capture the sync-start time BEFORE any fetch — a changed-since watermark advances to this on a
271
+ // clean full drain, so the next incremental picks up everything modified since this run began.
272
+ const watermarkStart = this.formatWatermark(new Date());
273
+ try {
274
+ const parentTokens = this.parentTokens(obj.APIPath);
275
+ const result = parentTokens.length === 0
276
+ ? await super.FetchChanges(ctx)
277
+ : await this.fetchWithParents(obj, parentTokens, ctx);
278
+ if (obj.SupportsIncrementalSync && obj.IncrementalWatermarkField && !result.HasMore) {
279
+ result.NewWatermarkValue = this.computeNewWatermark(ctx.WatermarkValue, watermarkStart);
280
+ }
281
+ return result;
282
+ }
283
+ finally {
284
+ this.currentWatermark = undefined;
285
+ }
286
+ }
287
+ /**
288
+ * Walks a nested object's access path: resolve the PRIMARY parent token → sibling IO (by the path
289
+ * segment preceding it), load the synced parent record IDs, process a bounded, keyset-resumable batch
290
+ * of parents, substitute + page each leaf. Surfaces a FetchWarning (never a silent empty) when the
291
+ * parent can't be resolved (Courses / tasks Users are not emitted IOs), when no parents are synced yet,
292
+ * or when a secondary parent token remains unresolved.
293
+ */
294
+ async fetchWithParents(obj, parentTokens, ctx) {
295
+ const fields = this.GetCachedFields(obj.ID);
296
+ const pkFieldNames = this.pkFieldNames(fields);
297
+ const auth = await this.Authenticate(ctx.CompanyIntegration, ctx.ContextUser);
298
+ const baseURL = this.GetBaseURL(ctx.CompanyIntegration, auth);
299
+ const integrationID = ctx.CompanyIntegration.IntegrationID;
300
+ const primaryToken = parentTokens[0];
301
+ const parentObjectName = this.resolveParentObjectName(obj.APIPath, primaryToken, integrationID);
302
+ if (!parentObjectName) {
303
+ return this.warnResult(obj, 'PARENT_UNRESOLVED', `"${obj.Name}": the parent for {${primaryToken}} resolves to no emitted IntegrationObject ` +
304
+ `(the preceding path resource segment is not a synced object) — cannot walk this access path.`);
305
+ }
306
+ const allParentIDs = this.sortIdsStable(await this.loadParentRecordIDs(parentObjectName, integrationID, ctx.ContextUser));
307
+ if (allParentIDs.length === 0) {
308
+ return this.warnResult(obj, 'ZERO_PARENTS', `"${obj.Name}": no "${parentObjectName}" parent records available to iterate {${primaryToken}} ` +
309
+ `over — sync the parent first (check DAG order/priority).`);
310
+ }
311
+ const after = ctx.AfterKeyValue ?? null;
312
+ const remaining = after != null ? allParentIDs.filter(id => this.compareIds(id, after) > 0) : allParentIDs;
313
+ const batch = remaining.slice(0, PARENT_BATCH_SIZE);
314
+ const out = [];
315
+ const warnings = [];
316
+ for (const parentID of batch) {
317
+ const resolvedPath = obj.APIPath.replace(`{${primaryToken}}`, encodeURIComponent(parentID));
318
+ const leftover = this.parentTokens(resolvedPath);
319
+ if (leftover.length > 0) {
320
+ warnings.push({
321
+ Code: 'PARENT_UNRESOLVED',
322
+ Message: `"${obj.Name}": secondary parent token {${leftover[0]}} could not be resolved — skipping this parent.`,
323
+ Data: { object: obj.Name, templateVar: leftover[0] },
324
+ });
325
+ continue;
326
+ }
327
+ const leafRecords = await this.fetchLeafPages(auth, baseURL, obj, resolvedPath);
328
+ for (const raw of leafRecords) {
329
+ // Emit the PURE source record — do NOT inject the parent id. `ExternalRecord.Fields` is the
330
+ // full-record pass-through of what the VENDOR returned; a connector-synthesized provenance tag
331
+ // is not source content. Critically, that tag flows into the content-hash basis (it is an
332
+ // unmapped key → overflow → change-detection), so tagging the same leaf record with the parent
333
+ // it was fetched under makes its hash depend on the TRAVERSAL PATH, not its content. When a
334
+ // leaf is reachable from multiple parents (a many-to-many child, or any source that returns
335
+ // the same child under several parents), the record is written once per parent with a
336
+ // different hash each time and re-writes every sync — breaking idempotency (content-hash skip
337
+ // never engages). Change-detection must be over the record's own content; the parent
338
+ // association, if needed, belongs in a mapped FK from real source data, not this tag.
339
+ out.push(this.buildExternalRecord(this.applyTransformPreservingKeys(raw, obj, fields), obj.Name, pkFieldNames));
340
+ }
341
+ }
342
+ const hasMore = remaining.length > batch.length;
343
+ return {
344
+ Records: out,
345
+ HasMore: hasMore,
346
+ NextAfterKeyValue: hasMore ? batch[batch.length - 1] : undefined,
347
+ Warnings: warnings.length > 0 ? warnings : undefined,
348
+ };
349
+ }
350
+ /**
351
+ * Drains ALL pages of a single resolved (parent-substituted) leaf path via page-in-path pagination,
352
+ * or a single request when the object is non-paginated. Stops on an empty dataList; skips gracefully
353
+ * on 403/404 (permission-gated / absent sub-resource). Injects the incremental watermark param.
354
+ */
355
+ async fetchLeafPages(auth, baseURL, obj, resolvedPath) {
356
+ const headers = this.BuildHeaders(auth);
357
+ const paginated = obj.SupportsPagination && obj.PaginationType !== 'None' && this.hasPageToken(resolvedPath);
358
+ if (!paginated) {
359
+ const url = this.appendIncrementalParam(this.joinURL(baseURL, resolvedPath), obj);
360
+ const response = await this.MakeHTTPRequest(auth, url, 'GET', headers);
361
+ if (this.isSkippableStatus(response.Status))
362
+ return [];
363
+ this.ensureOk(response, url);
364
+ return this.NormalizeResponse(response.Body, obj.ResponseDataKey);
365
+ }
366
+ const out = [];
367
+ let page = 1;
368
+ for (let i = 0; i < MAX_LEAF_PAGES; i++) {
369
+ const pathWithPage = this.substitutePagePlaceholder(resolvedPath, page);
370
+ const url = this.appendIncrementalParam(this.joinURL(baseURL, pathWithPage), obj);
371
+ const response = await this.MakeHTTPRequest(auth, url, 'GET', headers);
372
+ if (this.isSkippableStatus(response.Status))
373
+ break;
374
+ this.ensureOk(response, url);
375
+ const records = this.NormalizeResponse(response.Body, obj.ResponseDataKey);
376
+ if (records.length === 0)
377
+ break;
378
+ out.push(...records);
379
+ page++;
380
+ }
381
+ return out;
382
+ }
383
+ // ── CRUD (parent-scoped write paths — the base generic path can't substitute create-path parents) ──
384
+ /**
385
+ * Create. OVERRIDDEN because Impexium's create paths carry PARENT template vars ({code}/{id}/
386
+ * {Record Number}) filled from the record's own attributes — the generic base path leaves them literal.
387
+ * The body shape + loud-on-empty-id BuildCreatedResult are preserved. Top-level creates with no path
388
+ * vars (Individuals, Organizations, Tasks, Requests) behave exactly as the generic path would.
389
+ */
390
+ async CreateRecord(ctx) {
391
+ const ci = ctx.CompanyIntegration;
392
+ const contextUser = ctx.ContextUser;
393
+ const obj = this.GetCachedObject(ci.IntegrationID, ctx.ObjectName);
394
+ if (!obj.CreateAPIPath || !obj.CreateMethod)
395
+ return super.CreateRecord(ctx);
396
+ const auth = await this.Authenticate(ci, contextUser);
397
+ const baseURL = this.GetBaseURL(ci, auth);
398
+ const headers = this.BuildHeaders(auth);
399
+ const path = this.substitutePathVarsFromAttributes(obj.CreateAPIPath, ctx.Attributes);
400
+ const url = this.joinURL(baseURL, path);
401
+ const body = this.BuildOperationBody(ctx.Attributes, obj.CreateBodyShape, obj.CreateBodyKey);
402
+ const response = await this.MakeHTTPRequest(auth, url, obj.CreateMethod, headers, body);
403
+ if (response.Status >= 200 && response.Status < 300) {
404
+ const externalID = this.ExtractIDFromResponse(response, obj.CreateIDLocation);
405
+ return this.BuildCreatedResult(externalID, response.Status, ctx.ObjectName);
406
+ }
407
+ return {
408
+ Success: false,
409
+ StatusCode: response.Status,
410
+ ErrorMessage: this.ExtractErrorMessage(response) ?? `HTTP ${response.Status} on create`,
411
+ };
412
+ }
413
+ /**
414
+ * Update. OVERRIDDEN so parent path vars are filled from attributes and the path id ({id}/{Task Number}/
415
+ * {memberRecordNumber}/…) is filled from the ExternalID when UpdateIDLocation='path'. When
416
+ * UpdateIDLocation='body', the id is not path-substituted (the record body carries it).
417
+ */
418
+ async UpdateRecord(ctx) {
419
+ const ci = ctx.CompanyIntegration;
420
+ const contextUser = ctx.ContextUser;
421
+ const obj = this.GetCachedObject(ci.IntegrationID, ctx.ObjectName);
422
+ if (!obj.UpdateAPIPath || !obj.UpdateMethod)
423
+ return super.UpdateRecord(ctx);
424
+ const auth = await this.Authenticate(ci, contextUser);
425
+ const baseURL = this.GetBaseURL(ci, auth);
426
+ const headers = this.BuildHeaders(auth);
427
+ const path = this.substituteWritePath(obj.UpdateAPIPath, ctx.Attributes, ctx.ExternalID, obj.UpdateIDLocation);
428
+ const url = this.joinURL(baseURL, path);
429
+ const body = this.BuildOperationBody(ctx.Attributes, obj.UpdateBodyShape, obj.UpdateBodyKey);
430
+ const response = await this.MakeHTTPRequest(auth, url, obj.UpdateMethod, headers, body);
431
+ if (response.Status >= 200 && response.Status < 300) {
432
+ return { Success: true, StatusCode: response.Status, ExternalID: ctx.ExternalID };
433
+ }
434
+ return {
435
+ Success: false,
436
+ StatusCode: response.Status,
437
+ ErrorMessage: this.ExtractErrorMessage(response) ?? `HTTP ${response.Status} on update`,
438
+ };
439
+ }
440
+ /**
441
+ * Delete. OVERRIDDEN so the path id ({Category Code}) is filled from the ExternalID when
442
+ * DeleteIDLocation='path'. NOTE (documented limitation, flagged for T10 live verification): the only
443
+ * two delete-capable IOs (Categories, Links) are narrow sub-resources whose path ALSO carries a parent
444
+ * segment ({Record Number}) that the framework's DeleteRecordContext does not surface — a single-part
445
+ * ExternalID fills only the target id, so a live delete of these sub-resources needs the parent encoded
446
+ * in a composite ExternalID. Links additionally identifies the target via the request BODY
447
+ * (DeleteIDLocation='body'), which the delete context does not carry.
448
+ */
449
+ async DeleteRecord(ctx) {
450
+ const ci = ctx.CompanyIntegration;
451
+ const contextUser = ctx.ContextUser;
452
+ const obj = this.GetCachedObject(ci.IntegrationID, ctx.ObjectName);
453
+ if (!obj.DeleteAPIPath || !obj.DeleteMethod)
454
+ return super.DeleteRecord(ctx);
455
+ const auth = await this.Authenticate(ci, contextUser);
456
+ const baseURL = this.GetBaseURL(ci, auth);
457
+ const headers = this.BuildHeaders(auth);
458
+ const path = this.substituteWritePath(obj.DeleteAPIPath, {}, ctx.ExternalID, obj.DeleteIDLocation);
459
+ const url = this.joinURL(baseURL, path);
460
+ const response = await this.MakeHTTPRequest(auth, url, obj.DeleteMethod, headers);
461
+ if (response.Status >= 200 && response.Status < 300) {
462
+ return { Success: true, StatusCode: response.Status, ExternalID: ctx.ExternalID };
463
+ }
464
+ return {
465
+ Success: false,
466
+ StatusCode: response.Status,
467
+ ErrorMessage: this.ExtractErrorMessage(response) ?? `HTTP ${response.Status} on delete`,
468
+ };
469
+ }
470
+ /**
471
+ * Reads the created record's id from the Impexium create response. The universal envelope returns the
472
+ * new record under `id`; some sub-resource creates return the record with no server id
473
+ * (CreateIDLocation='n/a') — those route to BuildCreatedResult, which fails loudly on an empty id.
474
+ */
475
+ ExtractIDFromResponse(response, idLocation) {
476
+ if (idLocation === 'header')
477
+ return super.ExtractIDFromResponse(response, idLocation);
478
+ const body = response.Body;
479
+ if (!body || typeof body !== 'object')
480
+ return undefined;
481
+ const b = body;
482
+ const rootId = this.readId(b);
483
+ if (rootId)
484
+ return rootId;
485
+ // Some responses wrap the created record under the universal envelope or a single resource object.
486
+ const wrapped = b[DATA_LIST_KEY] ?? b['data'] ?? b['result'];
487
+ if (wrapped && typeof wrapped === 'object' && !Array.isArray(wrapped)) {
488
+ const id = this.readId(wrapped);
489
+ if (id)
490
+ return id;
491
+ }
492
+ return undefined;
493
+ }
494
+ // ── Connection test ──────────────────────────────────────────────────────
495
+ /**
496
+ * Tests connectivity by authenticating and reading one page of Individuals. A 2xx confirms the auth
497
+ * flow + tenant host are valid; 401/403 → auth failure; anything else → error. Never mutates.
498
+ */
499
+ async TestConnection(companyIntegration, contextUser) {
500
+ try {
501
+ const auth = await this.Authenticate(companyIntegration, contextUser);
502
+ const baseURL = this.GetBaseURL(companyIntegration, auth);
503
+ const headers = this.BuildHeaders(auth);
504
+ const url = this.joinURL(baseURL, '/api/v1/Individuals/1');
505
+ const response = await this.MakeHTTPRequest(auth, url, 'GET', headers);
506
+ if (response.Status >= 200 && response.Status < 300) {
507
+ return { Success: true, Message: `Connected to re:Members AMS at ${baseURL}` };
508
+ }
509
+ if (response.Status === 401 || response.Status === 403) {
510
+ return {
511
+ Success: false,
512
+ Message: `re:Members AMS authentication failed (HTTP ${response.Status}). ` +
513
+ `Verify the AppId/AppKey/AppName + user login (or the auth model) and the tenant HostUrl.`,
514
+ };
515
+ }
516
+ return { Success: false, Message: `re:Members AMS connection test returned HTTP ${response.Status}.` };
517
+ }
518
+ catch (err) {
519
+ const msg = err instanceof Error ? err.message : String(err);
520
+ return { Success: false, Message: `re:Members AMS connection test error: ${msg}` };
521
+ }
522
+ }
523
+ // ─────────────────────────────────────────────────────────────────────────
524
+ // Private helpers
525
+ // ─────────────────────────────────────────────────────────────────────────
526
+ // ── Auth handshake ───────────────────────────────────────────────────────
527
+ /**
528
+ * Runs the candidate-A app + user handshake: POST {AppId, AppKey, AppName, Email, Password} to the auth
529
+ * path and read the Access-Token + App-Token (+ optional expiry) from the response. UNVERIFIED at plan
530
+ * time — the endpoint path, request field names, response field names and TTL are all overridable, so a
531
+ * live/sandbox probe can settle them without a code change. Plain credential POST; no crypto.
532
+ */
533
+ async mintTokens(config, host) {
534
+ const authPath = config.AuthPath ?? DEFAULT_AUTH_PATH;
535
+ const url = this.joinURL(host, authPath);
536
+ const requestHeaders = { 'Content-Type': 'application/json', 'Accept': 'application/json' };
537
+ const requestBody = {
538
+ AppId: config.AppId,
539
+ AppKey: config.AppKey,
540
+ AppName: config.AppName,
541
+ Email: config.Email,
542
+ Password: config.Password,
543
+ };
544
+ // A bare auth context (no tokens) — the mint sends credentials in the body, not headers.
545
+ const bareAuth = { HostUrl: host, AuthModel: 'app-user' };
546
+ const response = await this.MakeHTTPRequest(bareAuth, url, 'POST', requestHeaders, requestBody);
547
+ if (response.Status < 200 || response.Status >= 300) {
548
+ throw new Error(`re:Members AMS app+user handshake failed at ${url}: HTTP ${response.Status}. ` +
549
+ `(Auth flow is UNVERIFIED — confirm the endpoint/field names against a live tenant.)`);
550
+ }
551
+ const body = (response.Body && typeof response.Body === 'object' ? response.Body : {});
552
+ const accessToken = this.firstString(body, ['AccessToken', 'accessToken', 'access_token', 'Token', 'token']);
553
+ if (!accessToken) {
554
+ throw new Error(`re:Members AMS handshake returned HTTP ${response.Status} but no access token was found in the response. ` +
555
+ `(Auth flow is UNVERIFIED — confirm the token field name.)`);
556
+ }
557
+ const appToken = this.firstString(body, ['AppToken', 'appToken', 'app_token', 'ApplicationToken', 'applicationToken']);
558
+ const expiresAtMs = this.resolveTokenExpiry(body, config);
559
+ return { accessToken, appToken, expiresAtMs };
560
+ }
561
+ /** Derives the token expiry from the handshake response, falling back to the configured/default TTL. */
562
+ resolveTokenExpiry(body, config) {
563
+ const ttl = config.TokenTTLMs ?? DEFAULT_TOKEN_TTL_MS;
564
+ // Absolute expiry (ISO string) — common vendor field names.
565
+ const absolute = this.firstString(body, ['AccessTokenExpiration', 'accessTokenExpiration', 'expiresAt', 'ExpiresAt']);
566
+ if (absolute) {
567
+ const t = Date.parse(absolute);
568
+ if (Number.isFinite(t) && t > Date.now())
569
+ return t;
570
+ }
571
+ // Relative expiry (seconds).
572
+ for (const key of ['ExpiresIn', 'expiresIn', 'expires_in']) {
573
+ const v = body[key];
574
+ if (typeof v === 'number' && Number.isFinite(v) && v > 0)
575
+ return Date.now() + v * 1_000;
576
+ }
577
+ return Date.now() + ttl;
578
+ }
579
+ // ── Config parsing ───────────────────────────────────────────────────────
580
+ /** Parses the connection config, preferring the linked Credential entity over the Configuration JSON. */
581
+ async parseConfig(companyIntegration, contextUser) {
582
+ let fromCredential = null;
583
+ if (companyIntegration.CredentialID) {
584
+ fromCredential = await this.loadCredentialValues(companyIntegration.CredentialID, contextUser);
585
+ }
586
+ let fromConfiguration = null;
587
+ if (companyIntegration.Configuration) {
588
+ try {
589
+ fromConfiguration = JSON.parse(companyIntegration.Configuration);
590
+ }
591
+ catch {
592
+ fromConfiguration = null;
593
+ }
594
+ }
595
+ if (!fromCredential && !fromConfiguration) {
596
+ throw new Error('re:Members AMS connector requires either a linked Credential or Configuration JSON.');
597
+ }
598
+ // Merge: Configuration provides non-secret keys (HostUrl, AuthModel, header overrides); the
599
+ // Credential provides secrets (AppId/AppKey/Email/Password/ApiKey). Credential wins on overlap.
600
+ const merged = { ...(fromConfiguration ?? {}), ...(fromCredential ?? {}) };
601
+ return this.buildConfig(merged);
602
+ }
603
+ /** Loads a credential row and parses its Values JSON to a plain map. */
604
+ async loadCredentialValues(credentialID, contextUser, provider) {
605
+ const md = provider ?? new Metadata();
606
+ const cred = await md.GetEntityObject('MJ: Credentials', contextUser);
607
+ const loaded = await cred.Load(credentialID);
608
+ if (!loaded || !cred.Values)
609
+ return null;
610
+ try {
611
+ return JSON.parse(cred.Values);
612
+ }
613
+ catch {
614
+ return null;
615
+ }
616
+ }
617
+ /** Builds the typed config from a raw case-insensitive map, applying defaults + validating HostUrl. */
618
+ buildConfig(raw) {
619
+ const hostUrl = this.firstString(raw, ['HostUrl', 'hostUrl', 'hosturl', 'BaseURL', 'baseURL', 'host']);
620
+ if (!hostUrl) {
621
+ throw new Error('re:Members AMS configuration missing required "HostUrl" (the per-tenant https://{tenant}.mpxapi.com host).');
622
+ }
623
+ const authModelRaw = (this.firstString(raw, ['AuthModel', 'authModel']) ?? 'app-user').toLowerCase();
624
+ const authModel = authModelRaw === 'apikey' || authModelRaw === 'api-key' ? 'apikey' : 'app-user';
625
+ const timeout = this.firstNumber(raw, ['RequestTimeoutMs', 'requestTimeoutMs']);
626
+ const ttl = this.firstNumber(raw, ['TokenTTLMs', 'tokenTTLMs']);
627
+ return {
628
+ HostUrl: hostUrl,
629
+ AuthModel: authModel,
630
+ AppId: this.firstString(raw, ['AppId', 'appId', 'appid']),
631
+ AppKey: this.firstString(raw, ['AppKey', 'appKey', 'appkey']),
632
+ AppName: this.firstString(raw, ['AppName', 'appName', 'appname']),
633
+ Email: this.firstString(raw, ['Email', 'email', 'Username', 'username', 'UserEmail', 'userEmail']),
634
+ Password: this.firstString(raw, ['Password', 'password']),
635
+ AuthPath: this.firstString(raw, ['AuthPath', 'authPath']),
636
+ AccessTokenHeader: this.firstString(raw, ['AccessTokenHeader', 'accessTokenHeader']),
637
+ AppTokenHeader: this.firstString(raw, ['AppTokenHeader', 'appTokenHeader']),
638
+ ApiKey: this.firstString(raw, ['ApiKey', 'apiKey', 'apikey', 'x-api-key']),
639
+ ApiKeyHeader: this.firstString(raw, ['ApiKeyHeader', 'apiKeyHeader']),
640
+ RequestTimeoutMs: timeout,
641
+ TokenTTLMs: ttl,
642
+ };
643
+ }
644
+ /** Resolves the per-tenant host (no trailing slash) — never a hardcoded host. */
645
+ resolveHost(config) {
646
+ return config.HostUrl.trim().replace(/\/+$/, '');
647
+ }
648
+ // ── Path / token helpers ─────────────────────────────────────────────────
649
+ /** All `{...}` placeholders in a path (space-tolerant), unwrapped. */
650
+ allTokens(path) {
651
+ return (path.match(/\{[^}]+\}/g) ?? []).map(s => s.slice(1, -1));
652
+ }
653
+ /** True for the page placeholder (`{Page Number}` / `{pageNumber}` / `{Page}`) — space/case tolerant. */
654
+ isPageToken(name) {
655
+ const n = name.toLowerCase().replace(/\s+/g, '');
656
+ return n === 'pagenumber' || n === 'page';
657
+ }
658
+ /** Parent (non-page) placeholders in a path. */
659
+ parentTokens(path) {
660
+ return this.allTokens(path).filter(t => !this.isPageToken(t));
661
+ }
662
+ /** True when the path still carries a page placeholder. */
663
+ hasPageToken(path) {
664
+ return this.allTokens(path).some(t => this.isPageToken(t));
665
+ }
666
+ /** Substitutes the page placeholder (space/case-tolerant) with the page number. */
667
+ substitutePagePlaceholder(path, page) {
668
+ return path.replace(/\{[^}]+\}/g, (m) => this.isPageToken(m.slice(1, -1)) ? String(page) : m);
669
+ }
670
+ /**
671
+ * Resolves the parent object name for a template var from the path segment IMMEDIATELY preceding the
672
+ * `{token}` (Impexium's REST convention: the parent resource name precedes its id), matched against a
673
+ * sibling IntegrationObject by name. Returns null when no emitted sibling matches (e.g. Courses, Users).
674
+ * Protected so tests can resolve a parent without a populated engine cache.
675
+ */
676
+ resolveParentObjectName(apiPath, token, integrationID) {
677
+ const idx = apiPath.indexOf(`{${token}}`);
678
+ if (idx < 0)
679
+ return null;
680
+ const segment = apiPath.slice(0, idx).replace(/\/+$/, '').split('/').pop() ?? '';
681
+ if (!segment)
682
+ return null;
683
+ const siblings = IntegrationEngineBase.Instance.GetActiveIntegrationObjects(integrationID);
684
+ const match = siblings.find(s => s.Name.toLowerCase() === segment.toLowerCase());
685
+ return match ? match.Name : null;
686
+ }
687
+ /**
688
+ * Loads the synced parent record IDs from the local MJ database (via the object's EntityMap → the target
689
+ * entity's PK column). Protected so tests can inject fixed parent IDs without a live RunView.
690
+ */
691
+ async loadParentRecordIDs(parentObjectName, integrationID, contextUser) {
692
+ const parentObj = IntegrationEngineBase.Instance.GetIntegrationObject(integrationID, parentObjectName);
693
+ if (!parentObj)
694
+ return [];
695
+ const pkField = this.GetCachedFields(parentObj.ID).find(f => f.IsPrimaryKey);
696
+ const pkFieldName = pkField ? pkField.Name : 'id';
697
+ const rv = new RunView();
698
+ const mapResult = await rv.RunView({
699
+ EntityName: 'MJ: Company Integration Entity Maps',
700
+ ExtraFilter: `ExternalObjectName='${parentObjectName.replace(/'/g, "''")}' AND SyncEnabled=1`,
701
+ Fields: ['Entity'],
702
+ MaxRows: 1,
703
+ ResultType: 'simple',
704
+ }, contextUser);
705
+ if (!mapResult.Success || mapResult.Results.length === 0)
706
+ return [];
707
+ const idResult = await rv.RunView({
708
+ EntityName: mapResult.Results[0].Entity,
709
+ Fields: [pkFieldName],
710
+ ResultType: 'simple',
711
+ }, contextUser);
712
+ if (!idResult.Success)
713
+ return [];
714
+ return idResult.Results.map(r => String(r[pkFieldName]));
715
+ }
716
+ /** Fills a CreateAPIPath's parent template vars from the record's own attributes (case/space-insensitive). */
717
+ substitutePathVarsFromAttributes(path, attributes) {
718
+ return path.replace(/\{([^}]+)\}/g, (match, name) => {
719
+ const v = this.lookupAttr(attributes, name);
720
+ return v != null && String(v).length > 0 ? encodeURIComponent(String(v)) : match;
721
+ });
722
+ }
723
+ /**
724
+ * Fills a write (update/delete) path: parent vars from attributes first, then the remaining path id from
725
+ * the (possibly composite) ExternalID when idLocation is 'path'. When idLocation is 'body'/'header', the
726
+ * id is NOT path-substituted (the record body / a header carries it).
727
+ */
728
+ substituteWritePath(path, attributes, externalID, idLocation) {
729
+ let out = this.substitutePathVarsFromAttributes(path, attributes);
730
+ if (externalID && (idLocation == null || idLocation === 'path')) {
731
+ const remaining = this.allTokens(out);
732
+ if (remaining.length > 0) {
733
+ const parts = externalID.split('|');
734
+ remaining.forEach((t, i) => {
735
+ const value = parts[i] ?? parts[parts.length - 1] ?? externalID;
736
+ out = out.replace(`{${t}}`, encodeURIComponent(value));
737
+ });
738
+ }
739
+ }
740
+ return out;
741
+ }
742
+ /** Looks up an attribute by name, matching case- and space-insensitively (`{Record Number}` → recordNumber). */
743
+ lookupAttr(attributes, name) {
744
+ const norm = (s) => s.toLowerCase().replace(/\s+/g, '');
745
+ const target = norm(name);
746
+ for (const [k, v] of Object.entries(attributes)) {
747
+ if (norm(k) === target)
748
+ return v;
749
+ }
750
+ return undefined;
751
+ }
752
+ // ── URL + response helpers ───────────────────────────────────────────────
753
+ /** Joins a base URL and a path (mirrors the base's private BuildFullURL). */
754
+ joinURL(baseURL, apiPath) {
755
+ const base = baseURL.endsWith('/') ? baseURL.slice(0, -1) : baseURL;
756
+ const path = apiPath.startsWith('/') ? apiPath : `/${apiPath}`;
757
+ return `${base}${path}`;
758
+ }
759
+ /** Appends the metadata-driven incremental watermark query param when active. */
760
+ appendIncrementalParam(url, obj) {
761
+ const field = obj.IncrementalWatermarkField;
762
+ if (obj.SupportsIncrementalSync && field && this.currentWatermark) {
763
+ const sep = url.includes('?') ? '&' : '?';
764
+ return `${url}${sep}${encodeURIComponent(field)}=${encodeURIComponent(this.currentWatermark)}`;
765
+ }
766
+ return url;
767
+ }
768
+ /** 403/404 are skippable (permission-gated object / absent sub-resource) — degrade to zero rows. */
769
+ isSkippableStatus(status) {
770
+ return status === 403 || status === 404;
771
+ }
772
+ /** Throws a descriptive error on a non-2xx (non-skippable) status. */
773
+ ensureOk(response, url) {
774
+ if (response.Status < 200 || response.Status >= 300) {
775
+ const preview = typeof response.Body === 'string'
776
+ ? response.Body.slice(0, 300)
777
+ : JSON.stringify(response.Body ?? '').slice(0, 300);
778
+ throw new Error(`[re:Members AMS] HTTP ${response.Status} from ${url}: ${preview}`);
779
+ }
780
+ }
781
+ // ── Record + identity helpers ────────────────────────────────────────────
782
+ /** PK field names in Sequence order; empty when the object declares no PK (→ content-hash identity). */
783
+ pkFieldNames(fields) {
784
+ return fields.filter(f => f.IsPrimaryKey).sort((a, b) => a.Sequence - b.Sequence).map(f => f.Name);
785
+ }
786
+ /**
787
+ * Builds an ExternalRecord with the FULL source record in Fields (full-record pass-through — the
788
+ * framework's custom-column capture diffs keys(Fields) against the field maps). ExternalID is the
789
+ * composite PK when every component is present + non-empty, else a deterministic content hash (which is
790
+ * also stamped into a single empty PK column so PK-less rows remain storable + idempotent). Mirrors the
791
+ * base's ToExternalRecord (which is private) for the parent-iterated path.
792
+ */
793
+ buildExternalRecord(raw, objectType, pkFieldNames) {
794
+ const allPkPresent = pkFieldNames.length > 0
795
+ && pkFieldNames.every(n => raw[n] != null && serializeKeyValue(raw[n]).length > 0);
796
+ const externalID = pkFieldNames.map(n => serializeKeyValue(raw[n])).join('|');
797
+ const resolvedID = allPkPresent ? externalID : computeContentHash(raw);
798
+ let fields = raw;
799
+ if (!allPkPresent && pkFieldNames.length === 1
800
+ && (raw[pkFieldNames[0]] == null || serializeKeyValue(raw[pkFieldNames[0]]).length === 0)) {
801
+ fields = { ...raw, [pkFieldNames[0]]: resolvedID };
802
+ }
803
+ return { ExternalID: resolvedID, ObjectType: objectType, Fields: fields };
804
+ }
805
+ /** Builds a warning-only fetch result (surfaces a silent-empty as a structured SyncWarning). */
806
+ warnResult(obj, code, message) {
807
+ return { Records: [], HasMore: false, Warnings: [{ Code: code, Message: message, Data: { object: obj.Name } }] };
808
+ }
809
+ // ── Watermark helpers ────────────────────────────────────────────────────
810
+ /**
811
+ * The next watermark for a changed-since object is the time this sync STARTED (captured before any
812
+ * fetch), so the next incremental picks up everything modified since — a slight overlap is idempotent.
813
+ * Impexium's list responses carry no reliable single modified-date field to derive it from, so wall-clock
814
+ * start is the safe, monotonic choice. Only ever advances forward.
815
+ */
816
+ computeNewWatermark(current, startTs) {
817
+ if (current && startTs <= current)
818
+ return current;
819
+ return startTs;
820
+ }
821
+ /** Formats a date as the Impexium changed-since format `yyyy-MM-ddTHH:mm:ss` (UTC). */
822
+ formatWatermark(date) {
823
+ return date.toISOString().replace(/\.\d+Z$/, '');
824
+ }
825
+ // ── Small utilities ──────────────────────────────────────────────────────
826
+ /** Returns the first present, non-empty string value among the given keys. */
827
+ firstString(obj, keys) {
828
+ for (const k of keys) {
829
+ const v = obj[k];
830
+ if (typeof v === 'string' && v.length > 0)
831
+ return v;
832
+ }
833
+ return undefined;
834
+ }
835
+ /** Returns the first present finite number among the given keys. */
836
+ firstNumber(obj, keys) {
837
+ for (const k of keys) {
838
+ const v = obj[k];
839
+ if (typeof v === 'number' && Number.isFinite(v))
840
+ return v;
841
+ }
842
+ return undefined;
843
+ }
844
+ /** Reads an `id`-like value as a string; undefined when absent/empty. */
845
+ readId(o) {
846
+ for (const k of ['id', 'ID', 'Id']) {
847
+ const v = o[k];
848
+ if (typeof v === 'string' && v.length > 0)
849
+ return v;
850
+ if (typeof v === 'number')
851
+ return String(v);
852
+ }
853
+ return undefined;
854
+ }
855
+ /** Stable total order over ID strings — numeric when ALL are integer-like, else lexical. */
856
+ sortIdsStable(ids) {
857
+ return [...ids].sort((a, b) => this.compareIds(a, b));
858
+ }
859
+ /** Total-order comparator matching sortIdsStable, used for keyset resume (id > AfterKeyValue). */
860
+ compareIds(a, b) {
861
+ if (/^\d+$/.test(a) && /^\d+$/.test(b)) {
862
+ const na = Number(a), nb = Number(b);
863
+ return na === nb ? 0 : (na < nb ? -1 : 1);
864
+ }
865
+ return a < b ? -1 : (a > b ? 1 : 0);
866
+ }
867
+ };
868
+ ImpexiumConnector = __decorate([
869
+ RegisterClass(BaseIntegrationConnector, 'ImpexiumConnector')
870
+ ], ImpexiumConnector);
871
+ export { ImpexiumConnector };
872
+ /** Tree-shaking prevention — import and call from the package entry point. */
873
+ export function LoadImpexiumConnector() { }
874
+ //# sourceMappingURL=ImpexiumConnector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ImpexiumConnector.js","sourceRoot":"","sources":["../src/ImpexiumConnector.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export * from './ImpexiumConnector.js';
2
+ /** Open App bootstrap entry: importing this module ran the connector's @RegisterClass decorator;
3
+ * this no-op satisfies the loader's required startupExport and forces the import at MJAPI boot. */
4
+ export declare function registerConnector(): void;
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ export * from './ImpexiumConnector.js';
2
+ /** Open App bootstrap entry: importing this module ran the connector's @RegisterClass decorator;
3
+ * this no-op satisfies the loader's required startupExport and forces the import at MJAPI boot. */
4
+ export function registerConnector() { }
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,wBAAwB,CAAC;AAEvC;oGACoG;AACpG,MAAM,UAAU,iBAAiB,KAAiD,CAAC"}
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@memberjunction/connector-impexium",
3
+ "version": "1.0.0",
4
+ "description": "MemberJunction Impexium connector.",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "files": [
9
+ "/dist"
10
+ ],
11
+ "scripts": {
12
+ "build": "tsc && tsc-alias -f",
13
+ "test": "vitest run"
14
+ },
15
+ "author": "MemberJunction.com",
16
+ "license": "ISC",
17
+ "peerDependencies": {
18
+ "@memberjunction/core": ">=5.43.0 <6.0.0",
19
+ "@memberjunction/core-entities": ">=5.43.0 <6.0.0",
20
+ "@memberjunction/global": ">=5.43.0 <6.0.0",
21
+ "@memberjunction/integration-engine": ">=5.43.0 <6.0.0",
22
+ "@memberjunction/integration-engine-base": ">=5.43.0 <6.0.0"
23
+ },
24
+ "dependencies": {
25
+ "@memberjunction/connector-schema-merge": "^1.0.0"
26
+ },
27
+ "devDependencies": {
28
+ "@memberjunction/connector-schema-merge": "^1.0.0",
29
+ "@memberjunction/core": "^5.43.0",
30
+ "@memberjunction/core-entities": "^5.43.0",
31
+ "@memberjunction/global": "^5.43.0",
32
+ "@memberjunction/integration-engine": "^5.43.0",
33
+ "@memberjunction/integration-engine-base": "^5.43.0",
34
+ "@types/node": "24.10.11",
35
+ "tsc-alias": "^1.8.16",
36
+ "typescript": "^5.9.3",
37
+ "vitest": "^4.0.18"
38
+ },
39
+ "repository": {
40
+ "type": "git",
41
+ "url": "https://github.com/MemberJunction/Integrations"
42
+ }
43
+ }