@memberjunction/connector-netsuite 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,135 @@
1
+ import { type UserInfo } from '@memberjunction/core';
2
+ import type { MJCompanyIntegrationEntity } from '@memberjunction/core-entities';
3
+ import { BaseRESTIntegrationConnector, type RESTAuthContext, type RESTResponse, type PaginationState, type PaginationType, type ConnectionTestResult, type FetchContext, type FetchBatchResult, type ExternalObjectSchema, type ExternalFieldSchema, type RateLimitPolicy } from '@memberjunction/integration-engine';
4
+ export declare class NetSuiteConnector extends BaseRESTIntegrationConnector {
5
+ private oauth2Manager;
6
+ get IntegrationName(): string;
7
+ get SupportsCreate(): boolean;
8
+ get SupportsUpdate(): boolean;
9
+ get SupportsDelete(): boolean;
10
+ /**
11
+ * NON-authoritative for deactivation (deliberately false — the T3-deadlock-safe posture).
12
+ *
13
+ * The metadata-catalog enumerates the Record-Service record types (standard + custom) — but it is
14
+ * NOT the complete gamut of this connector's emitted IOs: the contract retains sublist-access-path
15
+ * children (InvoiceItem, SalesOrderItem) that are reachable as nested graph children and NEVER
16
+ * appear in the record-type catalog (SublistScopeExtension). It also returns lowercase record-type
17
+ * SLUGS (`phonecall`) while Declared standard objects are humanized (`Phone Call`). An authoritative
18
+ * (deactivating) refresh would therefore wrongly Disable every Declared sublist child and any object
19
+ * whose catalog slug doesn't byte-match the Declared name. Per the convention rule — a partial/scoped
20
+ * enumeration is NEVER authoritative — absence here proves nothing, so we deactivate nothing. The
21
+ * standard universe is the Declared metadata baseline; a credential only ADDS the account's customs.
22
+ */
23
+ get DiscoveryIsAuthoritative(): boolean;
24
+ /**
25
+ * NetSuite governs by CONCURRENT-request limit (5–50 by tier), not requests/sec. We cap inner
26
+ * concurrency conservatively below the smallest tier; the engine's adaptive controller ramps within it.
27
+ */
28
+ get MaxConcurrencyHint(): number;
29
+ /** No documented RPS policy — concurrency-governed. Return null so the engine derives a conservative rate. */
30
+ get RateLimitPolicy(): RateLimitPolicy | null;
31
+ /**
32
+ * SuiteQL fetches in `ORDER BY lastModifiedDate` and an updated record always re-surfaces at a new,
33
+ * higher modstamp — so the last batch's max IS the true high-water mark. Safe for the engine to use
34
+ * the returned watermark to narrow the next incremental.
35
+ */
36
+ get MonotonicWatermark(): boolean;
37
+ /**
38
+ * For watermark-less objects, NetSuite's universal numeric `id` is a stable monotonic ordering key
39
+ * usable for keyset/seek resume.
40
+ */
41
+ StableOrderingKey(_objectName: string): string | null;
42
+ /** Parse NetSuite's 429 Retry-After (seconds) into ms; concurrency limit errors carry no precise hint. */
43
+ ExtractRetryAfterMs(error: unknown): number | undefined;
44
+ private ParseConfig;
45
+ private ValidateConfig;
46
+ /** Determines the auth mode from explicit AuthFlow or the credential keys present. */
47
+ private ResolveAuthMode;
48
+ protected Authenticate(companyIntegration: MJCompanyIntegrationEntity, contextUser: UserInfo): Promise<RESTAuthContext>;
49
+ private AssertTBACredentials;
50
+ /**
51
+ * Resolves a bearer token for OAuth2 mode. A pre-minted BearerToken/AccessToken is used directly
52
+ * (NetSuite OAuth2 access tokens are supplied by the operator). When a RefreshToken + TokenURL +
53
+ * client credentials are present, the shared OAuth2TokenManager refreshes — crypto-free token round-trip.
54
+ */
55
+ private ResolveBearerToken;
56
+ /**
57
+ * Base headers WITHOUT an Authorization value — the auth header is per-request (OAuth 1.0a signs
58
+ * each URL+method) so it's injected in MakeHTTPRequest, not here. The base class calls BuildHeaders
59
+ * for its generic CRUD / pagination GETs; MakeHTTPRequest stamps the signed/bearer Authorization.
60
+ */
61
+ protected BuildHeaders(_auth: RESTAuthContext): Record<string, string>;
62
+ /** Builds the Authorization header for a specific URL + method (TBA signs per request; OAuth2 is static). */
63
+ private BuildAuthorizationHeader;
64
+ /** Base URL = host root; the IO's APIPath already carries the full /services/rest/... path. */
65
+ protected GetBaseURL(_companyIntegration: MJCompanyIntegrationEntity, auth: RESTAuthContext): string;
66
+ protected MakeHTTPRequest(auth: RESTAuthContext, url: string, method: string, headers: Record<string, string>, body?: unknown): Promise<RESTResponse>;
67
+ private BuildRESTResponse;
68
+ private ParseRetryAfterHeader;
69
+ private IsTransientNetworkError;
70
+ private BackoffDelay;
71
+ private Sleep;
72
+ /** Strips the NetSuite HATEOAS envelope: { items: [...] } → the items array; bare object → [object]. */
73
+ protected NormalizeResponse(rawBody: unknown, _responseDataKey: string | null): Record<string, unknown>[];
74
+ /** Offset pagination: hasMore flag + offset advance (or the next-link href when present). */
75
+ protected ExtractPaginationInfo(rawBody: unknown, _paginationType: PaginationType, _currentPage: number, currentOffset: number, pageSize: number): PaginationState;
76
+ /** Reads the `offset` query param from the HATEOAS rel='next' link when the envelope provides one. */
77
+ private NextOffsetFromLinks;
78
+ /** Validates credentials + host by running a trivial SuiteQL probe (no record dependency). */
79
+ TestConnection(companyIntegration: MJCompanyIntegrationEntity, contextUser: UserInfo): Promise<ConnectionTestResult>;
80
+ /**
81
+ * Discovers objects by UNIONING the Declared baseline with the live metadata-catalog (case-2
82
+ * auth-gated discovery). The Declared metadata (200+ standard record types + the sublist-access-path
83
+ * children that the catalog never lists) is the floor — it is ALWAYS surfaced so a refresh can't drop
84
+ * it. The catalog's record-type SLUGS (`phonecall`) are name-aligned back to the Declared name via the
85
+ * cached Configuration.recordTypeSlug so a slug never duplicates an existing Declared object; an
86
+ * unknown slug passes through as a NEW custom record. Net: standard universe always present + tenant
87
+ * customs added. Non-authoritative (DiscoveryIsAuthoritative=false), so no object is ever deactivated.
88
+ */
89
+ DiscoverObjects(companyIntegration: MJCompanyIntegrationEntity, contextUser: UserInfo): Promise<ExternalObjectSchema[]>;
90
+ /**
91
+ * The Declared (cached) baseline objects — the floor DiscoverObjects always surfaces. Seam over the
92
+ * base class's cache read so the union logic is unit-testable without a DB-backed engine.
93
+ */
94
+ protected GetDeclaredObjects(companyIntegration: MJCompanyIntegrationEntity, contextUser: UserInfo): Promise<ExternalObjectSchema[]>;
95
+ /** Maps each Declared IO's Configuration.recordTypeSlug → its Declared Name (for catalog name-alignment). */
96
+ protected BuildSlugToDeclaredNameMap(integrationID: string): Map<string, string>;
97
+ /**
98
+ * Resolves the metadata-catalog slug for an object name: the cached IO's Configuration.recordTypeSlug
99
+ * when present (the authoritative slug, e.g. `phonecall` for `Phone Call`), else a name collapse.
100
+ */
101
+ private ResolveRecordTypeSlug;
102
+ /** Tolerantly reads Configuration.recordTypeSlug from an IO's Configuration JSON. */
103
+ private ReadRecordTypeSlug;
104
+ /** Pulls record-type names from the catalog's `items[].name` or `links[].href` trailing segment. */
105
+ private ExtractCatalogNames;
106
+ /**
107
+ * Discovers fields for a record type from the metadata-catalog JSON-schema for that type. The schema
108
+ * `properties` map → fields; `required` → IsRequired; `readOnly`/computed markers → IsReadOnly; the
109
+ * universal numeric `id` is the PK. Falls back to the cached Declared fields when the catalog can't
110
+ * describe this type.
111
+ */
112
+ DiscoverFields(companyIntegration: MJCompanyIntegrationEntity, objectName: string, contextUser: UserInfo): Promise<ExternalFieldSchema[]>;
113
+ /** Maps a JSON-schema describe body to ExternalFieldSchema[] (provable-only: only stated flags). */
114
+ private ParseFieldSchema;
115
+ /** Maps a JSON-schema property to a generic source type. */
116
+ private MapSchemaType;
117
+ private HumanizeName;
118
+ FetchChanges(ctx: FetchContext): Promise<FetchBatchResult>;
119
+ /** Builds the SuiteQL SELECT, applying the incremental watermark predicate when a watermark exists. */
120
+ private BuildSuiteQL;
121
+ /** Quotes a watermark value for SuiteQL — wraps a NetSuite datetime in TO_DATE/quotes, escaping quotes. */
122
+ private QuoteSuiteQLDate;
123
+ /** Returns the maximum value of the watermark field across the batch (string compare on ISO timestamps). */
124
+ private MaxWatermark;
125
+ /**
126
+ * Builds an ExternalRecord carrying the FULL source record in Fields (full-record pass-through, so the
127
+ * framework's custom-column capture sees every key) and the universal `id` as the ExternalID. Mirrors
128
+ * the base class's composite-PK + content-hash fallback at the connector boundary for hand-built records.
129
+ */
130
+ private BuildExternalRecord;
131
+ /** Reads the IO's Configuration JSON (suiteQLTable, defaultPageSize) tolerantly. */
132
+ private ReadIOConfig;
133
+ private PreviewBody;
134
+ }
135
+ export declare function LoadNetSuiteConnector(): void;
@@ -0,0 +1,703 @@
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
+ * NetSuiteConnector — Integration connector for Oracle NetSuite (SuiteTalk REST Web Services).
9
+ *
10
+ * API docs: https://docs.oracle.com/en/cloud/saas/netsuite/ns-online-help/ (SuiteTalk REST API Guide)
11
+ *
12
+ * SCOPE: SuiteTalk REST Web Services only — the Record Service (CRUD on standard + custom record
13
+ * types) + the SuiteQL query service. SOAP / RESTlets / SuiteAnalytics Connect are out of scope.
14
+ *
15
+ * ── Auth (selected per-connection by Configuration / credential) ───────────────────────────────
16
+ * • OAuth 1.0a TBA (Token-Based Authentication) — the primary documented flow. Long-lived,
17
+ * non-expiring ConsumerKey/ConsumerSecret + TokenID/TokenSecret, signed per request with
18
+ * HMAC-SHA256 and a per-account `realm`. Signing is delegated to the shared `OAuth1aSigner`
19
+ * auth-helper — NEVER inline crypto.
20
+ * • OAuth 2.0 bearer — the documented alternate (Authorization Code flow). A pre-minted bearer
21
+ * access token is supplied via the credential/Configuration and sent as `Authorization: Bearer`.
22
+ * (Refresh, when a refresh token + token URL are present, is delegated to OAuth2TokenManager.)
23
+ * Mode is chosen by Configuration.AuthFlow ('oauth1-tba' | 'oauth2') or inferred from the
24
+ * credential keys present (BearerToken/AccessToken ⇒ OAuth2; ConsumerKey+TokenID ⇒ TBA).
25
+ *
26
+ * ── Host (tenant-agnostic — NO baked account) ──────────────────────────────────────────────────
27
+ * The base URL is built at runtime from Configuration.AccountID:
28
+ * `https://{accountId}.suitetalk.api.netsuite.com` (e.g. '1234567' prod, '1234567_SB1' sandbox).
29
+ * Per-account subdomains require the account id be lowercased and '_' → '-' in the subdomain.
30
+ *
31
+ * ── Reads ──────────────────────────────────────────────────────────────────────────────────────
32
+ * FetchChanges runs SuiteQL: POST /services/rest/query/v1/suiteql with the SQL in the body
33
+ * `{ q: '<SQL>' }` and the REQUIRED header `Prefer: transient`. This expresses the lastModifiedDate
34
+ * incremental watermark predicate (a thing the generic GET path cannot), so FetchChanges is the one
35
+ * genuinely-idiosyncratic override. Everything else rides the base.
36
+ *
37
+ * ── CRUD ───────────────────────────────────────────────────────────────────────────────────────
38
+ * Create/Update/Delete/Get use the base class's GENERIC per-operation path — they read the IO's
39
+ * Create/Update/Delete APIPath/Method/BodyShape/BodyKey/IDLocation columns and dispatch. NetSuite's
40
+ * Record Service CRUD is a plain REST shape (POST collection, PATCH /{id}, DELETE /{id}), so no
41
+ * override is needed or written.
42
+ *
43
+ * ── Pagination ─────────────────────────────────────────────────────────────────────────────────
44
+ * Offset/limit (default 1000). Responses carry the HATEOAS envelope { items, count, totalResults,
45
+ * hasMore, offset, links:[{rel:'next',href}] } — surfaced in NormalizeResponse + ExtractPaginationInfo.
46
+ *
47
+ * ── Incremental ────────────────────────────────────────────────────────────────────────────────
48
+ * lastModifiedDate watermark (via WatermarkService in the engine; the connector applies the IO's
49
+ * IncrementalWatermarkField inside the SuiteQL predicate and returns the max-seen on full-batch success).
50
+ *
51
+ * ── Discovery ──────────────────────────────────────────────────────────────────────────────────
52
+ * Rides the metadata-catalog MECHANISM (no baked catalog): GET /services/rest/record/v1/metadata-catalog
53
+ * enumerates the record types accessible to the credential; per-type field schema is fetched from the
54
+ * same catalog (Accept: application/schema+json). DiscoverObjects UNIONS the Declared baseline (the 200+
55
+ * standard record types + the sublist-access-path children, which the catalog never lists) with the live
56
+ * catalog's record types — catalog slugs are name-aligned back to the Declared names via each IO's
57
+ * Configuration.recordTypeSlug so a slug never duplicates an existing Declared object; unknown slugs pass
58
+ * through as new customs. Discovery is NON-authoritative (DiscoveryIsAuthoritative=false): because the
59
+ * catalog is a partial enumeration (no sublist children, slug-vs-humanized names), absence proves nothing
60
+ * and nothing is ever deactivated. Standard baseline lives in the Declared metadata file; a credential
61
+ * only ADDS the account's custom records on top.
62
+ */
63
+ import { RegisterClass } from '@memberjunction/global';
64
+ import { Metadata } from '@memberjunction/core';
65
+ import { IntegrationEngineBase } from '@memberjunction/integration-engine-base';
66
+ import { BaseIntegrationConnector, BaseRESTIntegrationConnector, OAuth1aSigner, OAuth2TokenManager, } from '@memberjunction/integration-engine';
67
+ import { z } from 'zod';
68
+ // ─── Constants ────────────────────────────────────────────────────────────
69
+ const NS_DEFAULT_PAGE_SIZE = 1000;
70
+ const NS_MAX_RETRIES = 3;
71
+ const NS_REQUEST_TIMEOUT_MS = 90_000;
72
+ const NS_RECORD_BASE_PATH = '/services/rest/record/v1';
73
+ const NS_SUITEQL_PATH = '/services/rest/query/v1/suiteql';
74
+ const NS_METADATA_CATALOG_PATH = '/services/rest/record/v1/metadata-catalog';
75
+ const NS_SERVERTIME_PATH = '/services/rest/system/v1/serverTime';
76
+ // NetSuite REST media type for record bodies (per docs: application/vnd.oracle.resource+json).
77
+ const NS_RECORD_MEDIA_TYPE = 'application/vnd.oracle.resource+json';
78
+ // Concurrency-governed API (no RPS); the smallest documented tier is 5 concurrent. Stay well under it.
79
+ const NS_MAX_CONCURRENCY = 4;
80
+ // ─── Config (typed via Zod) ─────────────────────────────────────────────────
81
+ /**
82
+ * The per-connection config the connector reads from the credential JSON or
83
+ * CompanyIntegration.Configuration. AccountID is always required; the auth fields
84
+ * present select the auth mode.
85
+ */
86
+ const NetSuiteConfigSchema = z.object({
87
+ /** Per-account id, e.g. '1234567' or '1234567_SB1'. Builds the host subdomain. */
88
+ AccountID: z.string().min(1),
89
+ /**
90
+ * Optional explicit host-root override (no trailing slash). When set, it is used verbatim
91
+ * instead of deriving `https://{accountId}.suitetalk.api.netsuite.com` from AccountID.
92
+ * Production normally leaves this UNSET (host derives from AccountID). It exists for a custom
93
+ * gateway/proxy and for pointing the connector at a mock vendor in credential-free e2e tests —
94
+ * the same `BaseURL`-from-config hook iMIS/Hivebrite/GrowthZone expose.
95
+ */
96
+ HostBaseURL: z.string().url().optional(),
97
+ /** Explicit auth mode; inferred when absent. */
98
+ AuthFlow: z.enum(['oauth1-tba', 'oauth2']).optional(),
99
+ // OAuth 1.0a TBA
100
+ ConsumerKey: z.string().optional(),
101
+ ConsumerSecret: z.string().optional(),
102
+ TokenID: z.string().optional(),
103
+ TokenSecret: z.string().optional(),
104
+ // OAuth 2.0 bearer
105
+ BearerToken: z.string().optional(),
106
+ AccessToken: z.string().optional(),
107
+ RefreshToken: z.string().optional(),
108
+ ClientID: z.string().optional(),
109
+ ClientSecret: z.string().optional(),
110
+ TokenURL: z.string().optional(),
111
+ });
112
+ // ─── Connector ────────────────────────────────────────────────────────────
113
+ let NetSuiteConnector = class NetSuiteConnector extends BaseRESTIntegrationConnector {
114
+ constructor() {
115
+ super(...arguments);
116
+ this.oauth2Manager = new OAuth2TokenManager();
117
+ }
118
+ // ── Identity + capabilities ──────────────────────────────────────────
119
+ get IntegrationName() { return 'NetSuite'; }
120
+ get SupportsCreate() { return true; }
121
+ get SupportsUpdate() { return true; }
122
+ get SupportsDelete() { return true; }
123
+ /**
124
+ * NON-authoritative for deactivation (deliberately false — the T3-deadlock-safe posture).
125
+ *
126
+ * The metadata-catalog enumerates the Record-Service record types (standard + custom) — but it is
127
+ * NOT the complete gamut of this connector's emitted IOs: the contract retains sublist-access-path
128
+ * children (InvoiceItem, SalesOrderItem) that are reachable as nested graph children and NEVER
129
+ * appear in the record-type catalog (SublistScopeExtension). It also returns lowercase record-type
130
+ * SLUGS (`phonecall`) while Declared standard objects are humanized (`Phone Call`). An authoritative
131
+ * (deactivating) refresh would therefore wrongly Disable every Declared sublist child and any object
132
+ * whose catalog slug doesn't byte-match the Declared name. Per the convention rule — a partial/scoped
133
+ * enumeration is NEVER authoritative — absence here proves nothing, so we deactivate nothing. The
134
+ * standard universe is the Declared metadata baseline; a credential only ADDS the account's customs.
135
+ */
136
+ get DiscoveryIsAuthoritative() { return false; }
137
+ /**
138
+ * NetSuite governs by CONCURRENT-request limit (5–50 by tier), not requests/sec. We cap inner
139
+ * concurrency conservatively below the smallest tier; the engine's adaptive controller ramps within it.
140
+ */
141
+ get MaxConcurrencyHint() { return NS_MAX_CONCURRENCY; }
142
+ /** No documented RPS policy — concurrency-governed. Return null so the engine derives a conservative rate. */
143
+ get RateLimitPolicy() { return null; }
144
+ /**
145
+ * SuiteQL fetches in `ORDER BY lastModifiedDate` and an updated record always re-surfaces at a new,
146
+ * higher modstamp — so the last batch's max IS the true high-water mark. Safe for the engine to use
147
+ * the returned watermark to narrow the next incremental.
148
+ */
149
+ get MonotonicWatermark() { return true; }
150
+ /**
151
+ * For watermark-less objects, NetSuite's universal numeric `id` is a stable monotonic ordering key
152
+ * usable for keyset/seek resume.
153
+ */
154
+ StableOrderingKey(_objectName) { return 'id'; }
155
+ /** Parse NetSuite's 429 Retry-After (seconds) into ms; concurrency limit errors carry no precise hint. */
156
+ ExtractRetryAfterMs(error) {
157
+ const msg = error instanceof Error ? error.message : String(error);
158
+ const m = msg.match(/retry-after[:\s]+(\d+)/i);
159
+ if (m)
160
+ return parseInt(m[1], 10) * 1000;
161
+ return undefined;
162
+ }
163
+ // ── Config / credential parsing ──────────────────────────────────────
164
+ async ParseConfig(companyIntegration, contextUser, provider) {
165
+ // Credential JSON takes precedence over the non-secret Configuration JSON.
166
+ if (companyIntegration.CredentialID) {
167
+ const md = provider ?? new Metadata();
168
+ const cred = await md.GetEntityObject('MJ: Credentials', contextUser);
169
+ const loaded = await cred.Load(companyIntegration.CredentialID);
170
+ if (loaded && cred.Values) {
171
+ return this.ValidateConfig(JSON.parse(cred.Values));
172
+ }
173
+ }
174
+ if (companyIntegration.Configuration) {
175
+ return this.ValidateConfig(JSON.parse(companyIntegration.Configuration));
176
+ }
177
+ throw new Error('NetSuite connector requires a CredentialID or Configuration JSON carrying AccountID + auth credentials.');
178
+ }
179
+ ValidateConfig(raw) {
180
+ const parsed = NetSuiteConfigSchema.safeParse(raw);
181
+ if (!parsed.success) {
182
+ throw new Error(`NetSuite Configuration is invalid: ${parsed.error.issues.map(i => i.message).join('; ')}`);
183
+ }
184
+ return parsed.data;
185
+ }
186
+ /** Determines the auth mode from explicit AuthFlow or the credential keys present. */
187
+ ResolveAuthMode(config) {
188
+ if (config.AuthFlow === 'oauth2')
189
+ return 'oauth2';
190
+ if (config.AuthFlow === 'oauth1-tba')
191
+ return 'oauth1-tba';
192
+ if (config.BearerToken || config.AccessToken || config.RefreshToken)
193
+ return 'oauth2';
194
+ return 'oauth1-tba';
195
+ }
196
+ // ── Auth ─────────────────────────────────────────────────────────────
197
+ async Authenticate(companyIntegration, contextUser) {
198
+ const config = await this.ParseConfig(companyIntegration, contextUser);
199
+ const mode = this.ResolveAuthMode(config);
200
+ const subdomain = config.AccountID.toLowerCase().replace(/_/g, '-');
201
+ // Host derives from AccountID by default; an explicit Configuration.HostBaseURL (custom
202
+ // gateway / mock vendor in e2e) wins when present. Trailing slash trimmed so APIPath joins clean.
203
+ const hostBaseURL = (config.HostBaseURL && config.HostBaseURL.trim().length > 0)
204
+ ? config.HostBaseURL.replace(/\/+$/, '')
205
+ : `https://${subdomain}.suitetalk.api.netsuite.com`;
206
+ const realm = config.AccountID.replace(/-/g, '_').toUpperCase();
207
+ let bearerToken = '';
208
+ if (mode === 'oauth2') {
209
+ bearerToken = await this.ResolveBearerToken(config);
210
+ }
211
+ else {
212
+ this.AssertTBACredentials(config);
213
+ }
214
+ return {
215
+ Token: bearerToken,
216
+ TokenType: mode === 'oauth2' ? 'Bearer' : 'OAuth1',
217
+ Config: config,
218
+ Mode: mode,
219
+ HostBaseURL: hostBaseURL,
220
+ Realm: realm,
221
+ BearerToken: bearerToken,
222
+ };
223
+ }
224
+ AssertTBACredentials(config) {
225
+ if (!config.ConsumerKey || !config.ConsumerSecret || !config.TokenID || !config.TokenSecret) {
226
+ throw new Error('NetSuite TBA (OAuth 1.0a) requires ConsumerKey, ConsumerSecret, TokenID, and TokenSecret.');
227
+ }
228
+ }
229
+ /**
230
+ * Resolves a bearer token for OAuth2 mode. A pre-minted BearerToken/AccessToken is used directly
231
+ * (NetSuite OAuth2 access tokens are supplied by the operator). When a RefreshToken + TokenURL +
232
+ * client credentials are present, the shared OAuth2TokenManager refreshes — crypto-free token round-trip.
233
+ */
234
+ async ResolveBearerToken(config) {
235
+ const direct = config.BearerToken ?? config.AccessToken;
236
+ if (config.RefreshToken && config.TokenURL && config.ClientID && config.ClientSecret) {
237
+ const token = await this.oauth2Manager.GetAccessToken({
238
+ TokenURL: config.TokenURL,
239
+ ClientId: config.ClientID,
240
+ ClientSecret: config.ClientSecret,
241
+ RefreshToken: config.RefreshToken,
242
+ }, 'refresh_token');
243
+ return token.AccessToken;
244
+ }
245
+ if (direct)
246
+ return direct;
247
+ throw new Error('NetSuite OAuth2 mode requires either a BearerToken/AccessToken, or RefreshToken + TokenURL + ClientID + ClientSecret.');
248
+ }
249
+ // ── Headers ──────────────────────────────────────────────────────────
250
+ /**
251
+ * Base headers WITHOUT an Authorization value — the auth header is per-request (OAuth 1.0a signs
252
+ * each URL+method) so it's injected in MakeHTTPRequest, not here. The base class calls BuildHeaders
253
+ * for its generic CRUD / pagination GETs; MakeHTTPRequest stamps the signed/bearer Authorization.
254
+ */
255
+ BuildHeaders(_auth) {
256
+ return {
257
+ 'Accept': 'application/json',
258
+ 'User-Agent': 'MemberJunction-Integration/1.0',
259
+ };
260
+ }
261
+ /** Builds the Authorization header for a specific URL + method (TBA signs per request; OAuth2 is static). */
262
+ BuildAuthorizationHeader(auth, url, method) {
263
+ if (auth.Mode === 'oauth2') {
264
+ return `Bearer ${auth.BearerToken}`;
265
+ }
266
+ return OAuth1aSigner.BuildAuthorizationHeader({
267
+ ConsumerKey: auth.Config.ConsumerKey,
268
+ ConsumerSecret: auth.Config.ConsumerSecret,
269
+ TokenId: auth.Config.TokenID,
270
+ TokenSecret: auth.Config.TokenSecret,
271
+ Method: method,
272
+ Url: url,
273
+ Realm: auth.Realm,
274
+ SignatureMethod: 'HMAC-SHA256',
275
+ });
276
+ }
277
+ // ── URL building ─────────────────────────────────────────────────────
278
+ /** Base URL = host root; the IO's APIPath already carries the full /services/rest/... path. */
279
+ GetBaseURL(_companyIntegration, auth) {
280
+ return auth.HostBaseURL;
281
+ }
282
+ // ── HTTP transport ───────────────────────────────────────────────────
283
+ async MakeHTTPRequest(auth, url, method, headers, body) {
284
+ const nsAuth = auth;
285
+ const effectiveHeaders = { ...headers, 'Authorization': this.BuildAuthorizationHeader(nsAuth, url, method) };
286
+ if (body !== undefined && method !== 'GET' && method !== 'DELETE') {
287
+ // Record bodies use NetSuite's vendor media type unless a caller (SuiteQL) set its own.
288
+ if (!effectiveHeaders['Content-Type'])
289
+ effectiveHeaders['Content-Type'] = NS_RECORD_MEDIA_TYPE;
290
+ }
291
+ for (let attempt = 0; attempt <= NS_MAX_RETRIES; attempt++) {
292
+ let response;
293
+ try {
294
+ const opts = {
295
+ method,
296
+ headers: effectiveHeaders,
297
+ signal: AbortSignal.timeout(NS_REQUEST_TIMEOUT_MS),
298
+ };
299
+ if (body !== undefined && method !== 'GET' && method !== 'DELETE') {
300
+ opts.body = typeof body === 'string' ? body : JSON.stringify(body);
301
+ }
302
+ response = await fetch(url, opts);
303
+ }
304
+ catch (err) {
305
+ if (attempt < NS_MAX_RETRIES && this.IsTransientNetworkError(err)) {
306
+ await this.Sleep(this.BackoffDelay(attempt));
307
+ continue;
308
+ }
309
+ throw err;
310
+ }
311
+ // Concurrency-limit rejection (429) or transient 5xx — back off + retry within budget.
312
+ if ((response.status === 429 || response.status >= 500) && attempt < NS_MAX_RETRIES) {
313
+ const retryAfter = this.ParseRetryAfterHeader(response);
314
+ await this.Sleep(retryAfter ?? this.BackoffDelay(attempt));
315
+ continue;
316
+ }
317
+ return this.BuildRESTResponse(response);
318
+ }
319
+ throw new Error(`NetSuite request failed after ${NS_MAX_RETRIES + 1} attempts: ${url}`);
320
+ }
321
+ async BuildRESTResponse(response) {
322
+ const hdrs = {};
323
+ response.headers.forEach((v, k) => { hdrs[k.toLowerCase()] = v; });
324
+ const ct = hdrs['content-type'] ?? '';
325
+ let parsed;
326
+ if (ct.includes('json')) {
327
+ const text = await response.text();
328
+ parsed = text.length > 0 ? JSON.parse(text) : {};
329
+ }
330
+ else {
331
+ parsed = await response.text();
332
+ }
333
+ return { Status: response.status, Body: parsed, Headers: hdrs };
334
+ }
335
+ ParseRetryAfterHeader(response) {
336
+ const ra = response.headers.get('retry-after');
337
+ if (!ra)
338
+ return undefined;
339
+ const secs = parseInt(ra, 10);
340
+ return Number.isFinite(secs) ? secs * 1000 : undefined;
341
+ }
342
+ IsTransientNetworkError(err) {
343
+ const msg = err instanceof Error ? err.message : String(err);
344
+ return /econnreset|etimedout|enotfound|eai_again|socket hang up|network|fetch failed|timeout/i.test(msg);
345
+ }
346
+ BackoffDelay(attempt) {
347
+ return Math.min(1000 * Math.pow(2, attempt) + Math.floor(Math.random() * 500), 30_000);
348
+ }
349
+ Sleep(ms) {
350
+ return new Promise(resolve => setTimeout(resolve, ms));
351
+ }
352
+ // ── Response parsing ─────────────────────────────────────────────────
353
+ /** Strips the NetSuite HATEOAS envelope: { items: [...] } → the items array; bare object → [object]. */
354
+ NormalizeResponse(rawBody, _responseDataKey) {
355
+ if (Array.isArray(rawBody))
356
+ return rawBody;
357
+ if (!rawBody || typeof rawBody !== 'object')
358
+ return [];
359
+ const body = rawBody;
360
+ if (Array.isArray(body.items))
361
+ return body.items;
362
+ // A single-record GET returns the record object directly (with a `links` HATEOAS array).
363
+ if (Object.keys(body).length > 0)
364
+ return [body];
365
+ return [];
366
+ }
367
+ /** Offset pagination: hasMore flag + offset advance (or the next-link href when present). */
368
+ ExtractPaginationInfo(rawBody, _paginationType, _currentPage, currentOffset, pageSize) {
369
+ const body = (rawBody && typeof rawBody === 'object') ? rawBody : {};
370
+ const records = this.NormalizeResponse(rawBody, null);
371
+ // Prefer the explicit hasMore flag; fall back to "got a full page".
372
+ const hasMore = body.hasMore ?? (records.length >= (pageSize || NS_DEFAULT_PAGE_SIZE));
373
+ const nextOffset = this.NextOffsetFromLinks(body) ?? currentOffset + records.length;
374
+ return {
375
+ HasMore: hasMore,
376
+ NextOffset: nextOffset,
377
+ TotalRecords: body.totalResults,
378
+ };
379
+ }
380
+ /** Reads the `offset` query param from the HATEOAS rel='next' link when the envelope provides one. */
381
+ NextOffsetFromLinks(body) {
382
+ const next = body.links?.find(l => l.rel === 'next');
383
+ if (!next?.href)
384
+ return undefined;
385
+ const qIdx = next.href.indexOf('?');
386
+ if (qIdx === -1)
387
+ return undefined;
388
+ const off = new URLSearchParams(next.href.substring(qIdx + 1)).get('offset');
389
+ const n = off != null ? parseInt(off, 10) : NaN;
390
+ return Number.isFinite(n) ? n : undefined;
391
+ }
392
+ // ── TestConnection ───────────────────────────────────────────────────
393
+ /** Validates credentials + host by running a trivial SuiteQL probe (no record dependency). */
394
+ async TestConnection(companyIntegration, contextUser) {
395
+ try {
396
+ const auth = await this.Authenticate(companyIntegration, contextUser);
397
+ // serverTime is the cheapest authenticated read and seeds the incremental watermark.
398
+ const url = `${auth.HostBaseURL}${NS_SERVERTIME_PATH}`;
399
+ const headers = this.BuildHeaders(auth);
400
+ const response = await this.MakeHTTPRequest(auth, url, 'GET', headers);
401
+ if (response.Status >= 200 && response.Status < 300) {
402
+ const st = response.Body;
403
+ const t = st?.serverTime ?? st?.time ?? 'unknown';
404
+ return { Success: true, Message: `Connected to NetSuite (${auth.Mode}); server time ${t}` };
405
+ }
406
+ if (response.Status === 401 || response.Status === 403) {
407
+ return { Success: false, Message: `NetSuite authentication failed: HTTP ${response.Status}` };
408
+ }
409
+ return { Success: false, Message: `NetSuite returned HTTP ${response.Status} from ${NS_SERVERTIME_PATH}` };
410
+ }
411
+ catch (err) {
412
+ return { Success: false, Message: err instanceof Error ? err.message : String(err) };
413
+ }
414
+ }
415
+ // ── Discovery (auth-gated metadata-catalog MECHANISM — no baked catalog) ──
416
+ /**
417
+ * Discovers objects by UNIONING the Declared baseline with the live metadata-catalog (case-2
418
+ * auth-gated discovery). The Declared metadata (200+ standard record types + the sublist-access-path
419
+ * children that the catalog never lists) is the floor — it is ALWAYS surfaced so a refresh can't drop
420
+ * it. The catalog's record-type SLUGS (`phonecall`) are name-aligned back to the Declared name via the
421
+ * cached Configuration.recordTypeSlug so a slug never duplicates an existing Declared object; an
422
+ * unknown slug passes through as a NEW custom record. Net: standard universe always present + tenant
423
+ * customs added. Non-authoritative (DiscoveryIsAuthoritative=false), so no object is ever deactivated.
424
+ */
425
+ async DiscoverObjects(companyIntegration, contextUser) {
426
+ const declared = await this.GetDeclaredObjects(companyIntegration, contextUser);
427
+ const slugToDeclaredName = this.BuildSlugToDeclaredNameMap(companyIntegration.IntegrationID);
428
+ const declaredNamesLower = new Set(declared.map(o => o.Name.toLowerCase()));
429
+ const auth = await this.Authenticate(companyIntegration, contextUser);
430
+ const url = `${auth.HostBaseURL}${NS_METADATA_CATALOG_PATH}`;
431
+ const headers = { ...this.BuildHeaders(auth), 'Accept': 'application/schema+json' };
432
+ const response = await this.MakeHTTPRequest(auth, url, 'GET', headers);
433
+ if (response.Status < 200 || response.Status >= 300) {
434
+ throw new Error(`NetSuite metadata-catalog returned HTTP ${response.Status} — cannot enumerate record types.`);
435
+ }
436
+ const slugs = this.ExtractCatalogNames(response.Body);
437
+ const out = [...declared];
438
+ for (const slug of slugs) {
439
+ // Name-align to the Declared name this slug maps to; for an UNKNOWN slug (a new custom record)
440
+ // keep the slug VERBATIM as the Name — the slug is the API-addressable record-type identity, so
441
+ // it must survive untouched for the discovered custom's CRUD/SuiteQL paths. (Label is humanized.)
442
+ const name = slugToDeclaredName.get(slug.toLowerCase()) ?? slug;
443
+ if (declaredNamesLower.has(name.toLowerCase()))
444
+ continue; // already in the Declared floor
445
+ declaredNamesLower.add(name.toLowerCase());
446
+ out.push({
447
+ Name: name,
448
+ Label: this.HumanizeName(slug),
449
+ Description: `NetSuite ${this.HumanizeName(slug)} record`,
450
+ SupportsIncrementalSync: true,
451
+ SupportsWrite: true,
452
+ });
453
+ }
454
+ return out;
455
+ }
456
+ /**
457
+ * The Declared (cached) baseline objects — the floor DiscoverObjects always surfaces. Seam over the
458
+ * base class's cache read so the union logic is unit-testable without a DB-backed engine.
459
+ */
460
+ GetDeclaredObjects(companyIntegration, contextUser) {
461
+ return super.DiscoverObjects(companyIntegration, contextUser);
462
+ }
463
+ /** Maps each Declared IO's Configuration.recordTypeSlug → its Declared Name (for catalog name-alignment). */
464
+ BuildSlugToDeclaredNameMap(integrationID) {
465
+ const map = new Map();
466
+ const objects = IntegrationEngineBase.Instance.GetActiveIntegrationObjects(integrationID);
467
+ for (const obj of objects) {
468
+ const slug = this.ReadRecordTypeSlug(obj.Configuration);
469
+ if (slug)
470
+ map.set(slug.toLowerCase(), obj.Name);
471
+ }
472
+ return map;
473
+ }
474
+ /**
475
+ * Resolves the metadata-catalog slug for an object name: the cached IO's Configuration.recordTypeSlug
476
+ * when present (the authoritative slug, e.g. `phonecall` for `Phone Call`), else a name collapse.
477
+ */
478
+ ResolveRecordTypeSlug(integrationID, objectName) {
479
+ try {
480
+ const obj = this.GetCachedObject(integrationID, objectName);
481
+ const slug = this.ReadRecordTypeSlug(obj.Configuration);
482
+ if (slug)
483
+ return slug;
484
+ }
485
+ catch {
486
+ // object not in cache (a freshly-discovered custom) — fall through to name collapse
487
+ }
488
+ return objectName.toLowerCase().replace(/\s+/g, '');
489
+ }
490
+ /** Tolerantly reads Configuration.recordTypeSlug from an IO's Configuration JSON. */
491
+ ReadRecordTypeSlug(configuration) {
492
+ if (!configuration)
493
+ return undefined;
494
+ try {
495
+ const cfg = JSON.parse(configuration);
496
+ return typeof cfg.recordTypeSlug === 'string' ? cfg.recordTypeSlug : undefined;
497
+ }
498
+ catch {
499
+ return undefined;
500
+ }
501
+ }
502
+ /** Pulls record-type names from the catalog's `items[].name` or `links[].href` trailing segment. */
503
+ ExtractCatalogNames(body) {
504
+ const out = [];
505
+ for (const it of body.items ?? []) {
506
+ if (it.name)
507
+ out.push(it.name);
508
+ }
509
+ if (out.length === 0) {
510
+ for (const lk of body.links ?? []) {
511
+ if (lk.rel === 'self' || !lk.href)
512
+ continue;
513
+ const seg = lk.href.split('/').filter(Boolean).pop();
514
+ if (seg)
515
+ out.push(seg);
516
+ }
517
+ }
518
+ return out;
519
+ }
520
+ /**
521
+ * Discovers fields for a record type from the metadata-catalog JSON-schema for that type. The schema
522
+ * `properties` map → fields; `required` → IsRequired; `readOnly`/computed markers → IsReadOnly; the
523
+ * universal numeric `id` is the PK. Falls back to the cached Declared fields when the catalog can't
524
+ * describe this type.
525
+ */
526
+ async DiscoverFields(companyIntegration, objectName, contextUser) {
527
+ const auth = await this.Authenticate(companyIntegration, contextUser);
528
+ // Prefer the IO's Configuration.recordTypeSlug (authoritative slug); else collapse the name.
529
+ const slug = this.ResolveRecordTypeSlug(companyIntegration.IntegrationID, objectName);
530
+ const url = `${auth.HostBaseURL}${NS_METADATA_CATALOG_PATH}/${encodeURIComponent(slug)}`;
531
+ const headers = { ...this.BuildHeaders(auth), 'Accept': 'application/schema+json' };
532
+ const response = await this.MakeHTTPRequest(auth, url, 'GET', headers);
533
+ if (response.Status >= 200 && response.Status < 300) {
534
+ const parsed = this.ParseFieldSchema(response.Body);
535
+ if (parsed.length > 0)
536
+ return parsed;
537
+ }
538
+ // Could not describe via catalog — defer to the Declared metadata (cache) the base class returns.
539
+ return super.DiscoverFields(companyIntegration, objectName, contextUser);
540
+ }
541
+ /** Maps a JSON-schema describe body to ExternalFieldSchema[] (provable-only: only stated flags). */
542
+ ParseFieldSchema(body) {
543
+ if (!body || typeof body !== 'object')
544
+ return [];
545
+ const schema = body;
546
+ if (!schema.properties)
547
+ return [];
548
+ const required = new Set(schema.required ?? []);
549
+ const fields = [];
550
+ for (const [name, prop] of Object.entries(schema.properties)) {
551
+ const isId = name === 'id';
552
+ fields.push({
553
+ Name: name,
554
+ Label: String(prop['title'] ?? this.HumanizeName(name)),
555
+ Description: typeof prop['description'] === 'string' ? prop['description'] : undefined,
556
+ DataType: this.MapSchemaType(prop),
557
+ IsRequired: required.has(name),
558
+ IsPrimaryKey: isId || undefined,
559
+ IsUniqueKey: isId,
560
+ IsReadOnly: prop['readOnly'] === true || isId,
561
+ });
562
+ }
563
+ if (!fields.some(f => f.Name === 'id')) {
564
+ fields.unshift({
565
+ Name: 'id', Label: 'Internal ID', Description: 'NetSuite internal record ID',
566
+ DataType: 'string', IsRequired: false, IsPrimaryKey: true, IsUniqueKey: true, IsReadOnly: true,
567
+ });
568
+ }
569
+ return fields;
570
+ }
571
+ /** Maps a JSON-schema property to a generic source type. */
572
+ MapSchemaType(prop) {
573
+ const t = String(prop['type'] ?? '').toLowerCase();
574
+ const fmt = String(prop['format'] ?? '').toLowerCase();
575
+ if (fmt.includes('date') || fmt.includes('time'))
576
+ return 'datetime';
577
+ if (t === 'integer')
578
+ return 'number';
579
+ if (t === 'number')
580
+ return 'decimal';
581
+ if (t === 'boolean')
582
+ return 'boolean';
583
+ return 'string';
584
+ }
585
+ HumanizeName(name) {
586
+ return name
587
+ .replace(/([a-z])([A-Z])/g, '$1 $2')
588
+ .replace(/[_-]/g, ' ')
589
+ .trim()
590
+ .split(/\s+/)
591
+ .map(w => w.charAt(0).toUpperCase() + w.slice(1))
592
+ .join(' ') || name;
593
+ }
594
+ // ── FetchChanges — SuiteQL (the one idiosyncratic override) ──────────
595
+ //
596
+ // OVERRIDE RATIONALE: NetSuite reads run through SuiteQL (POST with SQL in the body + the required
597
+ // `Prefer: transient` header) so the lastModifiedDate incremental predicate can be expressed — a
598
+ // shape the base class's generic GET path cannot produce. CRUD stays on the generic per-operation path.
599
+ async FetchChanges(ctx) {
600
+ const obj = this.GetCachedObject(ctx.CompanyIntegration.IntegrationID, ctx.ObjectName);
601
+ const fields = this.GetCachedFields(obj.ID);
602
+ const auth = await this.Authenticate(ctx.CompanyIntegration, ctx.ContextUser);
603
+ const cfg = this.ReadIOConfig(obj);
604
+ // SuiteQL table = the IO's configured suiteQLTable; fall back to the catalog slug (collapsed name).
605
+ const table = cfg.suiteQLTable ?? this.ResolveRecordTypeSlug(ctx.CompanyIntegration.IntegrationID, ctx.ObjectName);
606
+ const watermarkField = obj.IncrementalWatermarkField ?? 'lastModifiedDate';
607
+ const offset = ctx.CurrentOffset ?? 0;
608
+ const pageSize = obj.DefaultPageSize ?? cfg.defaultPageSize ?? NS_DEFAULT_PAGE_SIZE;
609
+ const sql = this.BuildSuiteQL(table, watermarkField, ctx.WatermarkValue);
610
+ const url = `${auth.HostBaseURL}${NS_SUITEQL_PATH}?limit=${pageSize}&offset=${offset}`;
611
+ // SuiteQL bodies are plain JSON and REQUIRE the `Prefer: transient` header.
612
+ const headers = {
613
+ ...this.BuildHeaders(auth),
614
+ 'Content-Type': 'application/json',
615
+ 'Prefer': 'transient',
616
+ };
617
+ const response = await this.MakeHTTPRequest(auth, url, 'POST', headers, { q: sql });
618
+ if (response.Status < 200 || response.Status >= 300) {
619
+ throw new Error(`NetSuite SuiteQL read failed for "${ctx.ObjectName}": HTTP ${response.Status} — ${this.PreviewBody(response.Body)}`);
620
+ }
621
+ const body = response.Body;
622
+ const rawRecords = this.NormalizeResponse(body, null);
623
+ const pkFieldNames = fields.filter(f => f.IsPrimaryKey).map(f => f.Name);
624
+ const records = rawRecords.map(raw => this.BuildExternalRecord(raw, ctx.ObjectName, pkFieldNames));
625
+ const hasMore = body.hasMore ?? (rawRecords.length >= pageSize);
626
+ // Track the max watermark and persist it ONLY when the full result set has been drained
627
+ // (partial-failure semantics: a mid-iteration stop leaves the watermark unchanged).
628
+ const newWatermark = !hasMore ? this.MaxWatermark(rawRecords, watermarkField) : undefined;
629
+ return {
630
+ Records: records,
631
+ HasMore: hasMore,
632
+ NextOffset: offset + rawRecords.length,
633
+ NewWatermarkValue: newWatermark,
634
+ };
635
+ }
636
+ /** Builds the SuiteQL SELECT, applying the incremental watermark predicate when a watermark exists. */
637
+ BuildSuiteQL(table, watermarkField, watermark) {
638
+ const where = watermark
639
+ ? ` WHERE ${watermarkField} > ${this.QuoteSuiteQLDate(watermark)}`
640
+ : '';
641
+ return `SELECT * FROM ${table}${where} ORDER BY ${watermarkField}`;
642
+ }
643
+ /** Quotes a watermark value for SuiteQL — wraps a NetSuite datetime in TO_DATE/quotes, escaping quotes. */
644
+ QuoteSuiteQLDate(value) {
645
+ const escaped = value.replace(/'/g, "''");
646
+ return `'${escaped}'`;
647
+ }
648
+ /** Returns the maximum value of the watermark field across the batch (string compare on ISO timestamps). */
649
+ MaxWatermark(records, field) {
650
+ let max;
651
+ for (const r of records) {
652
+ const v = r[field];
653
+ if (typeof v === 'string' && v.length > 0 && (!max || v > max))
654
+ max = v;
655
+ }
656
+ return max;
657
+ }
658
+ /**
659
+ * Builds an ExternalRecord carrying the FULL source record in Fields (full-record pass-through, so the
660
+ * framework's custom-column capture sees every key) and the universal `id` as the ExternalID. Mirrors
661
+ * the base class's composite-PK + content-hash fallback at the connector boundary for hand-built records.
662
+ */
663
+ BuildExternalRecord(raw, objectType, pkFieldNames) {
664
+ const keys = pkFieldNames.length > 0 ? pkFieldNames : ['id'];
665
+ const parts = keys.map(k => {
666
+ const v = raw[k];
667
+ return v == null ? '' : String(v);
668
+ });
669
+ const allPresent = parts.every(p => p.length > 0);
670
+ const externalID = allPresent ? parts.join('|') : String(raw['id'] ?? '');
671
+ return {
672
+ ExternalID: externalID,
673
+ ObjectType: objectType,
674
+ Fields: raw,
675
+ };
676
+ }
677
+ /** Reads the IO's Configuration JSON (suiteQLTable, defaultPageSize) tolerantly. */
678
+ ReadIOConfig(obj) {
679
+ if (!obj.Configuration)
680
+ return {};
681
+ try {
682
+ const cfg = JSON.parse(obj.Configuration);
683
+ return {
684
+ suiteQLTable: typeof cfg.suiteQLTable === 'string' ? cfg.suiteQLTable : undefined,
685
+ defaultPageSize: typeof cfg.defaultPageSize === 'number' ? cfg.defaultPageSize : undefined,
686
+ };
687
+ }
688
+ catch {
689
+ return {};
690
+ }
691
+ }
692
+ PreviewBody(body) {
693
+ const s = typeof body === 'string' ? body : JSON.stringify(body);
694
+ return s ? s.substring(0, 300) : '';
695
+ }
696
+ };
697
+ NetSuiteConnector = __decorate([
698
+ RegisterClass(BaseIntegrationConnector, '@memberjunction/connector-netsuite')
699
+ ], NetSuiteConnector);
700
+ export { NetSuiteConnector };
701
+ // Tree-shaking prevention — REQUIRED so @RegisterClass survives bundling.
702
+ export function LoadNetSuiteConnector() { }
703
+ //# sourceMappingURL=NetSuiteConnector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NetSuiteConnector.js","sourceRoot":"","sources":["../src/NetSuiteConnector.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export * from './NetSuiteConnector.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 './NetSuiteConnector.js';
2
+ /** Open App bootstrap entry: importing this module ran the connector's @RegisterClass decorator;
3
+ * this no-op satisfies the loader's required startupExport and forces the import at MJAPI boot. */
4
+ export function registerConnector() { }
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,wBAAwB,CAAC;AAEvC;oGACoG;AACpG,MAAM,UAAU,iBAAiB,KAAiD,CAAC"}
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@memberjunction/connector-netsuite",
3
+ "version": "1.0.0",
4
+ "description": "MemberJunction NetSuite 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
+ "zod": "~3.24.4"
26
+ },
27
+ "devDependencies": {
28
+ "@types/node": "24.10.11",
29
+ "tsc-alias": "^1.8.16",
30
+ "typescript": "^5.9.3",
31
+ "vitest": "^4.0.18",
32
+ "@memberjunction/core": "^5.42.0",
33
+ "@memberjunction/core-entities": "^5.42.0",
34
+ "@memberjunction/global": "^5.42.0",
35
+ "@memberjunction/integration-engine": "^5.42.0",
36
+ "@memberjunction/integration-engine-base": "^5.42.0"
37
+ },
38
+ "repository": {
39
+ "type": "git",
40
+ "url": "https://github.com/MemberJunction/Integrations"
41
+ }
42
+ }