@memberjunction/connector-higher-logic-vanilla 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,179 @@
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 CRUDResult, type RateLimitPolicy, type SourceSchemaInfo } from '@memberjunction/integration-engine';
4
+ /**
5
+ * Higher Logic Vanilla connector — extends BaseRESTIntegrationConnector (REST/JSON over HTTP).
6
+ *
7
+ * Discovery, the paginated GET loop, template-var read traversal (second-layer objects resolve their
8
+ * parent via metadata), and generic per-operation CRUD are inherited. This class supplies only the
9
+ * Vanilla-specific protocol surface: Bearer auth, the per-tenant base URL, numbered + Link-header
10
+ * pagination, the `dateUpdated`/`dateInserted` incremental filter, connection testing, PK-aware create
11
+ * ID extraction, named/composite path-var substitution, and the §7/§10 sync-efficiency hooks the frozen
12
+ * contract evidences.
13
+ */
14
+ export declare class HigherLogicVanillaConnector extends BaseRESTIntegrationConnector {
15
+ /** Cached auth for the lifetime of a single sync run (Personal Access Tokens are long-lived, no refresh). */
16
+ private cachedAuth;
17
+ /**
18
+ * Response headers from the MOST RECENT MakeHTTPRequest, stashed so the (ctx-less) ExtractPaginationInfo
19
+ * can read the RFC-5988 `Link` header. Safe: the base pagination loop calls MakeHTTPRequest and
20
+ * ExtractPaginationInfo back-to-back with NO intervening await, so on a single-threaded event loop the
21
+ * stashed headers always correspond to the page just parsed.
22
+ */
23
+ private lastResponseHeaders;
24
+ /**
25
+ * The active incremental filter fragment (e.g. `dateUpdated=%3E%3D<iso>`) for the object currently being
26
+ * fetched, stashed by the FetchChanges override so the (ctx-less) BuildPaginatedURL can append it. Set at
27
+ * the top of an incremental FetchChanges, cleared in its finally. Safe because the engine drives one
28
+ * FetchChanges per object at a time (single-threaded async; no concurrent BuildPaginatedURL for a
29
+ * different watermark).
30
+ */
31
+ private activeWatermarkFilter;
32
+ /** Verbatim `MJ: Integrations.Name`. Load-bearing: the T1 three-way name check compares this === metadata Name. */
33
+ get IntegrationName(): string;
34
+ /**
35
+ * Sample-union enrichment (MJ connector standard): the Declared metadata is docs/spec-derived and misses
36
+ * a tenant's custom profile fields (Vanilla ProfileField extensions surface as extra keys on user/record
37
+ * reads) and addon-specific attributes. After the base cache-driven introspection, we sample each
38
+ * object's live read shape via `DiscoverFieldsViaFetch` and UNION it into the declared field set with
39
+ * `mergeDeclaredWithSampledFields` — never-shrink, declared-wins, capacities widened. Best-effort +
40
+ * parallel; a sample failure (no credential, addon disabled) leaves that object's declared fields intact.
41
+ * NOTE: we override `IntrospectSchema` (NOT `DiscoverFields` — that would recurse into
42
+ * `DiscoverFieldsViaFetch`'s own fallback). Connector-agnostic: no Vanilla-specific field logic.
43
+ */
44
+ IntrospectSchema(companyIntegration: MJCompanyIntegrationEntity, contextUser: UserInfo): Promise<SourceSchemaInfo>;
45
+ get SupportsCreate(): boolean;
46
+ get SupportsUpdate(): boolean;
47
+ get SupportsDelete(): boolean;
48
+ /**
49
+ * Discovery is NON-authoritative: DiscoverObjects / IntrospectSchema are cache-driven (they re-read
50
+ * persisted ACTIVE Declared metadata, NOT a live full-gamut enumeration). Vanilla exposes no
51
+ * describe/introspection endpoint enumerating everything a credential can access, and a tenant's custom
52
+ * ProfileFields are knowable only from data — they flow through the sample-union above + the framework's
53
+ * runtime custom-column capture. So absence in a refresh proves nothing → never deactivate. Matches
54
+ * Configuration.DiscoveryIsAuthoritative=false in the frozen contract.
55
+ */
56
+ get DiscoveryIsAuthoritative(): boolean;
57
+ /**
58
+ * From Configuration.RateLimitPolicy (KB article 44): GET is 300 req/min per IP (=5/s), writes 120/min
59
+ * (=2/s). A separate HARD limit — 250 requests / 10s — triggers a MANUAL-intervention IP block (not an
60
+ * auto-lifting 429), so we stay well under it: 5/s sustained (the GET ceiling) with a small burst. The
61
+ * engine's AIMD bucket backs off further on any 429.
62
+ */
63
+ get RateLimitPolicy(): RateLimitPolicy | null;
64
+ /** Conservative in-flight cap. The per-minute + hard-block ceilings are the real limiters. */
65
+ get MaxConcurrencyHint(): number | null;
66
+ /**
67
+ * No-watermark objects resume by their StableOrderingKey — read from the IO metadata when the extractor
68
+ * emitted one, else the object's declared PK (Vanilla's `<object>ID`). Returns null when the object
69
+ * declares no stable key or the cache is unavailable (unit-test context).
70
+ */
71
+ StableOrderingKey(objectName: string): string | null;
72
+ /**
73
+ * Resolves the Bearer token + the per-tenant community base URL from the linked Credential entity
74
+ * (preferred) or the CompanyIntegration Configuration JSON (fallback). Cached for the run.
75
+ */
76
+ protected Authenticate(companyIntegration: MJCompanyIntegrationEntity, contextUser: UserInfo): Promise<RESTAuthContext>;
77
+ /** Vanilla auth: the Personal Access Token as a Bearer header. A static string — no signing, no crypto. */
78
+ protected BuildHeaders(auth: RESTAuthContext): Record<string, string>;
79
+ /** HTTP transport (fetch). Owns the wire boundary; test subclasses override this to capture requests. */
80
+ protected MakeHTTPRequest(_auth: RESTAuthContext, url: string, method: string, headers: Record<string, string>, body?: unknown): Promise<RESTResponse>;
81
+ /**
82
+ * Vanilla list endpoints return a BARE JSON ARRAY of records at the response root (ResponseDataKey=null
83
+ * for almost every object). A few wrap under a key (e.g. ProductMessage → ResponseDataKey='data'). The
84
+ * get-one shape is a single bare object. Handles all three.
85
+ */
86
+ protected NormalizeResponse(rawBody: unknown, responseDataKey: string | null): Record<string, unknown>[];
87
+ /**
88
+ * Vanilla pagination (Configuration.PaginationDefaults): numbered pages whose authoritative "more data"
89
+ * signal is the RFC-5988 `Link` response header (`<url>; rel="next"`), NOT a body field. We read the Link
90
+ * header from the stashed headers (with the `x-app-page-next-url` header as a secondary signal, and a
91
+ * body-length heuristic as the last-resort fallback). For PageNumber the next page is page+1 (Vanilla's
92
+ * next URL is deterministic); for the one Cursor-typed object we surface the next-page URL verbatim as the
93
+ * cursor so BuildPaginatedURL follows it directly.
94
+ */
95
+ protected ExtractPaginationInfo(rawBody: unknown, paginationType: PaginationType, currentPage: number, _currentOffset: number, pageSize: number): PaginationState;
96
+ /**
97
+ * Per-tenant base host + version path: `{communityUrl}/api/v2`. Composed in Authenticate (honoring a
98
+ * Configuration BaseURL override for sandbox/mock redirection) and stashed on the auth context. The
99
+ * object APIPaths (`/discussions`, …) are appended by the base BuildFullURL.
100
+ */
101
+ protected GetBaseURL(_companyIntegration: MJCompanyIntegrationEntity, auth: RESTAuthContext): string;
102
+ /**
103
+ * Vanilla pagination params: `page=<n>&limit=<size>` (NOT the base default `page`/`pageSize`). When the
104
+ * incremental watermark filter is active this run, it is appended (`dateUpdated=>=<iso>`). For the
105
+ * Cursor-typed object the base hands back a FULL next-page URL as the cursor — return it verbatim.
106
+ */
107
+ protected BuildPaginatedURL(basePath: string, obj: MJIntegrationObjectEntity, page: number, _offset: number, cursor?: string, effectivePageSize?: number): string;
108
+ /**
109
+ * OVERRIDDEN to (1) inject the `<IncrementalWatermarkField>=>=<watermark>` list filter for objects that
110
+ * declare SupportsIncrementalSync + carry a watermark this run, and (2) EMIT the new watermark. The base
111
+ * flat/template fetch threads no watermark into the URL and returns no NewWatermarkValue, so the connector
112
+ * owns both. All fetching (numbered pagination, template-var parent traversal) is delegated to the base;
113
+ * this override only sets activeWatermarkFilter around the super call and computes the max on a drained batch.
114
+ *
115
+ * Partial-failure safety: NewWatermarkValue is emitted ONLY when the whole object is drained
116
+ * (HasMore=false). A mid-stream batch advances no watermark, so a failure between batches resumes from the
117
+ * unchanged prior watermark. The `>=` operator (not `>`) means the boundary record re-syncs each run —
118
+ * idempotent (content-hash dedup) and never loses a record that shares the boundary timestamp.
119
+ */
120
+ FetchChanges(ctx: FetchContext): Promise<FetchBatchResult>;
121
+ /**
122
+ * OVERRIDDEN for two Vanilla create idiosyncrasies the generic path can't express:
123
+ * 1. Vanilla's created-record ID field is `<object>ID` (discussionID / commentID / userID), NOT a plain
124
+ * `id`, so the base's ExtractIDFromResponse (which scans for id/ID/externalID) would return undefined
125
+ * and BuildCreatedResult would FAIL every create. We read the created object's PK from the metadata PK
126
+ * field(s) instead.
127
+ * 2. Nested create paths carry PARENT template vars (`/groups/{id}/members`, `/badges/{id}/requests`) —
128
+ * filled here from the record's own Attributes.
129
+ * The flat request body (Vanilla wraps nothing) and the loud-on-empty-id BuildCreatedResult are preserved.
130
+ * Top-level creates (no parent vars) behave exactly as the generic path would.
131
+ */
132
+ CreateRecord(ctx: CreateRecordContext): Promise<CRUDResult>;
133
+ /**
134
+ * OVERRIDDEN so the generic Update/Delete/Get path can template Vanilla's NAMED and NESTED path vars —
135
+ * `/discussions/{id}`, `/groups/{id}/members/{userID}`, `/badges/{id}/requests/{userID}` — which the base
136
+ * (which only replaces `{id}`/`{ExternalID}`) cannot. A single-var path takes the whole ExternalID; a
137
+ * multi-var (nested) path splits the composite `parent|child` ExternalID in path order.
138
+ */
139
+ protected SubstituteIDInPath(path: string, externalID: string, idLocation: string | null): string;
140
+ /**
141
+ * Tests the connection by hitting the current-user endpoint (`/api/v2/users/me`). A 2xx confirms the token
142
+ * + community URL are valid; 401/403 → auth failure; anything else → error.
143
+ */
144
+ TestConnection(companyIntegration: MJCompanyIntegrationEntity, contextUser: UserInfo): Promise<ConnectionTestResult>;
145
+ /** Reads the Vanilla credential (token + community URL) from the linked Credential entity or Configuration. */
146
+ private loadCredentials;
147
+ /** Loads a credential row and parses its Values JSON. */
148
+ private loadFromCredentialEntity;
149
+ /** Extracts Vanilla credential fields from a credential/config JSON string (tolerant of absent/invalid). */
150
+ private parseCredentialJson;
151
+ /**
152
+ * Composes the per-tenant base URL `{communityUrl}/api/v2`. Honors an explicit absolute-URL override
153
+ * (endpoint/baseUrl already carrying `/api/v2`, or a sandbox/mock origin) verbatim; otherwise appends the
154
+ * constant version path. ZERO community URL is baked into this connector — it always comes from data.
155
+ */
156
+ private resolveCommunityBaseURL;
157
+ /**
158
+ * Extracts the created record's ExternalID from the response using the object's METADATA PK field(s)
159
+ * (Vanilla's PK is `<object>ID`, not a plain `id`). Falls back to the base ExtractIDFromResponse for
160
+ * header/body-`id` locations. Returns undefined when no PK value is present → BuildCreatedResult fails loudly.
161
+ */
162
+ private extractCreatedID;
163
+ /** Substitutes CreateAPIPath parent template vars from the record's own attributes (nested creates). */
164
+ private substitutePathVarsFromAttributes;
165
+ /** Parses the RFC-5988 `Link` header (or `x-app-page-next-url`) for the `rel="next"` URL. */
166
+ private parseNextLink;
167
+ /**
168
+ * Max watermark across the batch's records for the given field, vs the prior watermark. ISO-8601
169
+ * timestamps compared by Date.parse (tolerant of differing offsets); the prior value is retained when no
170
+ * record carries a later timestamp (never regresses).
171
+ */
172
+ private maxWatermark;
173
+ /** Joins a base URL and a path (mirrors the base's private BuildFullURL). */
174
+ private joinURL;
175
+ /** Gets an IO from the cache without throwing (used by StableOrderingKey, which may be called early). */
176
+ private tryGetCachedObject;
177
+ /** Returns the first present, non-empty string value among the given keys. */
178
+ private firstString;
179
+ }
@@ -0,0 +1,560 @@
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 } from '@memberjunction/core';
9
+ import { IntegrationEngineBase } from '@memberjunction/integration-engine-base';
10
+ import { BaseIntegrationConnector, BaseRESTIntegrationConnector, } from '@memberjunction/integration-engine';
11
+ import { mergeDeclaredWithSampledFields } from '@memberjunction/connector-schema-merge';
12
+ // ─── Design note ────────────────────────────────────────────────────────
13
+ //
14
+ // The connector is PURE MECHANISM. The object/field catalog is NOT baked here — it lives in the
15
+ // Declared metadata (metadata/integrations/higherlogic-vanilla/.higherlogic-vanilla.integration.json),
16
+ // seeded from Vanilla's credential-free OpenAPI/KB corpus (case-1 discovery). Discovery is INHERITED
17
+ // from BaseRESTIntegrationConnector: DiscoverObjects / DiscoverFields / IntrospectSchema read the
18
+ // persisted Declared metadata from the IntegrationEngineBase cache. There is NO hardcoded object list,
19
+ // NO field catalog, NO baked PK/FK/required/readonly/type constants in this file.
20
+ //
21
+ // What this connector implements (the Higher Logic Vanilla API v2 protocol shape over REST/JSON):
22
+ // - Auth: a Personal Access Token as a Bearer token — `Authorization: Bearer <token>` (KB article 41).
23
+ // A static, long-lived, manually-issued token — no OAuth handshake, no signing, no crypto. The SSO
24
+ // flavours the same platform also offers (OAuth2 / SAML / OIDC / jsConnect / JWT-addon) are END-USER
25
+ // web auth, NOT this server-to-server connector's path, and are deliberately NOT implemented.
26
+ // - Tenancy: Vanilla is per-tenant hosted (no single global host). Every request targets
27
+ // `{communityUrl}/api/v2/…`; the per-tenant {communityUrl} is read from the connection Configuration
28
+ // (PerTenantConfig.communityUrlConfigKey='communityUrl') / credential — ZERO community URL baked here.
29
+ // - Pagination: numbered pages (`page` + `limit`) whose authoritative "more data" signal is the RFC-5988
30
+ // `Link` response header (`rel="next"`) — with the `x-app-page-next-url` header and a body-length
31
+ // heuristic as fallbacks. NOT a body-embedded cursor.
32
+ // - Incremental: a `dateUpdated` (or insert-only `dateInserted`) list-endpoint filter — the per-object
33
+ // IncrementalWatermarkField from metadata. Sent as `<field>=>=<watermark>`; the new max is persisted
34
+ // only on a fully-drained batch (partial-failure-safe).
35
+ // - Write: generic per-operation CRUD (metadata-driven, flat bodies) for the write-capable IOs. Two
36
+ // documented idiosyncrasies are overridden: (1) Vanilla's PK is `<object>ID` (discussionID / userID),
37
+ // not a plain `id`, so create-ID extraction reads the metadata PK; (2) nested write paths carry named
38
+ // parent vars (`/groups/{id}/members/{userID}`) the base's single-`{id}` templating can't fill.
39
+ // ─── Constants ────────────────────────────────────────────────────────
40
+ /** Vanilla's API version path segment — constant across every per-tenant community host. */
41
+ const VANILLA_API_VERSION_PATH = '/api/v2';
42
+ /** Fallback page size when the IO declares no DefaultPageSize. */
43
+ const VANILLA_DEFAULT_LIMIT = 30;
44
+ /**
45
+ * Conservative universal cap on the requested `limit`. Endpoint maxima vary (100 for
46
+ * discussions/comments/categories, 500 for users — Configuration.PaginationDefaults.maxLimitSamples), but
47
+ * 100 is safe on every documented endpoint; a larger batch simply fetches more pages. This is a pagination
48
+ * bound, NOT a catalog — no per-object limit is hardcoded (the real sizes live in metadata / are discovered).
49
+ */
50
+ const VANILLA_MAX_LIMIT = 100;
51
+ // ─── HigherLogicVanillaConnector ─────────────────────────────────────────
52
+ /**
53
+ * Higher Logic Vanilla connector — extends BaseRESTIntegrationConnector (REST/JSON over HTTP).
54
+ *
55
+ * Discovery, the paginated GET loop, template-var read traversal (second-layer objects resolve their
56
+ * parent via metadata), and generic per-operation CRUD are inherited. This class supplies only the
57
+ * Vanilla-specific protocol surface: Bearer auth, the per-tenant base URL, numbered + Link-header
58
+ * pagination, the `dateUpdated`/`dateInserted` incremental filter, connection testing, PK-aware create
59
+ * ID extraction, named/composite path-var substitution, and the §7/§10 sync-efficiency hooks the frozen
60
+ * contract evidences.
61
+ */
62
+ let HigherLogicVanillaConnector = class HigherLogicVanillaConnector extends BaseRESTIntegrationConnector {
63
+ constructor() {
64
+ super(...arguments);
65
+ /** Cached auth for the lifetime of a single sync run (Personal Access Tokens are long-lived, no refresh). */
66
+ this.cachedAuth = null;
67
+ /**
68
+ * Response headers from the MOST RECENT MakeHTTPRequest, stashed so the (ctx-less) ExtractPaginationInfo
69
+ * can read the RFC-5988 `Link` header. Safe: the base pagination loop calls MakeHTTPRequest and
70
+ * ExtractPaginationInfo back-to-back with NO intervening await, so on a single-threaded event loop the
71
+ * stashed headers always correspond to the page just parsed.
72
+ */
73
+ this.lastResponseHeaders = {};
74
+ /**
75
+ * The active incremental filter fragment (e.g. `dateUpdated=%3E%3D<iso>`) for the object currently being
76
+ * fetched, stashed by the FetchChanges override so the (ctx-less) BuildPaginatedURL can append it. Set at
77
+ * the top of an incremental FetchChanges, cleared in its finally. Safe because the engine drives one
78
+ * FetchChanges per object at a time (single-threaded async; no concurrent BuildPaginatedURL for a
79
+ * different watermark).
80
+ */
81
+ this.activeWatermarkFilter = null;
82
+ }
83
+ // ── Identity (T1 three-way invariant) ────────────────────────────
84
+ /** Verbatim `MJ: Integrations.Name`. Load-bearing: the T1 three-way name check compares this === metadata Name. */
85
+ get IntegrationName() {
86
+ return 'higherlogic-vanilla';
87
+ }
88
+ /**
89
+ * Sample-union enrichment (MJ connector standard): the Declared metadata is docs/spec-derived and misses
90
+ * a tenant's custom profile fields (Vanilla ProfileField extensions surface as extra keys on user/record
91
+ * reads) and addon-specific attributes. After the base cache-driven introspection, we sample each
92
+ * object's live read shape via `DiscoverFieldsViaFetch` and UNION it into the declared field set with
93
+ * `mergeDeclaredWithSampledFields` — never-shrink, declared-wins, capacities widened. Best-effort +
94
+ * parallel; a sample failure (no credential, addon disabled) leaves that object's declared fields intact.
95
+ * NOTE: we override `IntrospectSchema` (NOT `DiscoverFields` — that would recurse into
96
+ * `DiscoverFieldsViaFetch`'s own fallback). Connector-agnostic: no Vanilla-specific field logic.
97
+ */
98
+ async IntrospectSchema(companyIntegration, contextUser) {
99
+ const info = await super.IntrospectSchema(companyIntegration, contextUser);
100
+ await Promise.all(info.Objects.map(async (obj) => {
101
+ try {
102
+ const sampled = await this.DiscoverFieldsViaFetch(companyIntegration, obj.ExternalName, contextUser);
103
+ obj.Fields = mergeDeclaredWithSampledFields(obj.Fields, sampled);
104
+ }
105
+ catch {
106
+ /* best-effort — a sample failure leaves the declared fields as-is */
107
+ }
108
+ }));
109
+ return info;
110
+ }
111
+ // ── Capability getters (kept in lockstep with the per-op metadata columns) ──
112
+ // Write is a MIXED subset (Configuration.WriteCapability): many objects support create/update/delete,
113
+ // several are read-only, and a few support only a subset (e.g. BadgeRequest = create + delete, no
114
+ // update). The connector-level getters report the UNION (the connector CAN perform each verb); per-object
115
+ // null-column honesty is enforced by the base generic CRUD path, which THROWS for a verb whose
116
+ // Create/Update/Delete path+method columns are null (never silently sends a broken URL).
117
+ get SupportsCreate() { return true; }
118
+ get SupportsUpdate() { return true; }
119
+ get SupportsDelete() { return true; }
120
+ /**
121
+ * Discovery is NON-authoritative: DiscoverObjects / IntrospectSchema are cache-driven (they re-read
122
+ * persisted ACTIVE Declared metadata, NOT a live full-gamut enumeration). Vanilla exposes no
123
+ * describe/introspection endpoint enumerating everything a credential can access, and a tenant's custom
124
+ * ProfileFields are knowable only from data — they flow through the sample-union above + the framework's
125
+ * runtime custom-column capture. So absence in a refresh proves nothing → never deactivate. Matches
126
+ * Configuration.DiscoveryIsAuthoritative=false in the frozen contract.
127
+ */
128
+ get DiscoveryIsAuthoritative() {
129
+ return false;
130
+ }
131
+ // ── Sync-efficiency hooks (§7/§10 — populated from frozen-contract Configuration facts) ──
132
+ /**
133
+ * From Configuration.RateLimitPolicy (KB article 44): GET is 300 req/min per IP (=5/s), writes 120/min
134
+ * (=2/s). A separate HARD limit — 250 requests / 10s — triggers a MANUAL-intervention IP block (not an
135
+ * auto-lifting 429), so we stay well under it: 5/s sustained (the GET ceiling) with a small burst. The
136
+ * engine's AIMD bucket backs off further on any 429.
137
+ */
138
+ get RateLimitPolicy() {
139
+ return { TokensPerSec: 5, Burst: 10 };
140
+ }
141
+ // ExtractRetryAfterMs: inherited default (undefined). Configuration.RateLimitPolicy.retryAfterHeaderDocumented
142
+ // is FALSE — Vanilla documents no Retry-After / X-RateLimit-* header on its 429 (confirmed by a direct scan
143
+ // of the merged OpenAPI spec's response headers). Nothing to parse reliably; the AIMD bucket backs off on
144
+ // the 429 regardless. Left as a soft gap for live-probe confirmation rather than guessing a header name.
145
+ /** Conservative in-flight cap. The per-minute + hard-block ceilings are the real limiters. */
146
+ get MaxConcurrencyHint() { return 3; }
147
+ /**
148
+ * No-watermark objects resume by their StableOrderingKey — read from the IO metadata when the extractor
149
+ * emitted one, else the object's declared PK (Vanilla's `<object>ID`). Returns null when the object
150
+ * declares no stable key or the cache is unavailable (unit-test context).
151
+ */
152
+ StableOrderingKey(objectName) {
153
+ const obj = this.tryGetCachedObject(objectName);
154
+ if (!obj)
155
+ return null;
156
+ const declared = obj.StableOrderingKey;
157
+ if (declared && declared.trim().length > 0)
158
+ return declared.trim();
159
+ const pk = this.GetCachedFields(obj.ID).find(f => f.IsPrimaryKey);
160
+ return pk?.Name ?? null;
161
+ }
162
+ // ── Abstract REST hooks ──────────────────────────────────────────
163
+ /**
164
+ * Resolves the Bearer token + the per-tenant community base URL from the linked Credential entity
165
+ * (preferred) or the CompanyIntegration Configuration JSON (fallback). Cached for the run.
166
+ */
167
+ async Authenticate(companyIntegration, contextUser) {
168
+ if (this.cachedAuth)
169
+ return this.cachedAuth;
170
+ const creds = await this.loadCredentials(companyIntegration, contextUser);
171
+ if (!creds.Token) {
172
+ throw new Error('[higherlogic-vanilla] No Personal Access Token found. Attach a credential carrying the token ' +
173
+ '(token / apiKey / personalAccessToken), or set it in the connection Configuration JSON.');
174
+ }
175
+ const baseURL = this.resolveCommunityBaseURL(companyIntegration, creds);
176
+ this.cachedAuth = { Token: creds.Token, CommunityBaseURL: baseURL };
177
+ return this.cachedAuth;
178
+ }
179
+ /** Vanilla auth: the Personal Access Token as a Bearer header. A static string — no signing, no crypto. */
180
+ BuildHeaders(auth) {
181
+ return {
182
+ 'Authorization': `Bearer ${auth.Token}`,
183
+ 'Content-Type': 'application/json',
184
+ 'Accept': 'application/json',
185
+ };
186
+ }
187
+ /** HTTP transport (fetch). Owns the wire boundary; test subclasses override this to capture requests. */
188
+ async MakeHTTPRequest(_auth, url, method, headers, body) {
189
+ const response = await fetch(url, {
190
+ method,
191
+ headers,
192
+ body: body !== undefined ? JSON.stringify(body) : undefined,
193
+ });
194
+ const respHeaders = {};
195
+ response.headers.forEach((v, k) => { respHeaders[k.toLowerCase()] = v; });
196
+ this.lastResponseHeaders = respHeaders;
197
+ const text = await response.text();
198
+ let parsed = null;
199
+ if (text.length > 0) {
200
+ try {
201
+ parsed = JSON.parse(text);
202
+ }
203
+ catch {
204
+ parsed = text;
205
+ }
206
+ }
207
+ return { Status: response.status, Body: parsed, Headers: respHeaders };
208
+ }
209
+ /**
210
+ * Vanilla list endpoints return a BARE JSON ARRAY of records at the response root (ResponseDataKey=null
211
+ * for almost every object). A few wrap under a key (e.g. ProductMessage → ResponseDataKey='data'). The
212
+ * get-one shape is a single bare object. Handles all three.
213
+ */
214
+ NormalizeResponse(rawBody, responseDataKey) {
215
+ if (rawBody == null)
216
+ return [];
217
+ if (Array.isArray(rawBody))
218
+ return rawBody;
219
+ if (typeof rawBody !== 'object')
220
+ return [];
221
+ const body = rawBody;
222
+ if (responseDataKey) {
223
+ const arr = body[responseDataKey];
224
+ if (Array.isArray(arr))
225
+ return arr;
226
+ }
227
+ // get-one / non-list shape: the body IS the record.
228
+ return [body];
229
+ }
230
+ /**
231
+ * Vanilla pagination (Configuration.PaginationDefaults): numbered pages whose authoritative "more data"
232
+ * signal is the RFC-5988 `Link` response header (`<url>; rel="next"`), NOT a body field. We read the Link
233
+ * header from the stashed headers (with the `x-app-page-next-url` header as a secondary signal, and a
234
+ * body-length heuristic as the last-resort fallback). For PageNumber the next page is page+1 (Vanilla's
235
+ * next URL is deterministic); for the one Cursor-typed object we surface the next-page URL verbatim as the
236
+ * cursor so BuildPaginatedURL follows it directly.
237
+ */
238
+ ExtractPaginationInfo(rawBody, paginationType, currentPage, _currentOffset, pageSize) {
239
+ const nextURL = this.parseNextLink(this.lastResponseHeaders);
240
+ if (paginationType === 'Cursor') {
241
+ return nextURL ? { HasMore: true, NextCursor: nextURL } : { HasMore: false };
242
+ }
243
+ // PageNumber (the overwhelmingly common case).
244
+ if (nextURL) {
245
+ return { HasMore: true, NextPage: currentPage + 1 };
246
+ }
247
+ // Fallback when no Link/next header is present: a full page suggests more data. The KB warns a page
248
+ // CAN be short mid-stream (permission stripping), so the header is preferred; this only fires when the
249
+ // header is absent, and it stops on the first short/empty page (safe: worst case re-reads one page).
250
+ if (Array.isArray(rawBody) && pageSize > 0 && rawBody.length >= pageSize) {
251
+ return { HasMore: true, NextPage: currentPage + 1 };
252
+ }
253
+ return { HasMore: false };
254
+ }
255
+ /**
256
+ * Per-tenant base host + version path: `{communityUrl}/api/v2`. Composed in Authenticate (honoring a
257
+ * Configuration BaseURL override for sandbox/mock redirection) and stashed on the auth context. The
258
+ * object APIPaths (`/discussions`, …) are appended by the base BuildFullURL.
259
+ */
260
+ GetBaseURL(_companyIntegration, auth) {
261
+ return auth.CommunityBaseURL;
262
+ }
263
+ /**
264
+ * Vanilla pagination params: `page=<n>&limit=<size>` (NOT the base default `page`/`pageSize`). When the
265
+ * incremental watermark filter is active this run, it is appended (`dateUpdated=>=<iso>`). For the
266
+ * Cursor-typed object the base hands back a FULL next-page URL as the cursor — return it verbatim.
267
+ */
268
+ BuildPaginatedURL(basePath, obj, page, _offset, cursor, effectivePageSize) {
269
+ // Cursor style (Link-header full-URL follow): the cursor IS the next page's absolute URL.
270
+ if (cursor && /^https?:\/\//i.test(cursor))
271
+ return cursor;
272
+ const requested = effectivePageSize ?? obj.DefaultPageSize ?? VANILLA_DEFAULT_LIMIT;
273
+ const limit = Math.max(1, Math.min(requested, VANILLA_MAX_LIMIT));
274
+ const parts = [`page=${page}`, `limit=${limit}`];
275
+ if (this.activeWatermarkFilter)
276
+ parts.push(this.activeWatermarkFilter);
277
+ const separator = basePath.includes('?') ? '&' : '?';
278
+ return `${basePath}${separator}${parts.join('&')}`;
279
+ }
280
+ // ── FetchChanges override (incremental dateUpdated/dateInserted filter + new-watermark emission) ──
281
+ /**
282
+ * OVERRIDDEN to (1) inject the `<IncrementalWatermarkField>=>=<watermark>` list filter for objects that
283
+ * declare SupportsIncrementalSync + carry a watermark this run, and (2) EMIT the new watermark. The base
284
+ * flat/template fetch threads no watermark into the URL and returns no NewWatermarkValue, so the connector
285
+ * owns both. All fetching (numbered pagination, template-var parent traversal) is delegated to the base;
286
+ * this override only sets activeWatermarkFilter around the super call and computes the max on a drained batch.
287
+ *
288
+ * Partial-failure safety: NewWatermarkValue is emitted ONLY when the whole object is drained
289
+ * (HasMore=false). A mid-stream batch advances no watermark, so a failure between batches resumes from the
290
+ * unchanged prior watermark. The `>=` operator (not `>`) means the boundary record re-syncs each run —
291
+ * idempotent (content-hash dedup) and never loses a record that shares the boundary timestamp.
292
+ */
293
+ async FetchChanges(ctx) {
294
+ const obj = this.GetCachedObject(ctx.CompanyIntegration.IntegrationID, ctx.ObjectName);
295
+ const wmField = obj.IncrementalWatermarkField;
296
+ const incremental = obj.SupportsIncrementalSync
297
+ && !!wmField
298
+ && ctx.WatermarkValue != null
299
+ && ctx.WatermarkValue.length > 0;
300
+ if (!incremental) {
301
+ return super.FetchChanges(ctx);
302
+ }
303
+ this.activeWatermarkFilter = `${encodeURIComponent(wmField)}=${encodeURIComponent(`>=${ctx.WatermarkValue}`)}`;
304
+ try {
305
+ const result = await super.FetchChanges(ctx);
306
+ if (!result.HasMore) {
307
+ result.NewWatermarkValue = this.maxWatermark(result, wmField, ctx.WatermarkValue);
308
+ }
309
+ return result;
310
+ }
311
+ finally {
312
+ this.activeWatermarkFilter = null;
313
+ }
314
+ }
315
+ // ── CRUD ──────────────────────────────────────────────────────────
316
+ //
317
+ // Top-level single-`{id}` objects (Discussion, Comment, User, Category, Article, …) use the INHERITED
318
+ // generic UpdateRecord / DeleteRecord / GetRecord unchanged — they read the per-operation IO columns and
319
+ // route the single `{id}` through the SubstituteIDInPath override below. CreateRecord is overridden for two
320
+ // Vanilla idiosyncrasies (PK-aware id extraction + nested-create parent vars); SubstituteIDInPath is
321
+ // overridden so the nested objects' named/composite path vars (`/groups/{id}/members/{userID}`) resolve.
322
+ /**
323
+ * OVERRIDDEN for two Vanilla create idiosyncrasies the generic path can't express:
324
+ * 1. Vanilla's created-record ID field is `<object>ID` (discussionID / commentID / userID), NOT a plain
325
+ * `id`, so the base's ExtractIDFromResponse (which scans for id/ID/externalID) would return undefined
326
+ * and BuildCreatedResult would FAIL every create. We read the created object's PK from the metadata PK
327
+ * field(s) instead.
328
+ * 2. Nested create paths carry PARENT template vars (`/groups/{id}/members`, `/badges/{id}/requests`) —
329
+ * filled here from the record's own Attributes.
330
+ * The flat request body (Vanilla wraps nothing) and the loud-on-empty-id BuildCreatedResult are preserved.
331
+ * Top-level creates (no parent vars) behave exactly as the generic path would.
332
+ */
333
+ async CreateRecord(ctx) {
334
+ const ci = ctx.CompanyIntegration;
335
+ const contextUser = ctx.ContextUser;
336
+ const obj = this.GetCachedObject(ci.IntegrationID, ctx.ObjectName);
337
+ if (!obj.CreateAPIPath || !obj.CreateMethod) {
338
+ // Delegate so the "not configured" error is raised consistently by the base.
339
+ return super.CreateRecord(ctx);
340
+ }
341
+ const fields = this.GetCachedFields(obj.ID);
342
+ const auth = await this.Authenticate(ci, contextUser);
343
+ const baseURL = this.GetBaseURL(ci, auth);
344
+ const headers = this.BuildHeaders(auth);
345
+ const path = this.substitutePathVarsFromAttributes(obj.CreateAPIPath, ctx.Attributes);
346
+ const url = this.joinURL(baseURL, path);
347
+ const body = this.BuildOperationBody(ctx.Attributes, obj.CreateBodyShape, obj.CreateBodyKey);
348
+ const response = await this.MakeHTTPRequest(auth, url, obj.CreateMethod, headers, body);
349
+ if (response.Status >= 200 && response.Status < 300) {
350
+ const externalID = this.extractCreatedID(obj, fields, response);
351
+ return this.BuildCreatedResult(externalID, response.Status, ctx.ObjectName);
352
+ }
353
+ return {
354
+ Success: false,
355
+ StatusCode: response.Status,
356
+ ErrorMessage: this.ExtractErrorMessage(response) ?? `HTTP ${response.Status} on create`,
357
+ };
358
+ }
359
+ /**
360
+ * OVERRIDDEN so the generic Update/Delete/Get path can template Vanilla's NAMED and NESTED path vars —
361
+ * `/discussions/{id}`, `/groups/{id}/members/{userID}`, `/badges/{id}/requests/{userID}` — which the base
362
+ * (which only replaces `{id}`/`{ExternalID}`) cannot. A single-var path takes the whole ExternalID; a
363
+ * multi-var (nested) path splits the composite `parent|child` ExternalID in path order.
364
+ */
365
+ SubstituteIDInPath(path, externalID, idLocation) {
366
+ if (idLocation && idLocation !== 'path')
367
+ return path;
368
+ const vars = path.match(/\{\w+\}/g);
369
+ if (!vars || vars.length === 0)
370
+ return path;
371
+ if (vars.length === 1)
372
+ return path.replace(vars[0], encodeURIComponent(externalID));
373
+ const parts = externalID.split('|');
374
+ let out = path;
375
+ vars.forEach((v, i) => {
376
+ const val = parts[i] ?? parts[parts.length - 1] ?? externalID;
377
+ out = out.replace(v, encodeURIComponent(val));
378
+ });
379
+ return out;
380
+ }
381
+ // ── Connection test ──────────────────────────────────────────────
382
+ /**
383
+ * Tests the connection by hitting the current-user endpoint (`/api/v2/users/me`). A 2xx confirms the token
384
+ * + community URL are valid; 401/403 → auth failure; anything else → error.
385
+ */
386
+ async TestConnection(companyIntegration, contextUser) {
387
+ try {
388
+ const auth = await this.Authenticate(companyIntegration, contextUser);
389
+ const baseURL = this.GetBaseURL(companyIntegration, auth);
390
+ const headers = this.BuildHeaders(auth);
391
+ const url = this.joinURL(baseURL, '/users/me');
392
+ const response = await this.MakeHTTPRequest(auth, url, 'GET', headers);
393
+ if (response.Status >= 200 && response.Status < 300) {
394
+ return { Success: true, Message: 'Higher Logic Vanilla connection successful.' };
395
+ }
396
+ if (response.Status === 401 || response.Status === 403) {
397
+ return { Success: false, Message: `Higher Logic Vanilla authentication failed (HTTP ${response.Status}). Check the Personal Access Token and community URL.` };
398
+ }
399
+ return { Success: false, Message: `Higher Logic Vanilla connection test returned HTTP ${response.Status}.` };
400
+ }
401
+ catch (err) {
402
+ const msg = err instanceof Error ? err.message : String(err);
403
+ return { Success: false, Message: `Higher Logic Vanilla connection test error: ${msg}` };
404
+ }
405
+ }
406
+ // ── Credential + tenant loading ──────────────────────────────────
407
+ /** Reads the Vanilla credential (token + community URL) from the linked Credential entity or Configuration. */
408
+ async loadCredentials(companyIntegration, contextUser) {
409
+ const credentialID = companyIntegration.CredentialID;
410
+ let creds = null;
411
+ if (credentialID) {
412
+ creds = await this.loadFromCredentialEntity(credentialID, contextUser);
413
+ }
414
+ const configCreds = companyIntegration.Configuration ? this.parseCredentialJson(companyIntegration.Configuration) : null;
415
+ // Credential store wins for the secret token; Configuration usually carries the (non-secret) community URL.
416
+ const merged = {
417
+ Token: creds?.Token ?? configCreds?.Token,
418
+ CommunityUrl: creds?.CommunityUrl ?? configCreds?.CommunityUrl,
419
+ };
420
+ if (!creds && !configCreds) {
421
+ throw new Error('[higherlogic-vanilla] No credential found. Attach a credential carrying the Personal Access Token ' +
422
+ '(+ the community URL), or set Configuration JSON.');
423
+ }
424
+ return merged;
425
+ }
426
+ /** Loads a credential row and parses its Values JSON. */
427
+ async loadFromCredentialEntity(credentialID, contextUser, provider) {
428
+ const md = provider ?? new Metadata();
429
+ const credential = await md.GetEntityObject('MJ: Credentials', contextUser);
430
+ const loaded = await credential.Load(credentialID);
431
+ if (!loaded || !credential.Values)
432
+ return null;
433
+ return this.parseCredentialJson(credential.Values);
434
+ }
435
+ /** Extracts Vanilla credential fields from a credential/config JSON string (tolerant of absent/invalid). */
436
+ parseCredentialJson(json) {
437
+ try {
438
+ const parsed = JSON.parse(json);
439
+ return {
440
+ Token: this.firstString(parsed, ['token', 'Token', 'apiKey', 'ApiKey', 'personalAccessToken', 'PersonalAccessToken', 'accessToken', 'AccessToken']),
441
+ CommunityUrl: this.firstString(parsed, ['communityUrl', 'communityURL', 'CommunityUrl', 'CommunityURL', 'endpoint', 'Endpoint', 'baseUrl', 'baseURL', 'BaseUrl', 'BaseURL']),
442
+ };
443
+ }
444
+ catch {
445
+ return null;
446
+ }
447
+ }
448
+ /**
449
+ * Composes the per-tenant base URL `{communityUrl}/api/v2`. Honors an explicit absolute-URL override
450
+ * (endpoint/baseUrl already carrying `/api/v2`, or a sandbox/mock origin) verbatim; otherwise appends the
451
+ * constant version path. ZERO community URL is baked into this connector — it always comes from data.
452
+ */
453
+ resolveCommunityBaseURL(_companyIntegration, creds) {
454
+ const raw = (creds.CommunityUrl ?? '').trim();
455
+ if (!raw) {
456
+ throw new Error('[higherlogic-vanilla] No community URL configured. Set the per-connection "communityUrl" ' +
457
+ '(the `https://<community-host>` tenant identifier) — it is never baked into the connector.');
458
+ }
459
+ const trimmed = raw.replace(/\/+$/, '');
460
+ // If the supplied value already carries the /api/v2 version path, use it as-is (override / already-composed).
461
+ if (/\/api\/v2$/i.test(trimmed))
462
+ return trimmed;
463
+ return `${trimmed}${VANILLA_API_VERSION_PATH}`;
464
+ }
465
+ // ── Helpers ──────────────────────────────────────────────────────
466
+ /**
467
+ * Extracts the created record's ExternalID from the response using the object's METADATA PK field(s)
468
+ * (Vanilla's PK is `<object>ID`, not a plain `id`). Falls back to the base ExtractIDFromResponse for
469
+ * header/body-`id` locations. Returns undefined when no PK value is present → BuildCreatedResult fails loudly.
470
+ */
471
+ extractCreatedID(obj, fields, response) {
472
+ const records = this.NormalizeResponse(response.Body, obj.ResponseDataKey);
473
+ const created = records[0];
474
+ if (created) {
475
+ const pkNames = fields.filter(f => f.IsPrimaryKey).sort((a, b) => a.Sequence - b.Sequence).map(f => f.Name);
476
+ if (pkNames.length > 0 && pkNames.every(n => created[n] != null && String(created[n]).length > 0)) {
477
+ return pkNames.map(n => String(created[n])).join('|');
478
+ }
479
+ }
480
+ return this.ExtractIDFromResponse(response, obj.CreateIDLocation);
481
+ }
482
+ /** Substitutes CreateAPIPath parent template vars from the record's own attributes (nested creates). */
483
+ substitutePathVarsFromAttributes(path, attributes) {
484
+ return path.replace(/\{(\w+)\}/g, (match, name) => {
485
+ const v = attributes[name];
486
+ return v != null && String(v).length > 0 ? encodeURIComponent(String(v)) : match;
487
+ });
488
+ }
489
+ /** Parses the RFC-5988 `Link` header (or `x-app-page-next-url`) for the `rel="next"` URL. */
490
+ parseNextLink(headers) {
491
+ const direct = headers['x-app-page-next-url'];
492
+ if (typeof direct === 'string' && direct.trim().length > 0)
493
+ return direct.trim();
494
+ const link = headers['link'];
495
+ if (typeof link !== 'string' || link.length === 0)
496
+ return null;
497
+ // `<url1>; rel="first", <url2>; rel="next", <url3>; rel="last"`
498
+ for (const part of link.split(',')) {
499
+ const m = part.match(/<([^>]+)>\s*;\s*rel\s*=\s*"?next"?/i);
500
+ if (m)
501
+ return m[1].trim();
502
+ }
503
+ return null;
504
+ }
505
+ /**
506
+ * Max watermark across the batch's records for the given field, vs the prior watermark. ISO-8601
507
+ * timestamps compared by Date.parse (tolerant of differing offsets); the prior value is retained when no
508
+ * record carries a later timestamp (never regresses).
509
+ */
510
+ maxWatermark(result, wmField, prior) {
511
+ let bestStr = prior;
512
+ let bestMs = Date.parse(prior);
513
+ if (isNaN(bestMs))
514
+ bestMs = -Infinity;
515
+ for (const rec of result.Records) {
516
+ const raw = rec.Fields?.[wmField];
517
+ if (raw == null)
518
+ continue;
519
+ const s = String(raw);
520
+ const ms = Date.parse(s);
521
+ if (!isNaN(ms) && ms > bestMs) {
522
+ bestMs = ms;
523
+ bestStr = s;
524
+ }
525
+ }
526
+ return bestStr;
527
+ }
528
+ /** Joins a base URL and a path (mirrors the base's private BuildFullURL). */
529
+ joinURL(baseURL, apiPath) {
530
+ const base = baseURL.endsWith('/') ? baseURL.slice(0, -1) : baseURL;
531
+ const path = apiPath.startsWith('/') ? apiPath : `/${apiPath}`;
532
+ return `${base}${path}`;
533
+ }
534
+ /** Gets an IO from the cache without throwing (used by StableOrderingKey, which may be called early). */
535
+ tryGetCachedObject(objectName) {
536
+ try {
537
+ const integ = IntegrationEngineBase.Instance.GetIntegrationByName(this.IntegrationName);
538
+ if (!integ)
539
+ return null;
540
+ return IntegrationEngineBase.Instance.GetIntegrationObject(integ.ID, objectName) ?? null;
541
+ }
542
+ catch {
543
+ return null;
544
+ }
545
+ }
546
+ /** Returns the first present, non-empty string value among the given keys. */
547
+ firstString(obj, keys) {
548
+ for (const k of keys) {
549
+ const v = obj[k];
550
+ if (typeof v === 'string' && v.length > 0)
551
+ return v;
552
+ }
553
+ return undefined;
554
+ }
555
+ };
556
+ HigherLogicVanillaConnector = __decorate([
557
+ RegisterClass(BaseIntegrationConnector, 'HigherLogicVanillaConnector')
558
+ ], HigherLogicVanillaConnector);
559
+ export { HigherLogicVanillaConnector };
560
+ //# sourceMappingURL=HigherLogicVanillaConnector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HigherLogicVanillaConnector.js","sourceRoot":"","sources":["../src/HigherLogicVanillaConnector.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export * from './HigherLogicVanillaConnector.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 './HigherLogicVanillaConnector.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,kCAAkC,CAAC;AAEjD;oGACoG;AACpG,MAAM,UAAU,iBAAiB,KAAiD,CAAC"}
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@memberjunction/connector-higher-logic-vanilla",
3
+ "version": "0.2.0",
4
+ "description": "MemberJunction HigherLogicVanilla 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
+ },
23
+ "dependencies": {
24
+ "zod": "~3.24.4",
25
+ "@memberjunction/connector-schema-merge": "^1.0.0"
26
+ },
27
+ "devDependencies": {
28
+ "@memberjunction/core": "^5.43.0",
29
+ "@memberjunction/core-entities": "^5.43.0",
30
+ "@memberjunction/global": "^5.43.0",
31
+ "@memberjunction/integration-engine": "^5.43.0",
32
+ "@types/node": "24.10.11",
33
+ "tsc-alias": "^1.8.16",
34
+ "typescript": "^5.9.3",
35
+ "vitest": "^4.0.18"
36
+ },
37
+ "repository": {
38
+ "type": "git",
39
+ "url": "https://github.com/MemberJunction/Integrations"
40
+ }
41
+ }