@memberjunction/integration-connectors 5.41.0 → 5.42.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/dist/CventConnector.d.ts +146 -0
  2. package/dist/CventConnector.d.ts.map +1 -0
  3. package/dist/CventConnector.js +540 -0
  4. package/dist/CventConnector.js.map +1 -0
  5. package/dist/DynamicsDataverseConnector.d.ts +296 -0
  6. package/dist/DynamicsDataverseConnector.d.ts.map +1 -0
  7. package/dist/DynamicsDataverseConnector.js +790 -0
  8. package/dist/DynamicsDataverseConnector.js.map +1 -0
  9. package/dist/FontevaConnector.d.ts +153 -0
  10. package/dist/FontevaConnector.d.ts.map +1 -0
  11. package/dist/FontevaConnector.js +421 -0
  12. package/dist/FontevaConnector.js.map +1 -0
  13. package/dist/GrowthZoneConnector.d.ts +123 -125
  14. package/dist/GrowthZoneConnector.d.ts.map +1 -1
  15. package/dist/GrowthZoneConnector.js +456 -499
  16. package/dist/GrowthZoneConnector.js.map +1 -1
  17. package/dist/HivebriteConnector.d.ts +151 -0
  18. package/dist/HivebriteConnector.d.ts.map +1 -0
  19. package/dist/HivebriteConnector.js +533 -0
  20. package/dist/HivebriteConnector.js.map +1 -0
  21. package/dist/IMISConnector.d.ts +112 -96
  22. package/dist/IMISConnector.d.ts.map +1 -1
  23. package/dist/IMISConnector.js +430 -417
  24. package/dist/IMISConnector.js.map +1 -1
  25. package/dist/MemberSuiteConnector.d.ts +185 -0
  26. package/dist/MemberSuiteConnector.d.ts.map +1 -0
  27. package/dist/MemberSuiteConnector.js +762 -0
  28. package/dist/MemberSuiteConnector.js.map +1 -0
  29. package/dist/NeonCRMConnector.d.ts +273 -0
  30. package/dist/NeonCRMConnector.d.ts.map +1 -0
  31. package/dist/NeonCRMConnector.js +1039 -0
  32. package/dist/NeonCRMConnector.js.map +1 -0
  33. package/dist/NetForumConnector.d.ts +140 -47
  34. package/dist/NetForumConnector.d.ts.map +1 -1
  35. package/dist/NetForumConnector.js +668 -395
  36. package/dist/NetForumConnector.js.map +1 -1
  37. package/dist/NetSuiteConnector.d.ts +116 -50
  38. package/dist/NetSuiteConnector.d.ts.map +1 -1
  39. package/dist/NetSuiteConnector.js +608 -510
  40. package/dist/NetSuiteConnector.js.map +1 -1
  41. package/dist/NimbleAMSConnector.d.ts +104 -11
  42. package/dist/NimbleAMSConnector.d.ts.map +1 -1
  43. package/dist/NimbleAMSConnector.js +552 -258
  44. package/dist/NimbleAMSConnector.js.map +1 -1
  45. package/dist/NoviConnector.d.ts +178 -0
  46. package/dist/NoviConnector.d.ts.map +1 -0
  47. package/dist/NoviConnector.js +566 -0
  48. package/dist/NoviConnector.js.map +1 -0
  49. package/dist/ORCIDConnector.d.ts +207 -0
  50. package/dist/ORCIDConnector.d.ts.map +1 -0
  51. package/dist/ORCIDConnector.js +708 -0
  52. package/dist/ORCIDConnector.js.map +1 -0
  53. package/dist/OpenWaterConnector.d.ts +173 -0
  54. package/dist/OpenWaterConnector.d.ts.map +1 -0
  55. package/dist/OpenWaterConnector.js +893 -0
  56. package/dist/OpenWaterConnector.js.map +1 -0
  57. package/dist/PathLMSConnector.d.ts +395 -0
  58. package/dist/PathLMSConnector.d.ts.map +1 -0
  59. package/dist/PathLMSConnector.js +1281 -0
  60. package/dist/PathLMSConnector.js.map +1 -0
  61. package/dist/PheedLoopConnector.d.ts +119 -0
  62. package/dist/PheedLoopConnector.d.ts.map +1 -0
  63. package/dist/PheedLoopConnector.js +429 -0
  64. package/dist/PheedLoopConnector.js.map +1 -0
  65. package/dist/PropFuelConnector.d.ts +135 -61
  66. package/dist/PropFuelConnector.d.ts.map +1 -1
  67. package/dist/PropFuelConnector.js +366 -467
  68. package/dist/PropFuelConnector.js.map +1 -1
  69. package/dist/RhythmConnector.d.ts +188 -0
  70. package/dist/RhythmConnector.d.ts.map +1 -0
  71. package/dist/RhythmConnector.js +784 -0
  72. package/dist/RhythmConnector.js.map +1 -0
  73. package/dist/SalesforceConnector.d.ts +69 -3
  74. package/dist/SalesforceConnector.d.ts.map +1 -1
  75. package/dist/SalesforceConnector.js +188 -166
  76. package/dist/SalesforceConnector.js.map +1 -1
  77. package/dist/SharePointConnector.d.ts +37 -30
  78. package/dist/SharePointConnector.d.ts.map +1 -1
  79. package/dist/SharePointConnector.js +163 -243
  80. package/dist/SharePointConnector.js.map +1 -1
  81. package/dist/YourMembershipConnector.d.ts.map +1 -1
  82. package/dist/YourMembershipConnector.js +13 -5
  83. package/dist/YourMembershipConnector.js.map +1 -1
  84. package/dist/index.d.ts +14 -3
  85. package/dist/index.d.ts.map +1 -1
  86. package/dist/index.js +14 -3
  87. package/dist/index.js.map +1 -1
  88. package/package.json +8 -7
  89. package/dist/BettyConnector.d.ts +0 -288
  90. package/dist/BettyConnector.d.ts.map +0 -1
  91. package/dist/BettyConnector.js +0 -909
  92. package/dist/BettyConnector.js.map +0 -1
@@ -64,135 +64,6 @@ const TOOLING_API_DENYLIST = new Set([
64
64
  'CustomHttpHeader',
65
65
  'FormulaFunctionCategory',
66
66
  ]);
67
- // ─── Salesforce Object Metadata for Action Generation ─────────────────
68
- const SALESFORCE_OBJECTS = [
69
- {
70
- Name: 'Account', DisplayName: 'Account',
71
- Description: 'A company, organization, or consumer in Salesforce CRM', SupportsWrite: true,
72
- Fields: [
73
- { Name: 'Name', DisplayName: 'Account Name', Type: 'string', IsRequired: true, IsReadOnly: false, IsPrimaryKey: false, Description: 'Account name' },
74
- { Name: 'Industry', DisplayName: 'Industry', Type: 'string', IsRequired: false, IsReadOnly: false, IsPrimaryKey: false, Description: 'Account industry' },
75
- { Name: 'Phone', DisplayName: 'Phone', Type: 'string', IsRequired: false, IsReadOnly: false, IsPrimaryKey: false, Description: 'Account phone number' },
76
- { Name: 'Website', DisplayName: 'Website', Type: 'string', IsRequired: false, IsReadOnly: false, IsPrimaryKey: false, Description: 'Account website URL' },
77
- { Name: 'BillingStreet', DisplayName: 'Billing Street', Type: 'string', IsRequired: false, IsReadOnly: false, IsPrimaryKey: false, Description: 'Billing street address' },
78
- { Name: 'BillingCity', DisplayName: 'Billing City', Type: 'string', IsRequired: false, IsReadOnly: false, IsPrimaryKey: false, Description: 'Billing city' },
79
- { Name: 'BillingState', DisplayName: 'Billing State', Type: 'string', IsRequired: false, IsReadOnly: false, IsPrimaryKey: false, Description: 'Billing state/province' },
80
- { Name: 'BillingPostalCode', DisplayName: 'Billing Postal Code', Type: 'string', IsRequired: false, IsReadOnly: false, IsPrimaryKey: false, Description: 'Billing postal code' },
81
- { Name: 'BillingCountry', DisplayName: 'Billing Country', Type: 'string', IsRequired: false, IsReadOnly: false, IsPrimaryKey: false, Description: 'Billing country' },
82
- { Name: 'Description', DisplayName: 'Description', Type: 'text', IsRequired: false, IsReadOnly: false, IsPrimaryKey: false, Description: 'Account description' },
83
- { Name: 'Type', DisplayName: 'Type', Type: 'string', IsRequired: false, IsReadOnly: false, IsPrimaryKey: false, Description: 'Account type' },
84
- { Name: 'AnnualRevenue', DisplayName: 'Annual Revenue', Type: 'number', IsRequired: false, IsReadOnly: false, IsPrimaryKey: false, Description: 'Annual revenue' },
85
- { Name: 'NumberOfEmployees', DisplayName: 'Employees', Type: 'number', IsRequired: false, IsReadOnly: false, IsPrimaryKey: false, Description: 'Number of employees' },
86
- { Name: 'Id', DisplayName: 'Account ID', Type: 'string', IsRequired: false, IsReadOnly: true, IsPrimaryKey: true, Description: 'Salesforce record ID' },
87
- ],
88
- },
89
- {
90
- Name: 'Contact', DisplayName: 'Contact',
91
- Description: 'A person associated with an account in Salesforce CRM', SupportsWrite: true,
92
- Fields: [
93
- { Name: 'Email', DisplayName: 'Email', Type: 'string', IsRequired: false, IsReadOnly: false, IsPrimaryKey: false, Description: 'Contact email address' },
94
- { Name: 'FirstName', DisplayName: 'First Name', Type: 'string', IsRequired: false, IsReadOnly: false, IsPrimaryKey: false, Description: 'Contact first name' },
95
- { Name: 'LastName', DisplayName: 'Last Name', Type: 'string', IsRequired: true, IsReadOnly: false, IsPrimaryKey: false, Description: 'Contact last name' },
96
- { Name: 'Phone', DisplayName: 'Phone', Type: 'string', IsRequired: false, IsReadOnly: false, IsPrimaryKey: false, Description: 'Contact phone number' },
97
- { Name: 'MobilePhone', DisplayName: 'Mobile Phone', Type: 'string', IsRequired: false, IsReadOnly: false, IsPrimaryKey: false, Description: 'Mobile phone number' },
98
- { Name: 'Title', DisplayName: 'Title', Type: 'string', IsRequired: false, IsReadOnly: false, IsPrimaryKey: false, Description: 'Contact title' },
99
- { Name: 'Department', DisplayName: 'Department', Type: 'string', IsRequired: false, IsReadOnly: false, IsPrimaryKey: false, Description: 'Contact department' },
100
- { Name: 'MailingStreet', DisplayName: 'Mailing Street', Type: 'string', IsRequired: false, IsReadOnly: false, IsPrimaryKey: false, Description: 'Mailing street address' },
101
- { Name: 'MailingCity', DisplayName: 'Mailing City', Type: 'string', IsRequired: false, IsReadOnly: false, IsPrimaryKey: false, Description: 'Mailing city' },
102
- { Name: 'MailingState', DisplayName: 'Mailing State', Type: 'string', IsRequired: false, IsReadOnly: false, IsPrimaryKey: false, Description: 'Mailing state/province' },
103
- { Name: 'MailingPostalCode', DisplayName: 'Mailing Postal Code', Type: 'string', IsRequired: false, IsReadOnly: false, IsPrimaryKey: false, Description: 'Mailing postal code' },
104
- { Name: 'MailingCountry', DisplayName: 'Mailing Country', Type: 'string', IsRequired: false, IsReadOnly: false, IsPrimaryKey: false, Description: 'Mailing country' },
105
- { Name: 'AccountId', DisplayName: 'Account ID', Type: 'string', IsRequired: false, IsReadOnly: false, IsPrimaryKey: false, Description: 'Parent account ID' },
106
- { Name: 'Id', DisplayName: 'Contact ID', Type: 'string', IsRequired: false, IsReadOnly: true, IsPrimaryKey: true, Description: 'Salesforce record ID' },
107
- ],
108
- },
109
- {
110
- Name: 'Lead', DisplayName: 'Lead',
111
- Description: 'A prospective customer/lead in Salesforce CRM', SupportsWrite: true,
112
- Fields: [
113
- { Name: 'Email', DisplayName: 'Email', Type: 'string', IsRequired: false, IsReadOnly: false, IsPrimaryKey: false, Description: 'Lead email address' },
114
- { Name: 'FirstName', DisplayName: 'First Name', Type: 'string', IsRequired: false, IsReadOnly: false, IsPrimaryKey: false, Description: 'Lead first name' },
115
- { Name: 'LastName', DisplayName: 'Last Name', Type: 'string', IsRequired: true, IsReadOnly: false, IsPrimaryKey: false, Description: 'Lead last name' },
116
- { Name: 'Company', DisplayName: 'Company', Type: 'string', IsRequired: true, IsReadOnly: false, IsPrimaryKey: false, Description: 'Lead company name' },
117
- { Name: 'Phone', DisplayName: 'Phone', Type: 'string', IsRequired: false, IsReadOnly: false, IsPrimaryKey: false, Description: 'Lead phone number' },
118
- { Name: 'Title', DisplayName: 'Title', Type: 'string', IsRequired: false, IsReadOnly: false, IsPrimaryKey: false, Description: 'Lead title' },
119
- { Name: 'Status', DisplayName: 'Status', Type: 'string', IsRequired: false, IsReadOnly: false, IsPrimaryKey: false, Description: 'Lead status' },
120
- { Name: 'Id', DisplayName: 'Lead ID', Type: 'string', IsRequired: false, IsReadOnly: true, IsPrimaryKey: true, Description: 'Salesforce record ID' },
121
- ],
122
- },
123
- {
124
- Name: 'Opportunity', DisplayName: 'Opportunity',
125
- Description: 'A sales opportunity in Salesforce CRM', SupportsWrite: true,
126
- Fields: [
127
- { Name: 'Name', DisplayName: 'Opportunity Name', Type: 'string', IsRequired: true, IsReadOnly: false, IsPrimaryKey: false, Description: 'Opportunity name' },
128
- { Name: 'Amount', DisplayName: 'Amount', Type: 'number', IsRequired: false, IsReadOnly: false, IsPrimaryKey: false, Description: 'Opportunity amount' },
129
- { Name: 'StageName', DisplayName: 'Stage', Type: 'string', IsRequired: true, IsReadOnly: false, IsPrimaryKey: false, Description: 'Current stage' },
130
- { Name: 'CloseDate', DisplayName: 'Close Date', Type: 'date', IsRequired: true, IsReadOnly: false, IsPrimaryKey: false, Description: 'Expected close date' },
131
- { Name: 'AccountId', DisplayName: 'Account ID', Type: 'string', IsRequired: false, IsReadOnly: false, IsPrimaryKey: false, Description: 'Parent account ID' },
132
- { Name: 'Description', DisplayName: 'Description', Type: 'text', IsRequired: false, IsReadOnly: false, IsPrimaryKey: false, Description: 'Opportunity description' },
133
- { Name: 'Id', DisplayName: 'Opportunity ID', Type: 'string', IsRequired: false, IsReadOnly: true, IsPrimaryKey: true, Description: 'Salesforce record ID' },
134
- ],
135
- },
136
- {
137
- Name: 'Task', DisplayName: 'Task',
138
- Description: 'A task or to-do item in Salesforce', SupportsWrite: true,
139
- Fields: [
140
- { Name: 'Subject', DisplayName: 'Subject', Type: 'string', IsRequired: false, IsReadOnly: false, IsPrimaryKey: false, Description: 'Task subject' },
141
- { Name: 'Status', DisplayName: 'Status', Type: 'string', IsRequired: false, IsReadOnly: false, IsPrimaryKey: false, Description: 'Task status' },
142
- { Name: 'Priority', DisplayName: 'Priority', Type: 'string', IsRequired: false, IsReadOnly: false, IsPrimaryKey: false, Description: 'Task priority' },
143
- { Name: 'WhoId', DisplayName: 'Who ID', Type: 'string', IsRequired: false, IsReadOnly: false, IsPrimaryKey: false, Description: 'Related Contact or Lead ID' },
144
- { Name: 'WhatId', DisplayName: 'What ID', Type: 'string', IsRequired: false, IsReadOnly: false, IsPrimaryKey: false, Description: 'Related Account, Opportunity, etc. ID' },
145
- { Name: 'Id', DisplayName: 'Task ID', Type: 'string', IsRequired: false, IsReadOnly: true, IsPrimaryKey: true, Description: 'Salesforce record ID' },
146
- ],
147
- },
148
- {
149
- Name: 'Event', DisplayName: 'Event',
150
- Description: 'A calendar event in Salesforce', SupportsWrite: true,
151
- Fields: [
152
- { Name: 'Subject', DisplayName: 'Subject', Type: 'string', IsRequired: true, IsReadOnly: false, IsPrimaryKey: false, Description: 'Event subject' },
153
- { Name: 'StartDateTime', DisplayName: 'Start', Type: 'datetime', IsRequired: false, IsReadOnly: false, IsPrimaryKey: false, Description: 'Event start date/time' },
154
- { Name: 'EndDateTime', DisplayName: 'End', Type: 'datetime', IsRequired: false, IsReadOnly: false, IsPrimaryKey: false, Description: 'Event end date/time' },
155
- { Name: 'WhoId', DisplayName: 'Who ID', Type: 'string', IsRequired: false, IsReadOnly: false, IsPrimaryKey: false, Description: 'Related Contact or Lead ID' },
156
- { Name: 'Id', DisplayName: 'Event ID', Type: 'string', IsRequired: false, IsReadOnly: true, IsPrimaryKey: true, Description: 'Salesforce record ID' },
157
- ],
158
- },
159
- {
160
- Name: 'Case', DisplayName: 'Case',
161
- Description: 'A support case in Salesforce', SupportsWrite: true,
162
- Fields: [
163
- { Name: 'Subject', DisplayName: 'Subject', Type: 'string', IsRequired: false, IsReadOnly: false, IsPrimaryKey: false, Description: 'Case subject' },
164
- { Name: 'Status', DisplayName: 'Status', Type: 'string', IsRequired: false, IsReadOnly: false, IsPrimaryKey: false, Description: 'Case status' },
165
- { Name: 'Priority', DisplayName: 'Priority', Type: 'string', IsRequired: false, IsReadOnly: false, IsPrimaryKey: false, Description: 'Case priority' },
166
- { Name: 'AccountId', DisplayName: 'Account ID', Type: 'string', IsRequired: false, IsReadOnly: false, IsPrimaryKey: false, Description: 'Parent account ID' },
167
- { Name: 'ContactId', DisplayName: 'Contact ID', Type: 'string', IsRequired: false, IsReadOnly: false, IsPrimaryKey: false, Description: 'Related contact ID' },
168
- { Name: 'Description', DisplayName: 'Description', Type: 'text', IsRequired: false, IsReadOnly: false, IsPrimaryKey: false, Description: 'Case description' },
169
- { Name: 'Id', DisplayName: 'Case ID', Type: 'string', IsRequired: false, IsReadOnly: true, IsPrimaryKey: true, Description: 'Salesforce record ID' },
170
- ],
171
- },
172
- {
173
- Name: 'Campaign', DisplayName: 'Campaign',
174
- Description: 'A marketing campaign in Salesforce', SupportsWrite: true,
175
- Fields: [
176
- { Name: 'Name', DisplayName: 'Campaign Name', Type: 'string', IsRequired: true, IsReadOnly: false, IsPrimaryKey: false, Description: 'Campaign name' },
177
- { Name: 'Status', DisplayName: 'Status', Type: 'string', IsRequired: false, IsReadOnly: false, IsPrimaryKey: false, Description: 'Campaign status' },
178
- { Name: 'Type', DisplayName: 'Type', Type: 'string', IsRequired: false, IsReadOnly: false, IsPrimaryKey: false, Description: 'Campaign type' },
179
- { Name: 'StartDate', DisplayName: 'Start Date', Type: 'date', IsRequired: false, IsReadOnly: false, IsPrimaryKey: false, Description: 'Campaign start date' },
180
- { Name: 'EndDate', DisplayName: 'End Date', Type: 'date', IsRequired: false, IsReadOnly: false, IsPrimaryKey: false, Description: 'Campaign end date' },
181
- { Name: 'Id', DisplayName: 'Campaign ID', Type: 'string', IsRequired: false, IsReadOnly: true, IsPrimaryKey: true, Description: 'Salesforce record ID' },
182
- ],
183
- },
184
- {
185
- Name: 'User', DisplayName: 'User',
186
- Description: 'A Salesforce user (reference only, not writable)', SupportsWrite: false,
187
- IncludeInActionGeneration: false,
188
- Fields: [
189
- { Name: 'Username', DisplayName: 'Username', Type: 'string', IsRequired: false, IsReadOnly: true, IsPrimaryKey: false, Description: 'User login name' },
190
- { Name: 'Name', DisplayName: 'Full Name', Type: 'string', IsRequired: false, IsReadOnly: true, IsPrimaryKey: false, Description: 'Full name' },
191
- { Name: 'Email', DisplayName: 'Email', Type: 'string', IsRequired: false, IsReadOnly: true, IsPrimaryKey: false, Description: 'User email' },
192
- { Name: 'Id', DisplayName: 'User ID', Type: 'string', IsRequired: false, IsReadOnly: true, IsPrimaryKey: true, Description: 'Salesforce user ID' },
193
- ],
194
- },
195
- ];
196
67
  // ─── SalesforceConnector ──────────────────────────────────────────────
197
68
  /**
198
69
  * Production Salesforce CRM connector using the Salesforce REST API v61.0.
@@ -231,9 +102,58 @@ let SalesforceConnector = class SalesforceConnector extends BaseRESTIntegrationC
231
102
  get SupportsDelete() { return true; }
232
103
  get SupportsSearch() { return true; }
233
104
  get IntegrationName() { return 'Salesforce'; }
105
+ /**
106
+ * CORRECTION (IMPROVE build): affirm AUTHORITATIVE discovery. The global describe
107
+ * (`/sobjects/`) returns the COMPLETE credentialed gamut of queryable objects this
108
+ * org exposes (standard + custom `__c`), and `DoIntrospectSchema` describes that whole
109
+ * set. So an object/field absent from a comprehensive refresh genuinely means the
110
+ * source dropped it, and the engine's refresh path may safely DEACTIVATE it
111
+ * (`Status='Disabled'` — reversible; it flips back to Active if the object reappears on
112
+ * a later discovery). This is what makes comprehensive-refresh deactivation correct
113
+ * for Salesforce rather than wrongly wiping Declared metadata.
114
+ */
115
+ get DiscoveryIsAuthoritative() { return true; }
234
116
  // ─── Action Metadata ─────────────────────────────────────────────
117
+ /**
118
+ * Action-generation hint set. CORRECTION (IMPROVE build): this used to return
119
+ * a baked famous-subset catalog (~9 objects hardcoded in this file). That is a
120
+ * `catalog-in-code` defect — it froze the object universe to a famous subset.
121
+ *
122
+ * It now derives the hint set ENTIRELY from the runtime-cached IntegrationObject /
123
+ * IntegrationObjectField metadata for the Salesforce integration — the FULL
124
+ * Declared (credential-free catalog) + Discovered (live `DiscoverObjects` describe,
125
+ * custom `__c` included) gamut. There is NO baked catalog. If the metadata cache is
126
+ * not yet populated (e.g. action generation runs before the integration is seeded),
127
+ * it returns an empty array — the connector NEVER falls back to a hardcoded list.
128
+ *
129
+ * The per-tenant object UNIVERSE for sync comes exclusively from `DiscoverObjects`
130
+ * (live global describe / sObjects endpoint); this method only shapes the cached
131
+ * catalog into the ActionMetadataGenerator's hint structure.
132
+ */
235
133
  GetIntegrationObjects() {
236
- return SALESFORCE_OBJECTS;
134
+ const engine = IntegrationEngineBase.Instance;
135
+ const integration = engine.Integrations.find(i => i.Name === this.IntegrationName);
136
+ if (!integration)
137
+ return [];
138
+ const objects = engine.GetActiveIntegrationObjects(integration.ID);
139
+ return objects.map(obj => {
140
+ const fields = engine.GetIntegrationObjectFields(obj.ID);
141
+ return {
142
+ Name: obj.Name,
143
+ DisplayName: obj.DisplayName ?? obj.Name,
144
+ Description: obj.Description ?? undefined,
145
+ SupportsWrite: obj.SupportsWrite,
146
+ Fields: fields.map(f => ({
147
+ Name: f.Name,
148
+ DisplayName: f.DisplayName ?? f.Name,
149
+ Description: f.Description ?? undefined,
150
+ Type: f.Type ?? 'string',
151
+ IsRequired: f.IsRequired,
152
+ IsReadOnly: f.IsReadOnly,
153
+ IsPrimaryKey: f.IsPrimaryKey,
154
+ })),
155
+ };
156
+ });
237
157
  }
238
158
  GetActionGeneratorConfig() {
239
159
  const config = super.GetActionGeneratorConfig();
@@ -246,7 +166,7 @@ let SalesforceConnector = class SalesforceConnector extends BaseRESTIntegrationC
246
166
  async TestConnection(companyIntegration, contextUser) {
247
167
  try {
248
168
  const auth = await this.Authenticate(companyIntegration, contextUser);
249
- const url = `${auth.InstanceUrl}/services/data/v${auth.ApiVersion}/`;
169
+ const url = `${this.ApiBase(auth)}/services/data/v${auth.ApiVersion}/`;
250
170
  const headers = this.BuildHeaders(auth);
251
171
  const response = await this.MakeHTTPRequest(auth, url, 'GET', headers);
252
172
  if (response.Status >= 200 && response.Status < 300) {
@@ -276,7 +196,7 @@ let SalesforceConnector = class SalesforceConnector extends BaseRESTIntegrationC
276
196
  */
277
197
  async DiscoverObjects(companyIntegration, contextUser) {
278
198
  const auth = await this.Authenticate(companyIntegration, contextUser);
279
- const url = `${auth.InstanceUrl}/services/data/v${auth.ApiVersion}/sobjects/`;
199
+ const url = `${this.ApiBase(auth)}/services/data/v${auth.ApiVersion}/sobjects/`;
280
200
  const headers = this.BuildHeaders(auth);
281
201
  const response = await this.MakeHTTPRequest(auth, url, 'GET', headers);
282
202
  this.ValidateResponse(response, url);
@@ -365,7 +285,7 @@ let SalesforceConnector = class SalesforceConnector extends BaseRESTIntegrationC
365
285
  */
366
286
  async DiscoverFields(companyIntegration, objectName, contextUser) {
367
287
  const auth = await this.Authenticate(companyIntegration, contextUser);
368
- const url = `${auth.InstanceUrl}/services/data/v${auth.ApiVersion}/sobjects/${objectName}/describe`;
288
+ const url = `${this.ApiBase(auth)}/services/data/v${auth.ApiVersion}/sobjects/${objectName}/describe`;
369
289
  const headers = this.BuildHeaders(auth);
370
290
  const response = await this.MakeHTTPRequest(auth, url, 'GET', headers);
371
291
  this.ValidateResponse(response, url);
@@ -423,7 +343,11 @@ let SalesforceConnector = class SalesforceConnector extends BaseRESTIntegrationC
423
343
  const startMs = Date.now();
424
344
  const CONCURRENCY = 8;
425
345
  console.log(`[Salesforce] IntrospectSchema: describing ${total} queryable objects (parallel×${CONCURRENCY})...`);
426
- const result = { Objects: [] };
346
+ // CORRECTION (IMPROVE build): mark the FULL (unscoped) introspection authoritative so
347
+ // the comprehensive-refresh deactivation path may run. A SCOPED introspection
348
+ // (ObjectNames filter) describes only a subset and can NEVER prove absence, so it is
349
+ // never authoritative regardless of DiscoveryIsAuthoritative.
350
+ const result = { Objects: [], IsAuthoritative: this.DiscoveryIsAuthoritative && !wanted };
427
351
  let nextIdx = 0;
428
352
  let succeeded = 0;
429
353
  let skipped = 0;
@@ -540,7 +464,7 @@ let SalesforceConnector = class SalesforceConnector extends BaseRESTIntegrationC
540
464
  * Used by CRUD operations and field discovery.
541
465
  */
542
466
  SObjectBasePath(auth, family) {
543
- const base = `${auth.InstanceUrl}/services/data/v${auth.ApiVersion}`;
467
+ const base = `${this.ApiBase(auth)}/services/data/v${auth.ApiVersion}`;
544
468
  return family === 'tooling' ? `${base}/tooling/sobjects` : `${base}/sobjects`;
545
469
  }
546
470
  /**
@@ -548,7 +472,7 @@ let SalesforceConnector = class SalesforceConnector extends BaseRESTIntegrationC
548
472
  * Standard uses /query + /queryAll; Tooling uses /tooling/query.
549
473
  */
550
474
  SOQLEndpoint(auth, family, includeDeleted) {
551
- const base = `${auth.InstanceUrl}/services/data/v${auth.ApiVersion}`;
475
+ const base = `${this.ApiBase(auth)}/services/data/v${auth.ApiVersion}`;
552
476
  if (family === 'tooling')
553
477
  return `${base}/tooling/query`;
554
478
  return includeDeleted ? `${base}/queryAll` : `${base}/query`;
@@ -581,13 +505,22 @@ let SalesforceConnector = class SalesforceConnector extends BaseRESTIntegrationC
581
505
  const url = `${this.SObjectBasePath(auth, family)}/${ctx.ObjectName}/`;
582
506
  const body = this.StripReadOnlyFields(ctx.Attributes);
583
507
  const response = await this.MakeHTTPRequest(auth, url, 'POST', headers, body);
584
- if (response.Status === 201 || (response.Status >= 200 && response.Status < 300)) {
508
+ if (response.Status >= 200 && response.Status < 300) {
509
+ // SF SObject create returns { id, success, errors }. CORRECTION (IMPROVE build):
510
+ // route through BuildCreatedResult — a 2xx with success=false or an empty/absent id
511
+ // is a FAILURE (silently losing the record + duplicate create next sync), never
512
+ // a hand-constructed { Success:true, ExternalID:'' }.
585
513
  const created = response.Body;
586
- return {
587
- Success: true,
588
- ExternalID: created.id ?? '',
589
- StatusCode: response.Status,
590
- };
514
+ if (created.success === false) {
515
+ const detail = (created.errors ?? [])
516
+ .map(e => `${e.errorCode}: ${e.message}`).join('; ');
517
+ return {
518
+ Success: false,
519
+ StatusCode: response.Status,
520
+ ErrorMessage: `[Salesforce] Create of "${ctx.ObjectName}" returned HTTP ${response.Status} with success=false${detail ? `: ${detail}` : ''}`,
521
+ };
522
+ }
523
+ return this.BuildCreatedResult(created.id, response.Status, ctx.ObjectName);
591
524
  }
592
525
  return this.BuildCRUDError(response, 'CreateRecord', ctx.ObjectName);
593
526
  }
@@ -777,6 +710,7 @@ let SalesforceConnector = class SalesforceConnector extends BaseRESTIntegrationC
777
710
  InstanceUrl: tokenResponse.instance_url,
778
711
  ApiVersion: config.ApiVersion,
779
712
  Config: config,
713
+ CompanyIntegration: companyIntegration,
780
714
  };
781
715
  this.tokenObtainedAt = Date.now();
782
716
  console.log(`[Salesforce] Authenticated, instance: ${tokenResponse.instance_url}`);
@@ -793,24 +727,54 @@ let SalesforceConnector = class SalesforceConnector extends BaseRESTIntegrationC
793
727
  await this.CheckGovernorLimits();
794
728
  const maxRetries = this.effectiveMaxRetries;
795
729
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
796
- const response = await this.FetchWithTimeout(url, method, headers, body);
730
+ // Transient network / timeout resilience: a FetchWithTimeout throw (AbortSignal timeout,
731
+ // socket reset, DNS blip) is retried with backoff like any other transient failure, instead
732
+ // of failing the record on the very first attempt. Idempotent — it retries the SAME request.
733
+ let response;
734
+ try {
735
+ response = await this.FetchWithTimeout(url, method, headers, body);
736
+ }
737
+ catch (netErr) {
738
+ if (attempt < maxRetries) {
739
+ const delayMs = this.CalculateRetryDelay(attempt);
740
+ console.warn(`[Salesforce] Network/timeout error, retrying in ${delayMs}ms ` +
741
+ `(attempt ${attempt + 1}/${maxRetries})`);
742
+ await this.Sleep(delayMs);
743
+ continue;
744
+ }
745
+ throw netErr;
746
+ }
797
747
  this.lastRequestTime = Date.now();
798
748
  // Parse governor limits from response
799
749
  this.ParseGovernorLimits(response.headers);
800
- // Handle 401 — token expired, re-authenticate
750
+ // Handle 401 — token expired. Clear the cache so the NEXT operation re-authenticates.
751
+ // NOTE: inline re-auth+retry within this call is NOT done here — MakeHTTPRequest has no
752
+ // CompanyIntegration/contextUser to call Authenticate() with (that would need a base-signature
753
+ // change across all connectors). Cache-clear → next-op-recovers is the current contract.
801
754
  if (response.status === 401 && attempt === 0) {
802
- console.warn('[Salesforce] Token expired (401), re-authenticating...');
755
+ console.warn('[Salesforce] Token expired (401), re-authenticating on next operation...');
803
756
  this.cachedAuth = null;
804
- // Caller should re-authenticate; for now return the error
805
757
  }
806
- // Handle 429 — rate limited
758
+ // Handle 429 — rate limited. Honor the vendor's stated wait (Retry-After / Sforce-Limit-Info)
759
+ // when present; fall back to the AIMD-style exponential backoff otherwise.
807
760
  if (response.status === 429) {
808
- const delayMs = this.CalculateRetryDelay(attempt);
761
+ const delayMs = this.RetryAfterMs(response.headers) ?? this.CalculateRetryDelay(attempt);
809
762
  console.warn(`[Salesforce] Rate limited (429), retrying in ${delayMs}ms ` +
810
763
  `(attempt ${attempt + 1}/${maxRetries})`);
811
764
  await this.Sleep(delayMs);
812
765
  continue;
813
766
  }
767
+ // Handle 500/502/503/504 — transient server-side errors. Per the Salesforce/Fonteva error
768
+ // contract these are retry-if-safe (backoff). Only retried while attempts remain; otherwise
769
+ // the non-2xx response is returned to the caller for normal error handling.
770
+ if ((response.status === 500 || response.status === 502 || response.status === 503 || response.status === 504)
771
+ && attempt < maxRetries) {
772
+ const delayMs = this.RetryAfterMs(response.headers) ?? this.CalculateRetryDelay(attempt);
773
+ console.warn(`[Salesforce] Server error (${response.status}), retrying in ${delayMs}ms ` +
774
+ `(attempt ${attempt + 1}/${maxRetries})`);
775
+ await this.Sleep(delayMs);
776
+ continue;
777
+ }
814
778
  // Handle UNABLE_TO_LOCK_ROW — retry with backoff
815
779
  if (response.status === 400) {
816
780
  const responseBody = await this.SafeParseJSON(response);
@@ -836,6 +800,33 @@ let SalesforceConnector = class SalesforceConnector extends BaseRESTIntegrationC
836
800
  const body = rawBody;
837
801
  return (body.records ?? []);
838
802
  }
803
+ /**
804
+ * CORRECTION (IMPROVE build): per-record transform that strips Salesforce's
805
+ * `attributes` metadata blob (e.g. `{ type, url }`) from every record. This is a
806
+ * SANCTIONED removal declared in {@link ExcludedSourceKeys}, NOT a silent drop — the
807
+ * `attributes` key is vendor envelope noise, not customer data, so dropping it can
808
+ * never lose a custom column. Every OTHER source field flows through untouched, so
809
+ * the full-record pass-through contract (custom-column capture) is preserved.
810
+ *
811
+ * Used by both the base-fetch path (GetRecord via applyTransformPreservingKeys) and
812
+ * the SOQL override path (RawToExternalRecord routes through StripVendorAttributes,
813
+ * which reuses ExcludedSourceKeys so the two paths agree on what is removed).
814
+ */
815
+ TransformRecord(raw, _obj, _fields) {
816
+ if (!('attributes' in raw))
817
+ return raw; // identity fast-path — nothing to strip
818
+ const out = { ...raw };
819
+ delete out['attributes'];
820
+ return out;
821
+ }
822
+ /**
823
+ * The Salesforce `attributes` blob is the ONLY key any object's TransformRecord
824
+ * removes. Declaring it here makes the removal auditable and excludes it from the
825
+ * base's re-add-dropped-keys safety net (and from change-detection).
826
+ */
827
+ ExcludedSourceKeys(_objectName) {
828
+ return ['attributes'];
829
+ }
839
830
  ExtractPaginationInfo(rawBody, _paginationType, _currentPage, _currentOffset, _pageSize) {
840
831
  // Not used — we override FetchChanges entirely for SOQL
841
832
  const body = rawBody;
@@ -848,6 +839,15 @@ let SalesforceConnector = class SalesforceConnector extends BaseRESTIntegrationC
848
839
  const sfAuth = auth;
849
840
  return sfAuth.InstanceUrl;
850
841
  }
842
+ /**
843
+ * The API origin for all REST calls. Routes through GetBaseURL() so a test harness can
844
+ * redirect the connector to a mock server (the base class hooks GetBaseURL); in production
845
+ * this returns the authenticated InstanceUrl unchanged. ALL URL construction must go through
846
+ * this rather than reading auth.InstanceUrl directly, or the connector is not mock-testable.
847
+ */
848
+ ApiBase(auth) {
849
+ return this.GetBaseURL(auth.CompanyIntegration, auth);
850
+ }
851
851
  // ─── SOQL Query Engine ───────────────────────────────────────────
852
852
  /**
853
853
  * Builds a SOQL query string with optional watermark filter.
@@ -917,7 +917,7 @@ let SalesforceConnector = class SalesforceConnector extends BaseRESTIntegrationC
917
917
  */
918
918
  async FetchNextPage(auth, queryLocator) {
919
919
  // queryLocator is a relative URL like /services/data/v61.0/query/01gxx...
920
- const url = `${auth.InstanceUrl}${queryLocator}`;
920
+ const url = `${this.ApiBase(auth)}${queryLocator}`;
921
921
  const headers = this.BuildHeaders(auth);
922
922
  const response = await this.MakeHTTPRequest(auth, url, 'GET', headers);
923
923
  this.ValidateResponse(response, url);
@@ -1136,14 +1136,14 @@ let SalesforceConnector = class SalesforceConnector extends BaseRESTIntegrationC
1136
1136
  */
1137
1137
  async LoadCredentials(companyIntegration, contextUser) {
1138
1138
  // Try Credential entity first
1139
- const credentialID = companyIntegration.Get('CredentialID');
1139
+ const credentialID = companyIntegration.CredentialID;
1140
1140
  if (credentialID) {
1141
1141
  const creds = await this.LoadFromCredentialEntity(credentialID, contextUser);
1142
1142
  if (creds)
1143
1143
  return creds;
1144
1144
  }
1145
1145
  // Fallback: Configuration JSON
1146
- const configJson = companyIntegration.Get('Configuration');
1146
+ const configJson = companyIntegration.Configuration;
1147
1147
  if (configJson) {
1148
1148
  const creds = this.ParseCredentialJson(configJson);
1149
1149
  if (creds)
@@ -1216,7 +1216,7 @@ let SalesforceConnector = class SalesforceConnector extends BaseRESTIntegrationC
1216
1216
  ClientSecret: credentials.ClientSecret,
1217
1217
  TokenUrl: credentials.TokenUrl,
1218
1218
  };
1219
- const configJson = companyIntegration.Get('Configuration');
1219
+ const configJson = companyIntegration.Configuration;
1220
1220
  if (configJson) {
1221
1221
  this.ApplyConfigOverrides(config, configJson);
1222
1222
  }
@@ -1325,6 +1325,24 @@ let SalesforceConnector = class SalesforceConnector extends BaseRESTIntegrationC
1325
1325
  // Exponential backoff: 1s, 2s, 4s, 8s, 16s (capped at 30s)
1326
1326
  return Math.min(1000 * Math.pow(2, attempt), 30000);
1327
1327
  }
1328
+ /**
1329
+ * Parses the vendor's stated retry wait from a 429/503 response into milliseconds, so the connector
1330
+ * honors `Retry-After` (delta-seconds OR an HTTP-date) and Salesforce's `Sforce-Limit-Info` rather
1331
+ * than blindly using exponential backoff. Returns null when no usable signal is present (caller falls
1332
+ * back to {@link CalculateRetryDelay}). Capped at 60s so a hostile/garbage header can't stall a sync.
1333
+ */
1334
+ RetryAfterMs(headers) {
1335
+ const raw = headers?.get?.('retry-after');
1336
+ if (raw) {
1337
+ const secs = Number(raw);
1338
+ if (Number.isFinite(secs) && secs >= 0)
1339
+ return Math.min(secs * 1000, 60000);
1340
+ const when = Date.parse(raw); // HTTP-date form
1341
+ if (!Number.isNaN(when))
1342
+ return Math.min(Math.max(when - Date.now(), 0), 60000);
1343
+ }
1344
+ return null;
1345
+ }
1328
1346
  Sleep(ms) {
1329
1347
  return new Promise(resolve => setTimeout(resolve, ms));
1330
1348
  }
@@ -1475,14 +1493,18 @@ let SalesforceConnector = class SalesforceConnector extends BaseRESTIntegrationC
1475
1493
  }
1476
1494
  // ─── Record Conversion ───────────────────────────────────────────
1477
1495
  /**
1478
- * Converts a raw SF API record to an ExternalRecord.
1479
- * Strips the SF `attributes` metadata object from the fields.
1496
+ * Converts a raw SF API record to an ExternalRecord. The SOQL override path
1497
+ * hand-builds records, so it routes through the SAME sanctioned strip the base path
1498
+ * uses: every key flows through to `Fields` EXCEPT those declared in
1499
+ * {@link ExcludedSourceKeys} (just `attributes`). This preserves full-record
1500
+ * pass-through for custom-column capture while removing only the vendor envelope blob.
1480
1501
  */
1481
1502
  RawToExternalRecord(raw, objectType) {
1503
+ const excluded = new Set(this.ExcludedSourceKeys(objectType));
1482
1504
  const fields = {};
1483
1505
  for (const [key, value] of Object.entries(raw)) {
1484
- if (key === 'attributes')
1485
- continue; // SF metadata, not a data field
1506
+ if (excluded.has(key))
1507
+ continue; // sanctioned removal (SF metadata, not a data field)
1486
1508
  fields[key] = value;
1487
1509
  }
1488
1510
  return {
@@ -1511,7 +1533,7 @@ let SalesforceConnector = class SalesforceConnector extends BaseRESTIntegrationC
1511
1533
  // ─── Schema Building ─────────────────────────────────────────────
1512
1534
  async BuildSourceObjectFromDescribe(companyIntegration, contextUser, objectName) {
1513
1535
  const auth = await this.Authenticate(companyIntegration, contextUser);
1514
- const url = `${auth.InstanceUrl}/services/data/v${auth.ApiVersion}/sobjects/${objectName}/describe`;
1536
+ const url = `${this.ApiBase(auth)}/services/data/v${auth.ApiVersion}/sobjects/${objectName}/describe`;
1515
1537
  const headers = this.BuildHeaders(auth);
1516
1538
  const response = await this.MakeHTTPRequest(auth, url, 'GET', headers);
1517
1539
  this.ValidateResponse(response, url);
@@ -1607,7 +1629,7 @@ let SalesforceConnector = class SalesforceConnector extends BaseRESTIntegrationC
1607
1629
  */
1608
1630
  async FetchAnalyticsList(auth, family, objectName) {
1609
1631
  const segment = family === 'analytics_report' ? 'reports' : 'dashboards';
1610
- const url = `${auth.InstanceUrl}/services/data/v${auth.ApiVersion}/analytics/${segment}`;
1632
+ const url = `${this.ApiBase(auth)}/services/data/v${auth.ApiVersion}/analytics/${segment}`;
1611
1633
  const headers = this.BuildHeaders(auth);
1612
1634
  const response = await this.MakeHTTPRequest(auth, url, 'GET', headers);
1613
1635
  this.ValidateResponse(response, url);
@@ -1624,7 +1646,7 @@ let SalesforceConnector = class SalesforceConnector extends BaseRESTIntegrationC
1624
1646
  };
1625
1647
  }
1626
1648
  async GetAnalyticsReport(auth, id) {
1627
- const url = `${auth.InstanceUrl}/services/data/v${auth.ApiVersion}/analytics/reports/${id}`;
1649
+ const url = `${this.ApiBase(auth)}/services/data/v${auth.ApiVersion}/analytics/reports/${id}`;
1628
1650
  const headers = this.BuildHeaders(auth);
1629
1651
  const response = await this.MakeHTTPRequest(auth, url, 'GET', headers);
1630
1652
  if (response.Status === 404)
@@ -1637,7 +1659,7 @@ let SalesforceConnector = class SalesforceConnector extends BaseRESTIntegrationC
1637
1659
  };
1638
1660
  }
1639
1661
  async GetAnalyticsDashboard(auth, id) {
1640
- const url = `${auth.InstanceUrl}/services/data/v${auth.ApiVersion}/analytics/dashboards/${id}`;
1662
+ const url = `${this.ApiBase(auth)}/services/data/v${auth.ApiVersion}/analytics/dashboards/${id}`;
1641
1663
  const headers = this.BuildHeaders(auth);
1642
1664
  const response = await this.MakeHTTPRequest(auth, url, 'GET', headers);
1643
1665
  if (response.Status === 404)
@@ -1661,7 +1683,7 @@ let SalesforceConnector = class SalesforceConnector extends BaseRESTIntegrationC
1661
1683
  if (watermarkValue) {
1662
1684
  params.set('publishStatus', 'Online');
1663
1685
  }
1664
- const url = `${auth.InstanceUrl}/services/data/v${auth.ApiVersion}/support/knowledgeArticles?${params}`;
1686
+ const url = `${this.ApiBase(auth)}/services/data/v${auth.ApiVersion}/support/knowledgeArticles?${params}`;
1665
1687
  const headers = this.BuildHeaders(auth);
1666
1688
  const response = await this.MakeHTTPRequest(auth, url, 'GET', headers);
1667
1689
  this.ValidateResponse(response, url);
@@ -1692,7 +1714,7 @@ let SalesforceConnector = class SalesforceConnector extends BaseRESTIntegrationC
1692
1714
  */
1693
1715
  async FetchBulkJobs(auth, family, objectName) {
1694
1716
  const segment = family === 'bulk_ingest' ? 'ingest' : 'query';
1695
- const url = `${auth.InstanceUrl}/services/data/v${auth.ApiVersion}/jobs/${segment}`;
1717
+ const url = `${this.ApiBase(auth)}/services/data/v${auth.ApiVersion}/jobs/${segment}`;
1696
1718
  const headers = this.BuildHeaders(auth);
1697
1719
  const response = await this.MakeHTTPRequest(auth, url, 'GET', headers);
1698
1720
  this.ValidateResponse(response, url);
@@ -1719,7 +1741,7 @@ let SalesforceConnector = class SalesforceConnector extends BaseRESTIntegrationC
1719
1741
  */
1720
1742
  async CreateBulkJob(auth, family, attrs) {
1721
1743
  const segment = family === 'bulk_ingest' ? 'ingest' : 'query';
1722
- const url = `${auth.InstanceUrl}/services/data/v${auth.ApiVersion}/jobs/${segment}`;
1744
+ const url = `${this.ApiBase(auth)}/services/data/v${auth.ApiVersion}/jobs/${segment}`;
1723
1745
  const headers = { ...this.BuildHeaders(auth), 'Content-Type': 'application/json' };
1724
1746
  const body = this.StripReadOnlyFields(attrs);
1725
1747
  const response = await this.MakeHTTPRequest(auth, url, 'POST', headers, body);
@@ -1734,7 +1756,7 @@ let SalesforceConnector = class SalesforceConnector extends BaseRESTIntegrationC
1734
1756
  */
1735
1757
  async AbortBulkJob(auth, family, jobId) {
1736
1758
  const segment = family === 'bulk_ingest' ? 'ingest' : 'query';
1737
- const url = `${auth.InstanceUrl}/services/data/v${auth.ApiVersion}/jobs/${segment}/${jobId}`;
1759
+ const url = `${this.ApiBase(auth)}/services/data/v${auth.ApiVersion}/jobs/${segment}/${jobId}`;
1738
1760
  const headers = { ...this.BuildHeaders(auth), 'Content-Type': 'application/json' };
1739
1761
  const response = await this.MakeHTTPRequest(auth, url, 'PATCH', headers, { state: 'Aborted' });
1740
1762
  if (response.Status >= 200 && response.Status < 300) {
@@ -1744,7 +1766,7 @@ let SalesforceConnector = class SalesforceConnector extends BaseRESTIntegrationC
1744
1766
  }
1745
1767
  async GetBulkJob(auth, family, jobId) {
1746
1768
  const segment = family === 'bulk_ingest' ? 'ingest' : 'query';
1747
- const url = `${auth.InstanceUrl}/services/data/v${auth.ApiVersion}/jobs/${segment}/${jobId}`;
1769
+ const url = `${this.ApiBase(auth)}/services/data/v${auth.ApiVersion}/jobs/${segment}/${jobId}`;
1748
1770
  const headers = this.BuildHeaders(auth);
1749
1771
  const response = await this.MakeHTTPRequest(auth, url, 'GET', headers);
1750
1772
  if (response.Status === 404)
@@ -1772,7 +1794,7 @@ let SalesforceConnector = class SalesforceConnector extends BaseRESTIntegrationC
1772
1794
  * 4xx/5xx, summarizing the sub-request errors so the caller sees them.
1773
1795
  */
1774
1796
  async ExecuteCompositeRequest(auth, attrs) {
1775
- const url = `${auth.InstanceUrl}/services/data/v${auth.ApiVersion}/composite`;
1797
+ const url = `${this.ApiBase(auth)}/services/data/v${auth.ApiVersion}/composite`;
1776
1798
  const headers = { ...this.BuildHeaders(auth), 'Content-Type': 'application/json' };
1777
1799
  // Either pass the whole payload through or wrap a bare array
1778
1800
  const body = Array.isArray(attrs.compositeRequest)