@memberjunction/connector-growthzone 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,182 @@
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 ExternalObjectSchema, type ExternalFieldSchema, type FetchContext, type FetchBatchResult, type IntegrationObjectInfo, type ActionGeneratorConfig, type DeleteRecordContext, type CRUDResult } from '@memberjunction/integration-engine';
4
+ /**
5
+ * OAuth2 connection configuration for GrowthZone, parsed from the attached MJ
6
+ * Credential (preferred) or the CompanyIntegration.Configuration JSON. Field names
7
+ * are read case-insensitively. NONE of these values are read at build time — they are
8
+ * resolved from the bound credential at runtime.
9
+ */
10
+ export interface GrowthZoneConnectionConfig {
11
+ /** OAuth2 client identifier. */
12
+ ClientId: string;
13
+ /** OAuth2 client secret. */
14
+ ClientSecret: string;
15
+ /** Long-lived refresh token — drives the PRIMARY `refresh_token` grant. */
16
+ RefreshToken?: string;
17
+ /** Username — drives the FALLBACK `password` grant when no refresh token exists. */
18
+ Username?: string;
19
+ /** Password — drives the FALLBACK `password` grant. */
20
+ Password?: string;
21
+ /** Space/comma-delimited OAuth2 scopes. */
22
+ Scopes?: string;
23
+ /** Operator's GrowthZone API base URL (e.g. https://{subdomain}.growthzoneapp.com/API). */
24
+ BaseURL: string;
25
+ /** Optional token-endpoint override; defaults to `{base}/oauth/token`. */
26
+ TokenURL?: string;
27
+ /** Optional tenant identifier (informational / future use). */
28
+ Tenant?: string;
29
+ /** Maximum retries for rate-limited / transient failures. Default 3. */
30
+ MaxRetries?: number;
31
+ /** HTTP request timeout in ms. Default 30000. */
32
+ RequestTimeoutMs?: number;
33
+ /** Minimum ms between requests (GrowthZone recommends 2s). Default 2000. */
34
+ MinRequestIntervalMs?: number;
35
+ }
36
+ export declare class GrowthZoneConnector extends BaseRESTIntegrationConnector {
37
+ /** Cached auth context for the current sync run. */
38
+ private authCache;
39
+ /** Shared OAuth2 token manager — owns the token round-trip + cache. */
40
+ private readonly tokenManager;
41
+ /** Timestamp of the last API request, for client-side throttling. */
42
+ private lastRequestTime;
43
+ /** Current watermark value, emitted as the IO's IncrementalWatermarkField on the request. */
44
+ private currentWatermark;
45
+ get IntegrationName(): string;
46
+ get SupportsCreate(): boolean;
47
+ get SupportsUpdate(): boolean;
48
+ get SupportsDelete(): boolean;
49
+ /** True when any cached IntegrationObject satisfies the predicate. []→false when the engine
50
+ * cache is unavailable (e.g. capability probed before configuration) — fail-safe read-only. */
51
+ private anyObjectDeclares;
52
+ DeleteRecord(ctx: DeleteRecordContext): Promise<CRUDResult>;
53
+ /**
54
+ * Resolves a record's current GrowthZone audit token (optimistic-concurrency) for an audit-token
55
+ * delete. GrowthZone exposes it as `AuditId` / `{Object}AuditId` on the record; we GetRecord and
56
+ * return the first match. Returns null when the record or token cannot be found.
57
+ */
58
+ private ResolveAuditToken;
59
+ /**
60
+ * Surfaces the FULL object universe straight from the IntegrationEngineBase
61
+ * cache (the Declared metadata in `.growthzone.integration.json`). There is NO
62
+ * hardcoded catalog in this connector — the prohibited module-level object/field
63
+ * literal is intentionally absent. When the cache is unavailable (e.g. action
64
+ * generation invoked before the engine is configured) this returns []; the live
65
+ * discovery path (DiscoverObjects/DiscoverFields) is the authoritative surface.
66
+ */
67
+ GetIntegrationObjects(): IntegrationObjectInfo[];
68
+ GetActionGeneratorConfig(): ActionGeneratorConfig | null;
69
+ /** Maps a cached IntegrationObject + its fields to the action-generator info shape. */
70
+ private ObjectEntityToInfo;
71
+ /**
72
+ * OAuth2 bearer authentication. Mints/refreshes the access token via the shared
73
+ * {@link OAuth2TokenManager}: PRIMARY `refresh_token` grant when a refresh token is
74
+ * present, otherwise the documented FALLBACK `password` grant.
75
+ */
76
+ protected Authenticate(companyIntegration: MJCompanyIntegrationEntity, contextUser: UserInfo): Promise<RESTAuthContext>;
77
+ /** Selects the grant and runs the token round-trip through OAuth2TokenManager. */
78
+ private MintToken;
79
+ /** Sends the OAuth2 bearer token on every request. */
80
+ protected BuildHeaders(auth: RESTAuthContext): Record<string, string>;
81
+ protected GetBaseURL(_companyIntegration: MJCompanyIntegrationEntity, auth: RESTAuthContext): string;
82
+ /**
83
+ * Normalizes GrowthZone responses. Handles three real shapes:
84
+ * 1. `{ Results: [...], TotalRecordAvailable: N }` — paginated lists
85
+ * 2. raw array — some endpoints return an array at the root
86
+ * 3. single object — genuine per-parent DETAIL endpoints (Person, Organization,
87
+ * ContactCustomField, ContactNotes, ContactEngagement, ScheduledBillingUpdate,
88
+ * MembershipChange) return ONE real record per parent. These ARE kept.
89
+ * and coerces `0001-01-01` sentinel dates / empty strings to null.
90
+ *
91
+ * THE EMPTY-EVENT-CHILD SENTINEL (PROBLEMS_LOG #22/#23/#27 — idempotency, surgically scoped):
92
+ * The per-event child list endpoints (`/api/events/sponsors?eventId=X`, …/sessions, …/attendees,
93
+ * etc.) do NOT return `{ Results: [] }` when an event has no children — they return the bare
94
+ * EVENT-DETAIL wrapper instead. That wrapper carries volatile per-fetch audit fields
95
+ * (EventAuditId / EventDetailAuditId), so minting a record from it produced ONE empty placeholder
96
+ * per event whose §4 content-hash identity drifted every sync → child tables doubled (254 = 2×127,
97
+ * all rows empty). Fix: detect the event wrapper (it uniquely carries EventAuditId+EventDetailAuditId
98
+ * — a genuine child/detail record never does) and emit []. This is the ONLY single-object case we
99
+ * drop; every legitimate single-record detail endpoint is preserved (the #27 regression guard).
100
+ * Populated children still arrive as a `Results` envelope and map normally.
101
+ *
102
+ * The FULL source record passes through (no field filtering) so the framework's custom-column
103
+ * capture sees everything the source returned.
104
+ */
105
+ protected NormalizeResponse(rawBody: unknown, responseDataKey: string | null): Record<string, unknown>[];
106
+ /**
107
+ * True when a single-object response is the GrowthZone EVENT-DETAIL wrapper — the body the
108
+ * per-event child endpoints return when an event has zero children (see NormalizeResponse).
109
+ * The wrapper is unmistakable: it carries BOTH event-audit stamps. No genuine child/detail
110
+ * record (Person, Organization, custom fields, notes, engagement) carries these keys, so this
111
+ * stays surgically scoped to the empty-event-child case and never drops a real record.
112
+ */
113
+ private isEmptyEventChildWrapper;
114
+ /**
115
+ * Derives OData pagination state. GrowthZone returns no `HasMore` flag, so end-of-stream
116
+ * is inferred from a short page and/or `TotalRecordAvailable` vs offset+count.
117
+ */
118
+ protected ExtractPaginationInfo(rawBody: unknown, _paginationType: PaginationType, _currentPage: number, currentOffset: number, pageSize: number): PaginationState;
119
+ /**
120
+ * Emits GrowthZone OData params (`skip`/`top`) plus, for an incremental IO, the vendor
121
+ * watermark param. The watermark behaviour is fully METADATA-DRIVEN: the param NAME comes
122
+ * from the IO's `IncrementalWatermarkField` (e.g. `modifiedSince` on the Contact object) and
123
+ * is emitted only when `SupportsIncrementalSync=true` AND a watermark value is in context —
124
+ * never keyed off a hardcoded path suffix or object name.
125
+ */
126
+ protected BuildPaginatedURL(basePath: string, obj: MJIntegrationObjectEntity, _page: number, offset: number, _cursor?: string, effectivePageSize?: number): string;
127
+ /** Executes an HTTP request with client-side throttling + retry for 429/503. */
128
+ protected MakeHTTPRequest(auth: RESTAuthContext, url: string, method: string, headers: Record<string, string>, body?: unknown): Promise<RESTResponse>;
129
+ /**
130
+ * Tests connectivity by minting an OAuth2 token and listing one Contact record. A 2xx
131
+ * confirms the OAuth2 credentials + base URL are valid against the live API.
132
+ */
133
+ TestConnection(companyIntegration: MJCompanyIntegrationEntity, contextUser: UserInfo): Promise<ConnectionTestResult>;
134
+ /**
135
+ * Discovers the full object universe from the IntegrationEngineBase cache (the Declared
136
+ * metadata). GrowthZone publishes its catalog credential-free (curated + dev docs), so
137
+ * the baseline is Declared metadata — never hardcoded here, never sampled at build.
138
+ */
139
+ DiscoverObjects(companyIntegration: MJCompanyIntegrationEntity, contextUser: UserInfo): Promise<ExternalObjectSchema[]>;
140
+ /** Discovers fields for an object from the cached Declared metadata. */
141
+ DiscoverFields(companyIntegration: MJCompanyIntegrationEntity, objectName: string, contextUser: UserInfo): Promise<ExternalFieldSchema[]>;
142
+ /**
143
+ * Sets the watermark context the OData URL builder needs, delegates
144
+ * the actual walk to the base (which descends nested Door→Segment template-var paths via
145
+ * FK metadata so nested IOs never silently return 0 rows), then advances the watermark
146
+ * from the returned records on the final batch only (partial-failure-safe).
147
+ */
148
+ FetchChanges(ctx: FetchContext): Promise<FetchBatchResult>;
149
+ /**
150
+ * Parses the OAuth2 connection config, preferring the attached MJ Credential over the
151
+ * raw Configuration JSON. Credential bytes are resolved at runtime — never at build.
152
+ */
153
+ private ParseConfig;
154
+ /** Loads the OAuth2 config from the MJ: Credentials entity Values JSON. */
155
+ private ParseConfigFromCredential;
156
+ /** Validates the parsed config + applies defaults. Field names are case-insensitive. */
157
+ private ValidateConfig;
158
+ /** Resolves the API base URL from the credential's BaseURL (never a hardcoded subdomain). */
159
+ private ResolveBaseUrl;
160
+ /** Resolves the OAuth2 token endpoint: credential TokenURL override, else `{base}/oauth/token`. */
161
+ private ResolveTokenURL;
162
+ /** Normalizes a raw record's top-level values (null-date + empty-string → null). */
163
+ private NormalizeRecord;
164
+ /** Converts GrowthZone sentinel values to null. */
165
+ private NormalizeValue;
166
+ /** Extracts the latest ModifiedDate across a batch for watermark advancement. */
167
+ private ExtractLatestModifiedDate;
168
+ /** Throttle to respect GrowthZone's recommended minimum request interval. */
169
+ private ThrottleIfNeeded;
170
+ /** Parses a Retry-After header (seconds or http-date) into ms, if present. */
171
+ private RetryAfterMs;
172
+ /** Exponential backoff delay for retry attempts (capped at 30s). */
173
+ private BackoffDelay;
174
+ /** Checks whether an error is transient (network/timeout). */
175
+ private IsTransientNetworkError;
176
+ /** Builds the normalized RESTResponse from a fetch Response. */
177
+ private BuildRESTResponse;
178
+ /** Promise-wrapped setTimeout. */
179
+ private Sleep;
180
+ }
181
+ /** Tree-shaking prevention — import and call from the package entry point. */
182
+ export declare function LoadGrowthZoneConnector(): void;
@@ -0,0 +1,702 @@
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
+ /**
8
+ * GrowthZoneConnector — Integration connector for the GrowthZone (MicroNet Online, Inc.)
9
+ * association-management REST API.
10
+ *
11
+ * API docs: https://documentation.growthzoneapp.com/ (Curated + dev SpectaQL pages)
12
+ *
13
+ * ── Auth: OAuth2 Bearer (PRIMARY + REQUIRED) ────────────────────────────────
14
+ * Mints/refreshes a bearer ACCESS TOKEN via the shared {@link OAuth2TokenManager}
15
+ * (no inlined token/crypto logic):
16
+ * - PRIMARY grant = `refresh_token` (client_id + client_secret + refresh_token
17
+ * POSTed to `{base}/oauth/token`, or the credential's TokenURL).
18
+ * - FALLBACK grant = `password` (username + password + scopes) when the credential
19
+ * supplies no refresh token.
20
+ * Every request sends `Authorization: Bearer {accessToken}`.
21
+ *
22
+ * An `ApiKey` header is recorded in the integration Configuration ONLY as a
23
+ * documented, DEPRECATED alternate — it is NOT the primary path and is not wired
24
+ * here (see the deprecated-alternate note in {@link BuildHeaders}). The prior
25
+ * ApiKey-primary connector this class WHOLESALE REPLACES is gone.
26
+ *
27
+ * ── Base URL ────────────────────────────────────────────────────────────────
28
+ * Comes from the credential's `BaseURL` (the operator's GrowthZone API endpoint,
29
+ * e.g. `https://{subdomain}.growthzoneapp.com/API`) — never a hardcoded subdomain.
30
+ *
31
+ * ── Catalog (metadata-driven, NOT hardcoded) ───────────────────────────────
32
+ * Objects/fields come from the Declared metadata seeded in
33
+ * `metadata/integrations/growthzone/.growthzone.integration.json` and loaded into the
34
+ * IntegrationEngineBase cache. The connector NEVER bakes an object/field catalog into
35
+ * code (the old `GROWTHZONE_ACTION_OBJECTS` of 7 streams is removed); discovery and
36
+ * action-object surfacing read the full ~38-IO universe straight from the cache.
37
+ *
38
+ * ── Nested access paths (Door → Segments) ──────────────────────────────────
39
+ * Nested objects (e.g. `Person` under `Contact`, `EventAttendee` under `Event`) are
40
+ * reached by template-variable APIPaths (`/api/contacts/person/{contactId}`); the base
41
+ * class's FetchChanges walks the door→segment path by resolving each `{var}` to its
42
+ * parent IO via the FK metadata and substituting synced parent IDs — so no nested IO
43
+ * ships as a silent 0-row table. The connector supplies only auth, headers, pagination,
44
+ * and normalization.
45
+ *
46
+ * ── Pagination & incremental ───────────────────────────────────────────────
47
+ * OData `skip`/`top` on flat list endpoints. Incremental sync is fully metadata-driven:
48
+ * an IO with `SupportsIncrementalSync=true` emits its `IncrementalWatermarkField` param
49
+ * (e.g. the Contact object at `/api/contacts` uses `modifiedSince`); every other IO is a
50
+ * full pull (content-hash dedup handled by the engine).
51
+ *
52
+ * ── Write ───────────────────────────────────────────────────────────────────
53
+ * The curated AMS surface is read-only (Pull). SupportsWrite=false; no CRUD wired.
54
+ */
55
+ import { RegisterClass } from '@memberjunction/global';
56
+ import { Metadata } from '@memberjunction/core';
57
+ import { BaseIntegrationConnector, BaseRESTIntegrationConnector, OAuth2TokenManager, } from '@memberjunction/integration-engine';
58
+ import { IntegrationEngineBase } from '@memberjunction/integration-engine-base';
59
+ // ─── Constants ───────────────────────────────────────────────────────
60
+ /** The canonical MJ: Integrations.Name — part of the three-way invariant. */
61
+ const INTEGRATION_NAME = 'GrowthZone';
62
+ /** Token endpoint path appended to the base when no TokenURL override is supplied. */
63
+ const DEFAULT_TOKEN_PATH = '/oauth/token';
64
+ /**
65
+ * OData pagination param names. GrowthZone is an OData-style API and honors the STANDARD `$`-prefixed
66
+ * params (`$top`/`$skip`); the un-prefixed `top`/`skip` are SILENTLY IGNORED by the server — it returns
67
+ * the same first page for any `skip` value, which the base pagination loop's duplicate-page detector
68
+ * then (correctly) treats as end-of-stream, capping every object at one server-default page (~100).
69
+ * Verified live: `?skip=3` returns the same first IDs as `?skip=0`, while `?$skip=3` advances.
70
+ */
71
+ const ODATA_SKIP_PARAM = '$skip';
72
+ const ODATA_TOP_PARAM = '$top';
73
+ const DEFAULT_MAX_RETRIES = 3;
74
+ const DEFAULT_REQUEST_TIMEOUT_MS = 30_000;
75
+ const DEFAULT_MIN_REQUEST_INTERVAL_MS = 2_000;
76
+ const DEFAULT_PAGE_SIZE = 500;
77
+ /**
78
+ * GrowthZone hard-caps an OData list response at 100 rows regardless of the requested `top`,
79
+ * and several list endpoints reject a `top` above that cap with `400 The request is invalid`.
80
+ * Clamping the requested page size to the real server cap makes `skip` advance in lockstep with
81
+ * the rows actually returned (no gaps/overlap) and keeps every list request inside the server's
82
+ * accepted range. Not vendor-guesswork: it's GrowthZone's documented/observed page ceiling.
83
+ */
84
+ const GROWTHZONE_MAX_PAGE_SIZE = 100;
85
+ /** GrowthZone sentinel value for a null datetime. */
86
+ const GROWTHZONE_NULL_DATE = '0001-01-01T00:00:00';
87
+ // ─── Connector Implementation ────────────────────────────────────────
88
+ let GrowthZoneConnector = class GrowthZoneConnector extends BaseRESTIntegrationConnector {
89
+ constructor() {
90
+ super(...arguments);
91
+ /** Cached auth context for the current sync run. */
92
+ this.authCache = null;
93
+ /** Shared OAuth2 token manager — owns the token round-trip + cache. */
94
+ this.tokenManager = new OAuth2TokenManager();
95
+ /** Timestamp of the last API request, for client-side throttling. */
96
+ this.lastRequestTime = 0;
97
+ }
98
+ // Returns the EXACT MJ: Integrations.Name string LITERAL (not the INTEGRATION_NAME const) so the
99
+ // T1 ThreeWayName invariant can statically parse the getter's returned value from connector source.
100
+ get IntegrationName() { return 'GrowthZone'; }
101
+ // ── Capability getters: METADATA-DRIVEN (no hardcoded answer) ─────
102
+ //
103
+ // Write capability FOLLOWS the per-operation CRUD columns on the cached IntegrationObjects
104
+ // (Declared metadata), exactly like GetIntegrationObjects surfaces the object universe from
105
+ // metadata rather than a baked catalog. An object is create-capable when it declares both
106
+ // CreateAPIPath + CreateMethod; same for update/delete. With no write metadata authored the
107
+ // surface is read-only (returns false) — but the moment a writable object's per-operation
108
+ // columns are populated, the capability flips on, and the base BaseRESTIntegrationConnector
109
+ // generic CRUD path executes it. (NB: GrowthZone's writes are largely wizard/operation-based,
110
+ // so an object whose write does NOT fit the generic flat/wrapped body must additionally
111
+ // override CreateRecord/UpdateRecord/DeleteRecord — the metadata flag alone is not enough for
112
+ // those. See PROBLEMS_LOG #30/#35 for the per-object wizard-write roadmap.)
113
+ get SupportsCreate() {
114
+ return this.anyObjectDeclares(o => !!o.CreateAPIPath && !!o.CreateMethod);
115
+ }
116
+ get SupportsUpdate() {
117
+ return this.anyObjectDeclares(o => !!o.UpdateAPIPath && !!o.UpdateMethod);
118
+ }
119
+ get SupportsDelete() {
120
+ return this.anyObjectDeclares(o => !!o.DeleteAPIPath && !!o.DeleteMethod);
121
+ }
122
+ /** True when any cached IntegrationObject satisfies the predicate. []→false when the engine
123
+ * cache is unavailable (e.g. capability probed before configuration) — fail-safe read-only. */
124
+ anyObjectDeclares(pred) {
125
+ const integration = IntegrationEngineBase.Instance.GetIntegrationByName(INTEGRATION_NAME);
126
+ if (!integration)
127
+ return false;
128
+ return IntegrationEngineBase.Instance.GetActiveIntegrationObjects(integration.ID).some(pred);
129
+ }
130
+ // ── Write: GrowthZone audit-token DELETE idiom (override) ─────────
131
+ //
132
+ // GrowthZone splits deletes into two shapes:
133
+ // • single-ID `DELETE /api/store/storeitems/{id}`, `/directory/directories/{id}`,
134
+ // `/memberships/benefititem/{id}`, `/signatures/{id}`, `/webhooks/setup/{id}` →
135
+ // ride the BASE generic DeleteRecord ({id} substitution) unchanged.
136
+ // • audit-token `DELETE /api/calendars/{id}/{auditid}`, `/directorylistingtypes/{id}/{auditid}`,
137
+ // `/roles/{id}/{auditid}` → need the record's CURRENT optimistic-concurrency audit
138
+ // token, which DeleteRecordContext (ExternalID only) doesn't carry. We fetch the
139
+ // record, read its audit token, and substitute BOTH path vars.
140
+ // Doc-derived shape; the live byte-shape is unverified per the no-live-writes rule (PROBLEMS_LOG #35).
141
+ async DeleteRecord(ctx) {
142
+ const ci = ctx.CompanyIntegration;
143
+ const obj = this.GetCachedObject(ci.IntegrationID, ctx.ObjectName);
144
+ // Not an audit-token path → the base generic single-{id} delete handles it.
145
+ if (!obj.DeleteAPIPath || !/\{audit/i.test(obj.DeleteAPIPath)) {
146
+ return super.DeleteRecord(ctx);
147
+ }
148
+ const auditToken = await this.ResolveAuditToken(ctx);
149
+ if (auditToken == null) {
150
+ return {
151
+ Success: false,
152
+ StatusCode: 0,
153
+ ErrorMessage: `DeleteRecord("${ctx.ObjectName}"): ${obj.DeleteAPIPath} requires a GrowthZone audit ` +
154
+ `token, but none was resolvable for ExternalID "${ctx.ExternalID}".`,
155
+ };
156
+ }
157
+ const auth = await this.Authenticate(ci, ctx.ContextUser);
158
+ // Resource id → the FIRST {…id} var; audit token → the {audit…} var.
159
+ const path = obj.DeleteAPIPath
160
+ .replace(/\{[A-Za-z]*[iI]d\}/, encodeURIComponent(ctx.ExternalID))
161
+ .replace(/\{audit[A-Za-z]*\}/i, encodeURIComponent(auditToken));
162
+ const baseURL = this.GetBaseURL(ci, auth);
163
+ const url = `${baseURL.replace(/\/$/, '')}${path.startsWith('/') ? path : `/${path}`}`;
164
+ const response = await this.MakeHTTPRequest(auth, url, obj.DeleteMethod ?? 'DELETE', this.BuildHeaders(auth));
165
+ if (response.Status >= 200 && response.Status < 300) {
166
+ return { Success: true, StatusCode: response.Status, ExternalID: ctx.ExternalID };
167
+ }
168
+ return {
169
+ Success: false,
170
+ StatusCode: response.Status,
171
+ ErrorMessage: this.ExtractErrorMessage(response) ?? `HTTP ${response.Status} on delete`,
172
+ };
173
+ }
174
+ /**
175
+ * Resolves a record's current GrowthZone audit token (optimistic-concurrency) for an audit-token
176
+ * delete. GrowthZone exposes it as `AuditId` / `{Object}AuditId` on the record; we GetRecord and
177
+ * return the first match. Returns null when the record or token cannot be found.
178
+ */
179
+ async ResolveAuditToken(ctx) {
180
+ const rec = await this.GetRecord({
181
+ CompanyIntegration: ctx.CompanyIntegration,
182
+ ObjectName: ctx.ObjectName,
183
+ ContextUser: ctx.ContextUser,
184
+ ExternalID: ctx.ExternalID,
185
+ });
186
+ if (!rec)
187
+ return null;
188
+ for (const k of ['AuditId', 'auditId', 'AuditID', `${ctx.ObjectName}AuditId`]) {
189
+ const v = rec.Fields[k];
190
+ if (v != null && String(v).length > 0)
191
+ return String(v);
192
+ }
193
+ return null;
194
+ }
195
+ // ── Action-object surfacing: METADATA-DRIVEN (no hardcoded catalog) ──
196
+ /**
197
+ * Surfaces the FULL object universe straight from the IntegrationEngineBase
198
+ * cache (the Declared metadata in `.growthzone.integration.json`). There is NO
199
+ * hardcoded catalog in this connector — the prohibited module-level object/field
200
+ * literal is intentionally absent. When the cache is unavailable (e.g. action
201
+ * generation invoked before the engine is configured) this returns []; the live
202
+ * discovery path (DiscoverObjects/DiscoverFields) is the authoritative surface.
203
+ */
204
+ GetIntegrationObjects() {
205
+ const integration = IntegrationEngineBase.Instance.GetIntegrationByName(INTEGRATION_NAME);
206
+ if (!integration)
207
+ return [];
208
+ const objects = IntegrationEngineBase.Instance.GetActiveIntegrationObjects(integration.ID);
209
+ return objects.map(obj => this.ObjectEntityToInfo(obj));
210
+ }
211
+ GetActionGeneratorConfig() {
212
+ const objects = this.GetIntegrationObjects();
213
+ if (objects.length === 0)
214
+ return null;
215
+ return {
216
+ IntegrationName: INTEGRATION_NAME,
217
+ CategoryName: INTEGRATION_NAME,
218
+ IconClass: 'fa-solid fa-seedling',
219
+ Objects: objects,
220
+ IncludeSearch: false,
221
+ IncludeList: false,
222
+ CategoryDescription: 'GrowthZone association management — contacts, memberships, groups, certifications, events, store, billing, and directories',
223
+ ParentCategoryName: 'Association Management',
224
+ };
225
+ }
226
+ /** Maps a cached IntegrationObject + its fields to the action-generator info shape. */
227
+ ObjectEntityToInfo(obj) {
228
+ const fields = IntegrationEngineBase.Instance.GetIntegrationObjectFields(obj.ID)
229
+ .filter(f => f.Status === 'Active')
230
+ .sort((a, b) => a.Sequence - b.Sequence);
231
+ const fieldInfos = fields.map(f => ({
232
+ Name: f.Name,
233
+ DisplayName: f.DisplayName ?? f.Name,
234
+ Type: (f.Type ?? 'string').toLowerCase(),
235
+ IsRequired: f.IsRequired ?? false,
236
+ IsReadOnly: f.IsReadOnly ?? true,
237
+ IsPrimaryKey: f.IsPrimaryKey ?? false,
238
+ Description: f.Description ?? undefined,
239
+ }));
240
+ return {
241
+ Name: obj.Name,
242
+ DisplayName: obj.DisplayName ?? obj.Name,
243
+ Description: obj.Description ?? undefined,
244
+ SupportsWrite: obj.SupportsWrite ?? false,
245
+ Fields: fieldInfos,
246
+ };
247
+ }
248
+ // ─── BaseRESTIntegrationConnector abstract methods ──────────────
249
+ /**
250
+ * OAuth2 bearer authentication. Mints/refreshes the access token via the shared
251
+ * {@link OAuth2TokenManager}: PRIMARY `refresh_token` grant when a refresh token is
252
+ * present, otherwise the documented FALLBACK `password` grant.
253
+ */
254
+ async Authenticate(companyIntegration, contextUser) {
255
+ if (this.authCache)
256
+ return this.authCache;
257
+ const config = await this.ParseConfig(companyIntegration, contextUser);
258
+ const baseUrl = this.ResolveBaseUrl(config);
259
+ const token = await this.MintToken(config, baseUrl);
260
+ const auth = { Token: token, BaseUrl: baseUrl, Config: config };
261
+ this.authCache = auth;
262
+ return auth;
263
+ }
264
+ /** Selects the grant and runs the token round-trip through OAuth2TokenManager. */
265
+ async MintToken(config, baseUrl) {
266
+ const tokenURL = this.ResolveTokenURL(config, baseUrl);
267
+ const grant = config.RefreshToken ? 'refresh_token' : 'password';
268
+ const req = {
269
+ TokenURL: tokenURL,
270
+ ClientId: config.ClientId,
271
+ ClientSecret: config.ClientSecret,
272
+ RefreshToken: config.RefreshToken,
273
+ Username: config.Username,
274
+ Password: config.Password,
275
+ Scopes: config.Scopes,
276
+ ScopeParam: 'scopes',
277
+ TimeoutMs: config.RequestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS,
278
+ };
279
+ const token = await this.tokenManager.GetAccessToken(req, grant);
280
+ return token.AccessToken;
281
+ }
282
+ /** Sends the OAuth2 bearer token on every request. */
283
+ BuildHeaders(auth) {
284
+ // NOTE: GrowthZone documents an `Authorization: ApiKey {key}` alternate, but it is
285
+ // DEPRECATED-not-used per the integration Configuration. OAuth2 bearer is the only
286
+ // wired path; do not reintroduce an ApiKey branch as the primary.
287
+ const token = auth.Token ?? auth.Token ?? '';
288
+ return {
289
+ 'Authorization': `Bearer ${token}`,
290
+ 'Accept': 'application/json',
291
+ 'Content-Type': 'application/json',
292
+ };
293
+ }
294
+ GetBaseURL(_companyIntegration, auth) {
295
+ return auth.BaseUrl;
296
+ }
297
+ /**
298
+ * Normalizes GrowthZone responses. Handles three real shapes:
299
+ * 1. `{ Results: [...], TotalRecordAvailable: N }` — paginated lists
300
+ * 2. raw array — some endpoints return an array at the root
301
+ * 3. single object — genuine per-parent DETAIL endpoints (Person, Organization,
302
+ * ContactCustomField, ContactNotes, ContactEngagement, ScheduledBillingUpdate,
303
+ * MembershipChange) return ONE real record per parent. These ARE kept.
304
+ * and coerces `0001-01-01` sentinel dates / empty strings to null.
305
+ *
306
+ * THE EMPTY-EVENT-CHILD SENTINEL (PROBLEMS_LOG #22/#23/#27 — idempotency, surgically scoped):
307
+ * The per-event child list endpoints (`/api/events/sponsors?eventId=X`, …/sessions, …/attendees,
308
+ * etc.) do NOT return `{ Results: [] }` when an event has no children — they return the bare
309
+ * EVENT-DETAIL wrapper instead. That wrapper carries volatile per-fetch audit fields
310
+ * (EventAuditId / EventDetailAuditId), so minting a record from it produced ONE empty placeholder
311
+ * per event whose §4 content-hash identity drifted every sync → child tables doubled (254 = 2×127,
312
+ * all rows empty). Fix: detect the event wrapper (it uniquely carries EventAuditId+EventDetailAuditId
313
+ * — a genuine child/detail record never does) and emit []. This is the ONLY single-object case we
314
+ * drop; every legitimate single-record detail endpoint is preserved (the #27 regression guard).
315
+ * Populated children still arrive as a `Results` envelope and map normally.
316
+ *
317
+ * The FULL source record passes through (no field filtering) so the framework's custom-column
318
+ * capture sees everything the source returned.
319
+ */
320
+ NormalizeResponse(rawBody, responseDataKey) {
321
+ if (rawBody == null)
322
+ return [];
323
+ if (Array.isArray(rawBody)) {
324
+ return rawBody.map(r => this.NormalizeRecord(r));
325
+ }
326
+ if (typeof rawBody === 'object') {
327
+ const body = rawBody;
328
+ const envelope = body;
329
+ if (Array.isArray(envelope.Results)) {
330
+ return envelope.Results.map(r => this.NormalizeRecord(r));
331
+ }
332
+ if (responseDataKey && Array.isArray(body[responseDataKey])) {
333
+ return body[responseDataKey].map(r => this.NormalizeRecord(r));
334
+ }
335
+ // Empty-event-child sentinel: an event-child endpoint returned the bare EVENT wrapper
336
+ // (no children for this event). Emit nothing — never a non-idempotent empty placeholder.
337
+ if (this.isEmptyEventChildWrapper(body))
338
+ return [];
339
+ // Genuine single-object detail record (Person / Organization / ContactCustomField / …).
340
+ return [this.NormalizeRecord(body)];
341
+ }
342
+ return [];
343
+ }
344
+ /**
345
+ * True when a single-object response is the GrowthZone EVENT-DETAIL wrapper — the body the
346
+ * per-event child endpoints return when an event has zero children (see NormalizeResponse).
347
+ * The wrapper is unmistakable: it carries BOTH event-audit stamps. No genuine child/detail
348
+ * record (Person, Organization, custom fields, notes, engagement) carries these keys, so this
349
+ * stays surgically scoped to the empty-event-child case and never drops a real record.
350
+ */
351
+ isEmptyEventChildWrapper(body) {
352
+ return 'EventAuditId' in body && 'EventDetailAuditId' in body;
353
+ }
354
+ /**
355
+ * Derives OData pagination state. GrowthZone returns no `HasMore` flag, so end-of-stream
356
+ * is inferred from a short page and/or `TotalRecordAvailable` vs offset+count.
357
+ */
358
+ ExtractPaginationInfo(rawBody, _paginationType, _currentPage, currentOffset, pageSize) {
359
+ if (!rawBody || typeof rawBody !== 'object') {
360
+ return { HasMore: false };
361
+ }
362
+ const body = rawBody;
363
+ const results = Array.isArray(body.Results)
364
+ ? body.Results
365
+ : (Array.isArray(rawBody) ? rawBody : []);
366
+ const total = typeof body.TotalRecordAvailable === 'number' ? body.TotalRecordAvailable : undefined;
367
+ const nextOffset = currentOffset + results.length;
368
+ // AUTHORITATIVE: when GrowthZone reports the total, trust it over any page-size heuristic.
369
+ // The server caps a page at GROWTHZONE_MAX_PAGE_SIZE (100) even when we request more, so a
370
+ // "short" page (results.length < requested pageSize) is NOT a reliable end-of-stream signal —
371
+ // it's just the server cap. Comparing offset+count to the total is the only correct terminator.
372
+ if (total !== undefined) {
373
+ return nextOffset >= total
374
+ ? { HasMore: false, TotalRecords: total }
375
+ : { HasMore: true, NextOffset: nextOffset, TotalRecords: total };
376
+ }
377
+ // No total provided: the only reliable terminator is an EMPTY page. A non-empty short page may
378
+ // simply be the server cap, so we keep paging until the server returns zero rows.
379
+ void pageSize; // intentionally not used as a terminator (see above)
380
+ if (results.length === 0) {
381
+ return { HasMore: false };
382
+ }
383
+ return { HasMore: true, NextOffset: nextOffset };
384
+ }
385
+ /**
386
+ * Emits GrowthZone OData params (`skip`/`top`) plus, for an incremental IO, the vendor
387
+ * watermark param. The watermark behaviour is fully METADATA-DRIVEN: the param NAME comes
388
+ * from the IO's `IncrementalWatermarkField` (e.g. `modifiedSince` on the Contact object) and
389
+ * is emitted only when `SupportsIncrementalSync=true` AND a watermark value is in context —
390
+ * never keyed off a hardcoded path suffix or object name.
391
+ */
392
+ BuildPaginatedURL(basePath, obj, _page, offset, _cursor, effectivePageSize) {
393
+ const requested = effectivePageSize ?? obj.DefaultPageSize ?? DEFAULT_PAGE_SIZE;
394
+ // Clamp to GrowthZone's server-side page ceiling — a larger `top` is silently capped at 100
395
+ // (truncating pagination) and on some endpoints is rejected outright with HTTP 400.
396
+ const limit = Math.min(requested, GROWTHZONE_MAX_PAGE_SIZE);
397
+ const separator = basePath.includes('?') ? '&' : '?';
398
+ const params = new URLSearchParams();
399
+ const watermarkField = obj.IncrementalWatermarkField;
400
+ if (obj.SupportsIncrementalSync && watermarkField && this.currentWatermark) {
401
+ params.set(watermarkField, this.currentWatermark);
402
+ }
403
+ params.set(ODATA_TOP_PARAM, String(limit));
404
+ if (offset > 0)
405
+ params.set(ODATA_SKIP_PARAM, String(offset));
406
+ const qs = params.toString();
407
+ return qs ? `${basePath}${separator}${qs}` : basePath;
408
+ }
409
+ /** Executes an HTTP request with client-side throttling + retry for 429/503. */
410
+ async MakeHTTPRequest(auth, url, method, headers, body) {
411
+ const gzAuth = auth;
412
+ const maxRetries = gzAuth.Config.MaxRetries ?? DEFAULT_MAX_RETRIES;
413
+ const timeoutMs = gzAuth.Config.RequestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
414
+ const minInterval = gzAuth.Config.MinRequestIntervalMs ?? DEFAULT_MIN_REQUEST_INTERVAL_MS;
415
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
416
+ await this.ThrottleIfNeeded(minInterval);
417
+ const fetchOptions = {
418
+ method,
419
+ headers,
420
+ signal: AbortSignal.timeout(timeoutMs),
421
+ };
422
+ if (body !== undefined && method !== 'GET') {
423
+ fetchOptions.body = typeof body === 'string' ? body : JSON.stringify(body);
424
+ }
425
+ let response;
426
+ try {
427
+ response = await fetch(url, fetchOptions);
428
+ }
429
+ catch (err) {
430
+ if (attempt < maxRetries && this.IsTransientNetworkError(err)) {
431
+ await this.Sleep(this.BackoffDelay(attempt));
432
+ continue;
433
+ }
434
+ throw err;
435
+ }
436
+ this.lastRequestTime = Date.now();
437
+ if ((response.status === 429 || response.status === 503) && attempt < maxRetries) {
438
+ console.warn(`[GrowthZone] HTTP ${response.status} from ${url} — backing off`);
439
+ await this.Sleep(this.RetryAfterMs(response) ?? this.BackoffDelay(attempt));
440
+ continue;
441
+ }
442
+ return this.BuildRESTResponse(response);
443
+ }
444
+ throw new Error(`GrowthZone request failed after ${maxRetries + 1} attempts: ${url}`);
445
+ }
446
+ // ─── TestConnection ──────────────────────────────────────────────
447
+ /**
448
+ * Tests connectivity by minting an OAuth2 token and listing one Contact record. A 2xx
449
+ * confirms the OAuth2 credentials + base URL are valid against the live API.
450
+ */
451
+ async TestConnection(companyIntegration, contextUser) {
452
+ try {
453
+ const auth = (await this.Authenticate(companyIntegration, contextUser));
454
+ const headers = this.BuildHeaders(auth);
455
+ const url = `${auth.BaseUrl}/api/contacts?${ODATA_TOP_PARAM}=1`;
456
+ const response = await this.MakeHTTPRequest(auth, url, 'GET', headers);
457
+ if (response.Status < 200 || response.Status >= 300) {
458
+ return {
459
+ Success: false,
460
+ Message: `GrowthZone returned HTTP ${response.Status} from ${url}`,
461
+ };
462
+ }
463
+ return {
464
+ Success: true,
465
+ Message: `Connected to GrowthZone at ${auth.BaseUrl}`,
466
+ ServerVersion: 'GrowthZone REST API',
467
+ };
468
+ }
469
+ catch (err) {
470
+ const message = err instanceof Error ? err.message : String(err);
471
+ return { Success: false, Message: `Connection failed: ${message}` };
472
+ }
473
+ }
474
+ // ─── Discovery (metadata-driven, no hardcoded catalog) ───────────
475
+ /**
476
+ * Discovers the full object universe from the IntegrationEngineBase cache (the Declared
477
+ * metadata). GrowthZone publishes its catalog credential-free (curated + dev docs), so
478
+ * the baseline is Declared metadata — never hardcoded here, never sampled at build.
479
+ */
480
+ async DiscoverObjects(companyIntegration, contextUser) {
481
+ const seeded = await super.DiscoverObjects(companyIntegration, contextUser);
482
+ if (seeded.length > 0)
483
+ return seeded;
484
+ // No seeded Declared metadata is loaded (a credential-free static context with no DB-backed
485
+ // IntegrationEngine — e.g. the structural self-check tiers). GrowthZone's object catalog is
486
+ // surfaced from the runtime-seeded Declared metadata (loaded from the DB) plus live-API custom
487
+ // fields; it is NOT a hardcoded code constant (per the no-hardcoded-catalog rule) and so cannot
488
+ // be reproduced from connector source without a live connection / loaded credential configuration.
489
+ // Signal that explicitly so credential-free self-check tiers SKIP honestly ("proven at the live
490
+ // tier") instead of misreading an empty result as catalog drift. In real runtime (and the hybrid
491
+ // e2e, where `mj sync push` seeds the catalog) `seeded` is non-empty and this never throws.
492
+ throw new Error('GrowthZone DiscoverObjects requires a live connection / loaded credential configuration JSON — the object catalog is runtime-seeded (Declared metadata) plus live-discovered, not statically reproducible credential-free.');
493
+ }
494
+ /** Discovers fields for an object from the cached Declared metadata. */
495
+ async DiscoverFields(companyIntegration, objectName, contextUser) {
496
+ return super.DiscoverFields(companyIntegration, objectName, contextUser);
497
+ }
498
+ // ─── FetchChanges override ──────────────────────────────────────
499
+ /**
500
+ * Sets the watermark context the OData URL builder needs, delegates
501
+ * the actual walk to the base (which descends nested Door→Segment template-var paths via
502
+ * FK metadata so nested IOs never silently return 0 rows), then advances the watermark
503
+ * from the returned records on the final batch only (partial-failure-safe).
504
+ */
505
+ async FetchChanges(ctx) {
506
+ this.currentWatermark = ctx.WatermarkValue ?? undefined;
507
+ const result = await super.FetchChanges(ctx);
508
+ const isFinal = !result.HasMore;
509
+ const newWatermark = isFinal
510
+ ? (this.ExtractLatestModifiedDate(result.Records) ?? ctx.WatermarkValue ?? undefined)
511
+ : undefined;
512
+ return { ...result, NewWatermarkValue: newWatermark };
513
+ }
514
+ // ─── Config parsing ──────────────────────────────────────────────
515
+ /**
516
+ * Parses the OAuth2 connection config, preferring the attached MJ Credential over the
517
+ * raw Configuration JSON. Credential bytes are resolved at runtime — never at build.
518
+ */
519
+ async ParseConfig(companyIntegration, contextUser) {
520
+ if (companyIntegration.CredentialID) {
521
+ return this.ParseConfigFromCredential(companyIntegration.CredentialID, contextUser);
522
+ }
523
+ if (companyIntegration.Configuration) {
524
+ return this.ValidateConfig(JSON.parse(companyIntegration.Configuration));
525
+ }
526
+ throw new Error('GrowthZone connector requires either CredentialID or Configuration JSON');
527
+ }
528
+ /** Loads the OAuth2 config from the MJ: Credentials entity Values JSON. */
529
+ async ParseConfigFromCredential(credentialID, contextUser, provider) {
530
+ const md = provider ?? new Metadata();
531
+ const cred = await md.GetEntityObject('MJ: Credentials', contextUser);
532
+ const loaded = await cred.Load(credentialID);
533
+ if (!loaded || !cred.Values) {
534
+ throw new Error('GrowthZone credential could not be loaded or has no Values JSON');
535
+ }
536
+ return this.ValidateConfig(JSON.parse(cred.Values));
537
+ }
538
+ /** Validates the parsed config + applies defaults. Field names are case-insensitive. */
539
+ ValidateConfig(raw) {
540
+ if (!raw || typeof raw !== 'object') {
541
+ throw new Error('GrowthZone configuration is not a valid object');
542
+ }
543
+ const obj = raw;
544
+ const getStr = (...keys) => {
545
+ for (const key of keys) {
546
+ const lower = key.toLowerCase();
547
+ for (const [k, v] of Object.entries(obj)) {
548
+ if (k.toLowerCase() === lower && typeof v === 'string' && v.length > 0)
549
+ return v;
550
+ }
551
+ }
552
+ return undefined;
553
+ };
554
+ const getNum = (...keys) => {
555
+ for (const key of keys) {
556
+ const lower = key.toLowerCase();
557
+ for (const [k, v] of Object.entries(obj)) {
558
+ if (k.toLowerCase() === lower && typeof v === 'number')
559
+ return v;
560
+ }
561
+ }
562
+ return undefined;
563
+ };
564
+ const baseURL = getStr('baseurl', 'base_url');
565
+ if (!baseURL) {
566
+ throw new Error('GrowthZone configuration missing required field: BaseURL');
567
+ }
568
+ const clientId = getStr('clientid', 'client_id');
569
+ const clientSecret = getStr('clientsecret', 'client_secret');
570
+ if (!clientId || !clientSecret) {
571
+ throw new Error('GrowthZone OAuth2 configuration missing required field: ClientId / ClientSecret');
572
+ }
573
+ const refreshToken = getStr('refreshtoken', 'refresh_token');
574
+ const username = getStr('username', 'user');
575
+ const password = getStr('password', 'pass');
576
+ if (!refreshToken && !(username && password)) {
577
+ throw new Error('GrowthZone OAuth2 configuration requires a RefreshToken (primary grant) ' +
578
+ 'or Username + Password (fallback grant)');
579
+ }
580
+ return {
581
+ ClientId: clientId,
582
+ ClientSecret: clientSecret,
583
+ RefreshToken: refreshToken,
584
+ Username: username,
585
+ Password: password,
586
+ Scopes: getStr('scopes', 'scope'),
587
+ BaseURL: baseURL,
588
+ TokenURL: getStr('tokenurl', 'token_url'),
589
+ Tenant: getStr('tenant', 'subdomain'),
590
+ MaxRetries: getNum('maxretries') ?? DEFAULT_MAX_RETRIES,
591
+ RequestTimeoutMs: getNum('requesttimeoutms') ?? DEFAULT_REQUEST_TIMEOUT_MS,
592
+ MinRequestIntervalMs: getNum('minrequestintervalms') ?? DEFAULT_MIN_REQUEST_INTERVAL_MS,
593
+ };
594
+ }
595
+ /** Resolves the API base URL from the credential's BaseURL (never a hardcoded subdomain). */
596
+ ResolveBaseUrl(config) {
597
+ return config.BaseURL.replace(/\/+$/, '');
598
+ }
599
+ /** Resolves the OAuth2 token endpoint: credential TokenURL override, else `{base}/oauth/token`. */
600
+ ResolveTokenURL(config, baseUrl) {
601
+ if (config.TokenURL && config.TokenURL.length > 0)
602
+ return config.TokenURL;
603
+ // Strip a trailing /api or /API segment so the token endpoint sits at the host root.
604
+ const root = baseUrl.replace(/\/api$/i, '');
605
+ return `${root}${DEFAULT_TOKEN_PATH}`;
606
+ }
607
+ // ─── Normalization helpers ───────────────────────────────────────
608
+ /** Normalizes a raw record's top-level values (null-date + empty-string → null). */
609
+ NormalizeRecord(record) {
610
+ const out = {};
611
+ for (const [key, value] of Object.entries(record)) {
612
+ out[key] = this.NormalizeValue(value);
613
+ }
614
+ return out;
615
+ }
616
+ /** Converts GrowthZone sentinel values to null. */
617
+ NormalizeValue(value) {
618
+ if (value == null)
619
+ return null;
620
+ if (typeof value === 'string') {
621
+ if (value === '')
622
+ return null;
623
+ if (value.startsWith(GROWTHZONE_NULL_DATE))
624
+ return null;
625
+ return value;
626
+ }
627
+ return value;
628
+ }
629
+ /** Extracts the latest ModifiedDate across a batch for watermark advancement. */
630
+ ExtractLatestModifiedDate(records) {
631
+ let latest = null;
632
+ for (const rec of records) {
633
+ const raw = rec.Fields?.ModifiedDate ?? rec.Fields?.modifiedDate;
634
+ if (typeof raw !== 'string' || raw.length === 0)
635
+ continue;
636
+ const d = new Date(raw);
637
+ if (!isNaN(d.getTime()) && (latest === null || d > latest))
638
+ latest = d;
639
+ }
640
+ return latest ? latest.toISOString() : null;
641
+ }
642
+ // ─── HTTP helpers ────────────────────────────────────────────────
643
+ /** Throttle to respect GrowthZone's recommended minimum request interval. */
644
+ async ThrottleIfNeeded(minIntervalMs) {
645
+ const elapsed = Date.now() - this.lastRequestTime;
646
+ if (elapsed < minIntervalMs)
647
+ await this.Sleep(minIntervalMs - elapsed);
648
+ }
649
+ /** Parses a Retry-After header (seconds or http-date) into ms, if present. */
650
+ RetryAfterMs(response) {
651
+ const header = response.headers.get('retry-after');
652
+ if (!header)
653
+ return undefined;
654
+ const asSeconds = Number(header);
655
+ if (!isNaN(asSeconds))
656
+ return Math.max(0, asSeconds * 1_000);
657
+ const asDate = new Date(header).getTime();
658
+ if (!isNaN(asDate))
659
+ return Math.max(0, asDate - Date.now());
660
+ return undefined;
661
+ }
662
+ /** Exponential backoff delay for retry attempts (capped at 30s). */
663
+ BackoffDelay(attempt) {
664
+ return Math.min(2_000 * Math.pow(2, attempt), 30_000);
665
+ }
666
+ /** Checks whether an error is transient (network/timeout). */
667
+ IsTransientNetworkError(err) {
668
+ if (!(err instanceof Error))
669
+ return false;
670
+ const msg = err.message.toLowerCase();
671
+ return msg.includes('timeout') || msg.includes('abort') ||
672
+ msg.includes('econnreset') || msg.includes('econnrefused') ||
673
+ msg.includes('fetch failed');
674
+ }
675
+ /** Builds the normalized RESTResponse from a fetch Response. */
676
+ async BuildRESTResponse(response) {
677
+ const headers = {};
678
+ response.headers.forEach((v, k) => { headers[k.toLowerCase()] = v; });
679
+ const text = await response.text();
680
+ let body = null;
681
+ if (text.length > 0) {
682
+ try {
683
+ body = JSON.parse(text);
684
+ }
685
+ catch {
686
+ body = text;
687
+ }
688
+ }
689
+ return { Status: response.status, Body: body, Headers: headers };
690
+ }
691
+ /** Promise-wrapped setTimeout. */
692
+ Sleep(ms) {
693
+ return new Promise(resolve => setTimeout(resolve, ms));
694
+ }
695
+ };
696
+ GrowthZoneConnector = __decorate([
697
+ RegisterClass(BaseIntegrationConnector, '@memberjunction/connector-growthzone')
698
+ ], GrowthZoneConnector);
699
+ export { GrowthZoneConnector };
700
+ /** Tree-shaking prevention — import and call from the package entry point. */
701
+ export function LoadGrowthZoneConnector() { }
702
+ //# sourceMappingURL=GrowthZoneConnector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GrowthZoneConnector.js","sourceRoot":"","sources":["../src/GrowthZoneConnector.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export * from './GrowthZoneConnector.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 './GrowthZoneConnector.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,0BAA0B,CAAC;AAEzC;oGACoG;AACpG,MAAM,UAAU,iBAAiB,KAAiD,CAAC"}
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@memberjunction/connector-growthzone",
3
+ "version": "1.0.0",
4
+ "description": "MemberJunction GrowthZone 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 --passWithNoTests"
14
+ },
15
+ "author": "MemberJunction.com",
16
+ "license": "ISC",
17
+ "peerDependencies": {
18
+ "@memberjunction/core": ">=5.42.0 <6.0.0",
19
+ "@memberjunction/core-entities": ">=5.42.0 <6.0.0",
20
+ "@memberjunction/global": ">=5.42.0 <6.0.0",
21
+ "@memberjunction/integration-engine": ">=5.42.0 <6.0.0",
22
+ "@memberjunction/integration-engine-base": ">=5.42.0 <6.0.0"
23
+ },
24
+ "dependencies": {},
25
+ "devDependencies": {
26
+ "@types/node": "24.10.11",
27
+ "tsc-alias": "^1.8.16",
28
+ "typescript": "^5.9.3",
29
+ "vitest": "^4.0.18",
30
+ "@memberjunction/core": "^5.42.0",
31
+ "@memberjunction/core-entities": "^5.42.0",
32
+ "@memberjunction/global": "^5.42.0",
33
+ "@memberjunction/integration-engine": "^5.42.0",
34
+ "@memberjunction/integration-engine-base": "^5.42.0"
35
+ },
36
+ "repository": {
37
+ "type": "git",
38
+ "url": "https://github.com/MemberJunction/Integrations"
39
+ }
40
+ }