lakesync 0.1.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.
- package/README.md +74 -0
- package/dist/adapter.d.ts +369 -0
- package/dist/adapter.js +39 -0
- package/dist/adapter.js.map +1 -0
- package/dist/analyst.d.ts +268 -0
- package/dist/analyst.js +495 -0
- package/dist/analyst.js.map +1 -0
- package/dist/auth-CAVutXzx.d.ts +30 -0
- package/dist/base-poller-Qo_SmCZs.d.ts +82 -0
- package/dist/catalogue.d.ts +65 -0
- package/dist/catalogue.js +17 -0
- package/dist/catalogue.js.map +1 -0
- package/dist/chunk-4ARO6KTJ.js +257 -0
- package/dist/chunk-4ARO6KTJ.js.map +1 -0
- package/dist/chunk-5YOFCJQ7.js +1115 -0
- package/dist/chunk-5YOFCJQ7.js.map +1 -0
- package/dist/chunk-7D4SUZUM.js +38 -0
- package/dist/chunk-7D4SUZUM.js.map +1 -0
- package/dist/chunk-BNJOGBYK.js +335 -0
- package/dist/chunk-BNJOGBYK.js.map +1 -0
- package/dist/chunk-ICNT7I3K.js +1180 -0
- package/dist/chunk-ICNT7I3K.js.map +1 -0
- package/dist/chunk-P5DRFKIT.js +413 -0
- package/dist/chunk-P5DRFKIT.js.map +1 -0
- package/dist/chunk-X3RO5SYJ.js +880 -0
- package/dist/chunk-X3RO5SYJ.js.map +1 -0
- package/dist/client.d.ts +428 -0
- package/dist/client.js +2048 -0
- package/dist/client.js.map +1 -0
- package/dist/compactor.d.ts +342 -0
- package/dist/compactor.js +793 -0
- package/dist/compactor.js.map +1 -0
- package/dist/coordinator-CxckTzYW.d.ts +396 -0
- package/dist/db-types-BR6Kt4uf.d.ts +29 -0
- package/dist/gateway-D5SaaMvT.d.ts +337 -0
- package/dist/gateway-server.d.ts +306 -0
- package/dist/gateway-server.js +4663 -0
- package/dist/gateway-server.js.map +1 -0
- package/dist/gateway.d.ts +196 -0
- package/dist/gateway.js +79 -0
- package/dist/gateway.js.map +1 -0
- package/dist/hlc-DiD8QNG3.d.ts +70 -0
- package/dist/index.d.ts +245 -0
- package/dist/index.js +102 -0
- package/dist/index.js.map +1 -0
- package/dist/json-dYtqiL0F.d.ts +18 -0
- package/dist/nessie-client-DrNikVXy.d.ts +160 -0
- package/dist/parquet.d.ts +78 -0
- package/dist/parquet.js +15 -0
- package/dist/parquet.js.map +1 -0
- package/dist/proto.d.ts +434 -0
- package/dist/proto.js +67 -0
- package/dist/proto.js.map +1 -0
- package/dist/react.d.ts +147 -0
- package/dist/react.js +224 -0
- package/dist/react.js.map +1 -0
- package/dist/resolver-C3Wphi6O.d.ts +10 -0
- package/dist/result-CojzlFE2.d.ts +64 -0
- package/dist/src-QU2YLPZY.js +383 -0
- package/dist/src-QU2YLPZY.js.map +1 -0
- package/dist/src-WYBF5LOI.js +102 -0
- package/dist/src-WYBF5LOI.js.map +1 -0
- package/dist/src-WZNPHANQ.js +426 -0
- package/dist/src-WZNPHANQ.js.map +1 -0
- package/dist/types-Bs-QyOe-.d.ts +143 -0
- package/dist/types-DAQL_vU_.d.ts +118 -0
- package/dist/types-DSC_EiwR.d.ts +45 -0
- package/dist/types-V_jVu2sA.d.ts +73 -0
- package/package.json +119 -0
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BaseSourcePoller,
|
|
3
|
+
Err,
|
|
4
|
+
LakeSyncError,
|
|
5
|
+
Ok,
|
|
6
|
+
extractDelta
|
|
7
|
+
} from "./chunk-ICNT7I3K.js";
|
|
8
|
+
import "./chunk-7D4SUZUM.js";
|
|
9
|
+
|
|
10
|
+
// ../connector-salesforce/src/errors.ts
|
|
11
|
+
var SalesforceApiError = class extends LakeSyncError {
|
|
12
|
+
/** HTTP status code returned by Salesforce. */
|
|
13
|
+
statusCode;
|
|
14
|
+
/** Raw response body from Salesforce. */
|
|
15
|
+
responseBody;
|
|
16
|
+
constructor(statusCode, responseBody, cause) {
|
|
17
|
+
super(`Salesforce API error (${statusCode}): ${responseBody}`, "SALESFORCE_API_ERROR", cause);
|
|
18
|
+
this.statusCode = statusCode;
|
|
19
|
+
this.responseBody = responseBody;
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
var SalesforceAuthError = class extends LakeSyncError {
|
|
23
|
+
constructor(message, cause) {
|
|
24
|
+
super(message, "SALESFORCE_AUTH_ERROR", cause);
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// ../connector-salesforce/src/client.ts
|
|
29
|
+
var DEFAULT_API_VERSION = "v62.0";
|
|
30
|
+
var MAX_RETRY_ATTEMPTS = 3;
|
|
31
|
+
var DEFAULT_RETRY_AFTER_MS = 1e4;
|
|
32
|
+
var SalesforceClient = class {
|
|
33
|
+
config;
|
|
34
|
+
apiVersion;
|
|
35
|
+
loginUrl;
|
|
36
|
+
accessToken = null;
|
|
37
|
+
instanceUrl;
|
|
38
|
+
constructor(config) {
|
|
39
|
+
this.config = config;
|
|
40
|
+
this.apiVersion = config.apiVersion ?? DEFAULT_API_VERSION;
|
|
41
|
+
this.loginUrl = config.isSandbox ? "https://test.salesforce.com" : "https://login.salesforce.com";
|
|
42
|
+
this.instanceUrl = config.instanceUrl;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Authenticate via OAuth 2.0 Username-Password flow.
|
|
46
|
+
*
|
|
47
|
+
* Stores access token and updates instance URL from the response.
|
|
48
|
+
*/
|
|
49
|
+
async authenticate() {
|
|
50
|
+
const body = new URLSearchParams({
|
|
51
|
+
grant_type: "password",
|
|
52
|
+
client_id: this.config.clientId,
|
|
53
|
+
client_secret: this.config.clientSecret,
|
|
54
|
+
username: this.config.username,
|
|
55
|
+
password: this.config.password
|
|
56
|
+
});
|
|
57
|
+
let response;
|
|
58
|
+
try {
|
|
59
|
+
response = await fetch(`${this.loginUrl}/services/oauth2/token`, {
|
|
60
|
+
method: "POST",
|
|
61
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
62
|
+
body: body.toString()
|
|
63
|
+
});
|
|
64
|
+
} catch (err) {
|
|
65
|
+
return Err(
|
|
66
|
+
new SalesforceAuthError(
|
|
67
|
+
`Failed to connect to Salesforce auth endpoint: ${err instanceof Error ? err.message : String(err)}`,
|
|
68
|
+
err instanceof Error ? err : void 0
|
|
69
|
+
)
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
if (!response.ok) {
|
|
73
|
+
const text = await response.text();
|
|
74
|
+
return Err(
|
|
75
|
+
new SalesforceAuthError(`Salesforce authentication failed (${response.status}): ${text}`)
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
const data = await response.json();
|
|
79
|
+
this.accessToken = data.access_token;
|
|
80
|
+
this.instanceUrl = data.instance_url;
|
|
81
|
+
return Ok(void 0);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Execute a SOQL query with auto-pagination.
|
|
85
|
+
*
|
|
86
|
+
* Automatically authenticates on first call and re-authenticates on 401.
|
|
87
|
+
*/
|
|
88
|
+
async query(soql) {
|
|
89
|
+
if (!this.accessToken) {
|
|
90
|
+
const authResult = await this.authenticate();
|
|
91
|
+
if (!authResult.ok) return authResult;
|
|
92
|
+
}
|
|
93
|
+
const allRecords = [];
|
|
94
|
+
let url = `${this.instanceUrl}/services/data/${this.apiVersion}/query?q=${encodeURIComponent(soql)}`;
|
|
95
|
+
while (true) {
|
|
96
|
+
const result = await this.request(url);
|
|
97
|
+
if (!result.ok && result.error instanceof SalesforceApiError && result.error.statusCode === 401) {
|
|
98
|
+
const authResult = await this.authenticate();
|
|
99
|
+
if (!authResult.ok) return authResult;
|
|
100
|
+
const retryResult = await this.request(url);
|
|
101
|
+
if (!retryResult.ok) return retryResult;
|
|
102
|
+
for (const record of retryResult.value.records) {
|
|
103
|
+
allRecords.push(record);
|
|
104
|
+
}
|
|
105
|
+
if (retryResult.value.done || !retryResult.value.nextRecordsUrl) break;
|
|
106
|
+
url = `${this.instanceUrl}${retryResult.value.nextRecordsUrl}`;
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
if (!result.ok) return result;
|
|
110
|
+
for (const record of result.value.records) {
|
|
111
|
+
allRecords.push(record);
|
|
112
|
+
}
|
|
113
|
+
if (result.value.done || !result.value.nextRecordsUrl) break;
|
|
114
|
+
url = `${this.instanceUrl}${result.value.nextRecordsUrl}`;
|
|
115
|
+
}
|
|
116
|
+
return Ok(allRecords);
|
|
117
|
+
}
|
|
118
|
+
// -----------------------------------------------------------------------
|
|
119
|
+
// Internal HTTP helpers
|
|
120
|
+
// -----------------------------------------------------------------------
|
|
121
|
+
/** Make an HTTP request with rate-limit retry logic. */
|
|
122
|
+
async request(url) {
|
|
123
|
+
for (let attempt = 0; attempt <= MAX_RETRY_ATTEMPTS; attempt++) {
|
|
124
|
+
const headers = {
|
|
125
|
+
Authorization: `Bearer ${this.accessToken}`,
|
|
126
|
+
Accept: "application/json"
|
|
127
|
+
};
|
|
128
|
+
const response = await fetch(url, { method: "GET", headers });
|
|
129
|
+
if (response.ok) {
|
|
130
|
+
const data = await response.json();
|
|
131
|
+
return Ok(data);
|
|
132
|
+
}
|
|
133
|
+
if (response.status === 503) {
|
|
134
|
+
const retryAfter = response.headers.get("Retry-After");
|
|
135
|
+
const waitMs = retryAfter ? Number.parseInt(retryAfter, 10) * 1e3 : DEFAULT_RETRY_AFTER_MS;
|
|
136
|
+
if (attempt < MAX_RETRY_ATTEMPTS) {
|
|
137
|
+
await sleep(waitMs);
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
const responseBody2 = await response.text();
|
|
141
|
+
return Err(new SalesforceApiError(503, responseBody2));
|
|
142
|
+
}
|
|
143
|
+
const responseBody = await response.text();
|
|
144
|
+
return Err(new SalesforceApiError(response.status, responseBody));
|
|
145
|
+
}
|
|
146
|
+
return Err(new SalesforceApiError(0, "Unknown error after retries"));
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
function sleep(ms) {
|
|
150
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// ../connector-salesforce/src/mapping.ts
|
|
154
|
+
function mapAccount(account) {
|
|
155
|
+
return {
|
|
156
|
+
rowId: account.Id,
|
|
157
|
+
row: {
|
|
158
|
+
sf_id: account.Id,
|
|
159
|
+
name: account.Name ?? null,
|
|
160
|
+
type: account.Type ?? null,
|
|
161
|
+
industry: account.Industry ?? null,
|
|
162
|
+
website: account.Website ?? null,
|
|
163
|
+
phone: account.Phone ?? null,
|
|
164
|
+
billing_city: account.BillingCity ?? null,
|
|
165
|
+
billing_state: account.BillingState ?? null,
|
|
166
|
+
billing_country: account.BillingCountry ?? null,
|
|
167
|
+
annual_revenue: account.AnnualRevenue ?? null,
|
|
168
|
+
number_of_employees: account.NumberOfEmployees ?? null,
|
|
169
|
+
owner_name: account.Owner?.Name ?? null,
|
|
170
|
+
created_date: account.CreatedDate ?? null,
|
|
171
|
+
last_modified_date: account.LastModifiedDate ?? null
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
function mapContact(contact) {
|
|
176
|
+
return {
|
|
177
|
+
rowId: contact.Id,
|
|
178
|
+
row: {
|
|
179
|
+
sf_id: contact.Id,
|
|
180
|
+
first_name: contact.FirstName ?? null,
|
|
181
|
+
last_name: contact.LastName ?? null,
|
|
182
|
+
email: contact.Email ?? null,
|
|
183
|
+
phone: contact.Phone ?? null,
|
|
184
|
+
title: contact.Title ?? null,
|
|
185
|
+
account_id: contact.AccountId ?? null,
|
|
186
|
+
account_name: contact.Account?.Name ?? null,
|
|
187
|
+
mailing_city: contact.MailingCity ?? null,
|
|
188
|
+
mailing_state: contact.MailingState ?? null,
|
|
189
|
+
mailing_country: contact.MailingCountry ?? null,
|
|
190
|
+
owner_name: contact.Owner?.Name ?? null,
|
|
191
|
+
created_date: contact.CreatedDate ?? null,
|
|
192
|
+
last_modified_date: contact.LastModifiedDate ?? null
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
function mapOpportunity(opportunity) {
|
|
197
|
+
return {
|
|
198
|
+
rowId: opportunity.Id,
|
|
199
|
+
row: {
|
|
200
|
+
sf_id: opportunity.Id,
|
|
201
|
+
name: opportunity.Name ?? null,
|
|
202
|
+
stage_name: opportunity.StageName ?? null,
|
|
203
|
+
amount: opportunity.Amount ?? null,
|
|
204
|
+
close_date: opportunity.CloseDate ?? null,
|
|
205
|
+
probability: opportunity.Probability ?? null,
|
|
206
|
+
account_id: opportunity.AccountId ?? null,
|
|
207
|
+
account_name: opportunity.Account?.Name ?? null,
|
|
208
|
+
type: opportunity.Type ?? null,
|
|
209
|
+
lead_source: opportunity.LeadSource ?? null,
|
|
210
|
+
is_closed: opportunity.IsClosed ?? null,
|
|
211
|
+
is_won: opportunity.IsWon ?? null,
|
|
212
|
+
owner_name: opportunity.Owner?.Name ?? null,
|
|
213
|
+
created_date: opportunity.CreatedDate ?? null,
|
|
214
|
+
last_modified_date: opportunity.LastModifiedDate ?? null
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
function mapLead(lead) {
|
|
219
|
+
return {
|
|
220
|
+
rowId: lead.Id,
|
|
221
|
+
row: {
|
|
222
|
+
sf_id: lead.Id,
|
|
223
|
+
first_name: lead.FirstName ?? null,
|
|
224
|
+
last_name: lead.LastName ?? null,
|
|
225
|
+
company: lead.Company ?? null,
|
|
226
|
+
email: lead.Email ?? null,
|
|
227
|
+
phone: lead.Phone ?? null,
|
|
228
|
+
title: lead.Title ?? null,
|
|
229
|
+
status: lead.Status ?? null,
|
|
230
|
+
lead_source: lead.LeadSource ?? null,
|
|
231
|
+
is_converted: lead.IsConverted ?? null,
|
|
232
|
+
converted_account_id: lead.ConvertedAccountId ?? null,
|
|
233
|
+
converted_contact_id: lead.ConvertedContactId ?? null,
|
|
234
|
+
converted_opportunity_id: lead.ConvertedOpportunityId ?? null,
|
|
235
|
+
owner_name: lead.Owner?.Name ?? null,
|
|
236
|
+
created_date: lead.CreatedDate ?? null,
|
|
237
|
+
last_modified_date: lead.LastModifiedDate ?? null
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// ../connector-salesforce/src/poller.ts
|
|
243
|
+
var DEFAULT_INTERVAL_MS = 3e4;
|
|
244
|
+
var ACCOUNT_FIELDS = [
|
|
245
|
+
"Id",
|
|
246
|
+
"Name",
|
|
247
|
+
"Type",
|
|
248
|
+
"Industry",
|
|
249
|
+
"Website",
|
|
250
|
+
"Phone",
|
|
251
|
+
"BillingCity",
|
|
252
|
+
"BillingState",
|
|
253
|
+
"BillingCountry",
|
|
254
|
+
"AnnualRevenue",
|
|
255
|
+
"NumberOfEmployees",
|
|
256
|
+
"Owner.Name",
|
|
257
|
+
"CreatedDate",
|
|
258
|
+
"LastModifiedDate"
|
|
259
|
+
].join(", ");
|
|
260
|
+
var CONTACT_FIELDS = [
|
|
261
|
+
"Id",
|
|
262
|
+
"FirstName",
|
|
263
|
+
"LastName",
|
|
264
|
+
"Email",
|
|
265
|
+
"Phone",
|
|
266
|
+
"Title",
|
|
267
|
+
"AccountId",
|
|
268
|
+
"Account.Name",
|
|
269
|
+
"MailingCity",
|
|
270
|
+
"MailingState",
|
|
271
|
+
"MailingCountry",
|
|
272
|
+
"Owner.Name",
|
|
273
|
+
"CreatedDate",
|
|
274
|
+
"LastModifiedDate"
|
|
275
|
+
].join(", ");
|
|
276
|
+
var OPPORTUNITY_FIELDS = [
|
|
277
|
+
"Id",
|
|
278
|
+
"Name",
|
|
279
|
+
"StageName",
|
|
280
|
+
"Amount",
|
|
281
|
+
"CloseDate",
|
|
282
|
+
"Probability",
|
|
283
|
+
"AccountId",
|
|
284
|
+
"Account.Name",
|
|
285
|
+
"Type",
|
|
286
|
+
"LeadSource",
|
|
287
|
+
"IsClosed",
|
|
288
|
+
"IsWon",
|
|
289
|
+
"Owner.Name",
|
|
290
|
+
"CreatedDate",
|
|
291
|
+
"LastModifiedDate"
|
|
292
|
+
].join(", ");
|
|
293
|
+
var LEAD_FIELDS = [
|
|
294
|
+
"Id",
|
|
295
|
+
"FirstName",
|
|
296
|
+
"LastName",
|
|
297
|
+
"Company",
|
|
298
|
+
"Email",
|
|
299
|
+
"Phone",
|
|
300
|
+
"Title",
|
|
301
|
+
"Status",
|
|
302
|
+
"LeadSource",
|
|
303
|
+
"IsConverted",
|
|
304
|
+
"ConvertedAccountId",
|
|
305
|
+
"ConvertedContactId",
|
|
306
|
+
"ConvertedOpportunityId",
|
|
307
|
+
"Owner.Name",
|
|
308
|
+
"CreatedDate",
|
|
309
|
+
"LastModifiedDate"
|
|
310
|
+
].join(", ");
|
|
311
|
+
var SalesforceSourcePoller = class extends BaseSourcePoller {
|
|
312
|
+
connectionConfig;
|
|
313
|
+
client;
|
|
314
|
+
/** Per-entity cursors: max LastModifiedDate from the last poll. */
|
|
315
|
+
cursors = {
|
|
316
|
+
accounts: void 0,
|
|
317
|
+
contacts: void 0,
|
|
318
|
+
opportunities: void 0,
|
|
319
|
+
leads: void 0
|
|
320
|
+
};
|
|
321
|
+
constructor(connectionConfig, ingestConfig, name, gateway, client) {
|
|
322
|
+
super({
|
|
323
|
+
name,
|
|
324
|
+
intervalMs: ingestConfig?.intervalMs ?? DEFAULT_INTERVAL_MS,
|
|
325
|
+
gateway,
|
|
326
|
+
memory: {
|
|
327
|
+
chunkSize: ingestConfig?.chunkSize,
|
|
328
|
+
memoryBudgetBytes: ingestConfig?.memoryBudgetBytes
|
|
329
|
+
}
|
|
330
|
+
});
|
|
331
|
+
this.connectionConfig = connectionConfig;
|
|
332
|
+
this.client = client ?? new SalesforceClient(connectionConfig);
|
|
333
|
+
}
|
|
334
|
+
/** Execute a single poll cycle across all enabled entity types. */
|
|
335
|
+
async poll() {
|
|
336
|
+
const includeAccounts = this.connectionConfig.includeAccounts ?? true;
|
|
337
|
+
if (includeAccounts) {
|
|
338
|
+
await this.pollEntity(
|
|
339
|
+
"Account",
|
|
340
|
+
ACCOUNT_FIELDS,
|
|
341
|
+
"accounts",
|
|
342
|
+
"sf_accounts",
|
|
343
|
+
mapAccount
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
const includeContacts = this.connectionConfig.includeContacts ?? true;
|
|
347
|
+
if (includeContacts) {
|
|
348
|
+
await this.pollEntity(
|
|
349
|
+
"Contact",
|
|
350
|
+
CONTACT_FIELDS,
|
|
351
|
+
"contacts",
|
|
352
|
+
"sf_contacts",
|
|
353
|
+
mapContact
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
const includeOpportunities = this.connectionConfig.includeOpportunities ?? true;
|
|
357
|
+
if (includeOpportunities) {
|
|
358
|
+
await this.pollEntity(
|
|
359
|
+
"Opportunity",
|
|
360
|
+
OPPORTUNITY_FIELDS,
|
|
361
|
+
"opportunities",
|
|
362
|
+
"sf_opportunities",
|
|
363
|
+
mapOpportunity
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
const includeLeads = this.connectionConfig.includeLeads ?? true;
|
|
367
|
+
if (includeLeads) {
|
|
368
|
+
await this.pollEntity("Lead", LEAD_FIELDS, "leads", "sf_leads", mapLead);
|
|
369
|
+
}
|
|
370
|
+
await this.flushAccumulator();
|
|
371
|
+
}
|
|
372
|
+
// -----------------------------------------------------------------------
|
|
373
|
+
// Generic entity polling via LastModifiedDate cursor
|
|
374
|
+
// -----------------------------------------------------------------------
|
|
375
|
+
async pollEntity(sObjectType, fields, cursorKey, table, mapFn) {
|
|
376
|
+
const cursor = this.cursors[cursorKey];
|
|
377
|
+
const soql = this.buildSoql(sObjectType, fields, cursor);
|
|
378
|
+
const result = await this.client.query(soql);
|
|
379
|
+
if (!result.ok) return;
|
|
380
|
+
const records = result.value;
|
|
381
|
+
if (records.length === 0) return;
|
|
382
|
+
let maxLastModified = cursor;
|
|
383
|
+
for (const record of records) {
|
|
384
|
+
const { rowId, row } = mapFn(record);
|
|
385
|
+
const delta = await extractDelta(null, row, {
|
|
386
|
+
table,
|
|
387
|
+
rowId,
|
|
388
|
+
clientId: this.clientId,
|
|
389
|
+
hlc: this.hlc.now()
|
|
390
|
+
});
|
|
391
|
+
if (delta) {
|
|
392
|
+
await this.accumulateDelta(delta);
|
|
393
|
+
}
|
|
394
|
+
const lastModified = record.LastModifiedDate;
|
|
395
|
+
if (lastModified && (!maxLastModified || lastModified > maxLastModified)) {
|
|
396
|
+
maxLastModified = lastModified;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
this.cursors[cursorKey] = maxLastModified;
|
|
400
|
+
}
|
|
401
|
+
// -----------------------------------------------------------------------
|
|
402
|
+
// SOQL query builder
|
|
403
|
+
// -----------------------------------------------------------------------
|
|
404
|
+
buildSoql(sObjectType, fields, cursor) {
|
|
405
|
+
const clauses = [];
|
|
406
|
+
if (cursor) {
|
|
407
|
+
clauses.push(`LastModifiedDate > ${cursor}`);
|
|
408
|
+
}
|
|
409
|
+
if (this.connectionConfig.soqlFilter) {
|
|
410
|
+
clauses.push(this.connectionConfig.soqlFilter);
|
|
411
|
+
}
|
|
412
|
+
const where = clauses.length > 0 ? ` WHERE ${clauses.join(" AND ")}` : "";
|
|
413
|
+
return `SELECT ${fields} FROM ${sObjectType}${where} ORDER BY LastModifiedDate ASC`;
|
|
414
|
+
}
|
|
415
|
+
};
|
|
416
|
+
export {
|
|
417
|
+
SalesforceApiError,
|
|
418
|
+
SalesforceAuthError,
|
|
419
|
+
SalesforceClient,
|
|
420
|
+
SalesforceSourcePoller,
|
|
421
|
+
mapAccount,
|
|
422
|
+
mapContact,
|
|
423
|
+
mapLead,
|
|
424
|
+
mapOpportunity
|
|
425
|
+
};
|
|
426
|
+
//# sourceMappingURL=src-WZNPHANQ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../connector-salesforce/src/errors.ts","../../connector-salesforce/src/client.ts","../../connector-salesforce/src/mapping.ts","../../connector-salesforce/src/poller.ts"],"sourcesContent":["import { LakeSyncError } from \"@lakesync/core\";\n\n/** HTTP error from the Salesforce REST API. */\nexport class SalesforceApiError extends LakeSyncError {\n\t/** HTTP status code returned by Salesforce. */\n\treadonly statusCode: number;\n\t/** Raw response body from Salesforce. */\n\treadonly responseBody: string;\n\n\tconstructor(statusCode: number, responseBody: string, cause?: Error) {\n\t\tsuper(`Salesforce API error (${statusCode}): ${responseBody}`, \"SALESFORCE_API_ERROR\", cause);\n\t\tthis.statusCode = statusCode;\n\t\tthis.responseBody = responseBody;\n\t}\n}\n\n/** Authentication failure from the Salesforce OAuth token endpoint. */\nexport class SalesforceAuthError extends LakeSyncError {\n\tconstructor(message: string, cause?: Error) {\n\t\tsuper(message, \"SALESFORCE_AUTH_ERROR\", cause);\n\t}\n}\n","// ---------------------------------------------------------------------------\n// SalesforceClient — HTTP wrapper for Salesforce REST API\n// ---------------------------------------------------------------------------\n\nimport { Err, Ok, type Result } from \"@lakesync/core\";\nimport { SalesforceApiError, SalesforceAuthError } from \"./errors\";\nimport type {\n\tSalesforceAuthResponse,\n\tSalesforceConnectorConfig,\n\tSalesforceQueryResponse,\n} from \"./types\";\n\nconst DEFAULT_API_VERSION = \"v62.0\";\nconst MAX_RETRY_ATTEMPTS = 3;\nconst DEFAULT_RETRY_AFTER_MS = 10_000;\n\n/**\n * HTTP client for the Salesforce REST API.\n *\n * Uses OAuth 2.0 Username-Password flow for authentication and global `fetch`.\n * All public methods return `Result<T, SalesforceApiError | SalesforceAuthError>`.\n */\nexport class SalesforceClient {\n\tprivate readonly config: SalesforceConnectorConfig;\n\tprivate readonly apiVersion: string;\n\tprivate readonly loginUrl: string;\n\n\tprivate accessToken: string | null = null;\n\tprivate instanceUrl: string;\n\n\tconstructor(config: SalesforceConnectorConfig) {\n\t\tthis.config = config;\n\t\tthis.apiVersion = config.apiVersion ?? DEFAULT_API_VERSION;\n\t\tthis.loginUrl = config.isSandbox\n\t\t\t? \"https://test.salesforce.com\"\n\t\t\t: \"https://login.salesforce.com\";\n\t\tthis.instanceUrl = config.instanceUrl;\n\t}\n\n\t/**\n\t * Authenticate via OAuth 2.0 Username-Password flow.\n\t *\n\t * Stores access token and updates instance URL from the response.\n\t */\n\tasync authenticate(): Promise<Result<void, SalesforceAuthError>> {\n\t\tconst body = new URLSearchParams({\n\t\t\tgrant_type: \"password\",\n\t\t\tclient_id: this.config.clientId,\n\t\t\tclient_secret: this.config.clientSecret,\n\t\t\tusername: this.config.username,\n\t\t\tpassword: this.config.password,\n\t\t});\n\n\t\tlet response: Response;\n\t\ttry {\n\t\t\tresponse = await fetch(`${this.loginUrl}/services/oauth2/token`, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n\t\t\t\tbody: body.toString(),\n\t\t\t});\n\t\t} catch (err) {\n\t\t\treturn Err(\n\t\t\t\tnew SalesforceAuthError(\n\t\t\t\t\t`Failed to connect to Salesforce auth endpoint: ${err instanceof Error ? err.message : String(err)}`,\n\t\t\t\t\terr instanceof Error ? err : undefined,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\n\t\tif (!response.ok) {\n\t\t\tconst text = await response.text();\n\t\t\treturn Err(\n\t\t\t\tnew SalesforceAuthError(`Salesforce authentication failed (${response.status}): ${text}`),\n\t\t\t);\n\t\t}\n\n\t\tconst data = (await response.json()) as SalesforceAuthResponse;\n\t\tthis.accessToken = data.access_token;\n\t\tthis.instanceUrl = data.instance_url;\n\n\t\treturn Ok(undefined);\n\t}\n\n\t/**\n\t * Execute a SOQL query with auto-pagination.\n\t *\n\t * Automatically authenticates on first call and re-authenticates on 401.\n\t */\n\tasync query<T>(soql: string): Promise<Result<T[], SalesforceApiError | SalesforceAuthError>> {\n\t\t// Ensure we have a token\n\t\tif (!this.accessToken) {\n\t\t\tconst authResult = await this.authenticate();\n\t\t\tif (!authResult.ok) return authResult;\n\t\t}\n\n\t\tconst allRecords: T[] = [];\n\t\tlet url = `${this.instanceUrl}/services/data/${this.apiVersion}/query?q=${encodeURIComponent(soql)}`;\n\n\t\twhile (true) {\n\t\t\tconst result = await this.request<SalesforceQueryResponse<T>>(url);\n\n\t\t\t// Re-auth on 401 and retry once\n\t\t\tif (\n\t\t\t\t!result.ok &&\n\t\t\t\tresult.error instanceof SalesforceApiError &&\n\t\t\t\tresult.error.statusCode === 401\n\t\t\t) {\n\t\t\t\tconst authResult = await this.authenticate();\n\t\t\t\tif (!authResult.ok) return authResult;\n\n\t\t\t\tconst retryResult = await this.request<SalesforceQueryResponse<T>>(url);\n\t\t\t\tif (!retryResult.ok) return retryResult;\n\n\t\t\t\tfor (const record of retryResult.value.records) {\n\t\t\t\t\tallRecords.push(record);\n\t\t\t\t}\n\n\t\t\t\tif (retryResult.value.done || !retryResult.value.nextRecordsUrl) break;\n\t\t\t\turl = `${this.instanceUrl}${retryResult.value.nextRecordsUrl}`;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (!result.ok) return result;\n\n\t\t\tfor (const record of result.value.records) {\n\t\t\t\tallRecords.push(record);\n\t\t\t}\n\n\t\t\tif (result.value.done || !result.value.nextRecordsUrl) break;\n\t\t\turl = `${this.instanceUrl}${result.value.nextRecordsUrl}`;\n\t\t}\n\n\t\treturn Ok(allRecords);\n\t}\n\n\t// -----------------------------------------------------------------------\n\t// Internal HTTP helpers\n\t// -----------------------------------------------------------------------\n\n\t/** Make an HTTP request with rate-limit retry logic. */\n\tprivate async request<T>(\n\t\turl: string,\n\t): Promise<Result<T, SalesforceApiError | SalesforceAuthError>> {\n\t\tfor (let attempt = 0; attempt <= MAX_RETRY_ATTEMPTS; attempt++) {\n\t\t\tconst headers: Record<string, string> = {\n\t\t\t\tAuthorization: `Bearer ${this.accessToken}`,\n\t\t\t\tAccept: \"application/json\",\n\t\t\t};\n\n\t\t\tconst response = await fetch(url, { method: \"GET\", headers });\n\n\t\t\tif (response.ok) {\n\t\t\t\tconst data = (await response.json()) as T;\n\t\t\t\treturn Ok(data);\n\t\t\t}\n\n\t\t\t// Rate limit: 503 with Retry-After\n\t\t\tif (response.status === 503) {\n\t\t\t\tconst retryAfter = response.headers.get(\"Retry-After\");\n\t\t\t\tconst waitMs = retryAfter ? Number.parseInt(retryAfter, 10) * 1000 : DEFAULT_RETRY_AFTER_MS;\n\n\t\t\t\tif (attempt < MAX_RETRY_ATTEMPTS) {\n\t\t\t\t\tawait sleep(waitMs);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst responseBody = await response.text();\n\t\t\t\treturn Err(new SalesforceApiError(503, responseBody));\n\t\t\t}\n\n\t\t\tconst responseBody = await response.text();\n\t\t\treturn Err(new SalesforceApiError(response.status, responseBody));\n\t\t}\n\n\t\treturn Err(new SalesforceApiError(0, \"Unknown error after retries\"));\n\t}\n}\n\n/** Sleep for the given number of milliseconds. */\nfunction sleep(ms: number): Promise<void> {\n\treturn new Promise((resolve) => setTimeout(resolve, ms));\n}\n","// ---------------------------------------------------------------------------\n// Salesforce Entity → Flat LakeSync Row Mapping\n// ---------------------------------------------------------------------------\n\nimport type { SfAccount, SfContact, SfLead, SfOpportunity } from \"./types\";\n\n/**\n * Map a Salesforce Account to a flat row for the `sf_accounts` table.\n *\n * The row ID is the Salesforce record Id.\n */\nexport function mapAccount(account: SfAccount): { rowId: string; row: Record<string, unknown> } {\n\treturn {\n\t\trowId: account.Id,\n\t\trow: {\n\t\t\tsf_id: account.Id,\n\t\t\tname: account.Name ?? null,\n\t\t\ttype: account.Type ?? null,\n\t\t\tindustry: account.Industry ?? null,\n\t\t\twebsite: account.Website ?? null,\n\t\t\tphone: account.Phone ?? null,\n\t\t\tbilling_city: account.BillingCity ?? null,\n\t\t\tbilling_state: account.BillingState ?? null,\n\t\t\tbilling_country: account.BillingCountry ?? null,\n\t\t\tannual_revenue: account.AnnualRevenue ?? null,\n\t\t\tnumber_of_employees: account.NumberOfEmployees ?? null,\n\t\t\towner_name: account.Owner?.Name ?? null,\n\t\t\tcreated_date: account.CreatedDate ?? null,\n\t\t\tlast_modified_date: account.LastModifiedDate ?? null,\n\t\t},\n\t};\n}\n\n/**\n * Map a Salesforce Contact to a flat row for the `sf_contacts` table.\n *\n * The row ID is the Salesforce record Id.\n */\nexport function mapContact(contact: SfContact): { rowId: string; row: Record<string, unknown> } {\n\treturn {\n\t\trowId: contact.Id,\n\t\trow: {\n\t\t\tsf_id: contact.Id,\n\t\t\tfirst_name: contact.FirstName ?? null,\n\t\t\tlast_name: contact.LastName ?? null,\n\t\t\temail: contact.Email ?? null,\n\t\t\tphone: contact.Phone ?? null,\n\t\t\ttitle: contact.Title ?? null,\n\t\t\taccount_id: contact.AccountId ?? null,\n\t\t\taccount_name: contact.Account?.Name ?? null,\n\t\t\tmailing_city: contact.MailingCity ?? null,\n\t\t\tmailing_state: contact.MailingState ?? null,\n\t\t\tmailing_country: contact.MailingCountry ?? null,\n\t\t\towner_name: contact.Owner?.Name ?? null,\n\t\t\tcreated_date: contact.CreatedDate ?? null,\n\t\t\tlast_modified_date: contact.LastModifiedDate ?? null,\n\t\t},\n\t};\n}\n\n/**\n * Map a Salesforce Opportunity to a flat row for the `sf_opportunities` table.\n *\n * The row ID is the Salesforce record Id.\n */\nexport function mapOpportunity(opportunity: SfOpportunity): {\n\trowId: string;\n\trow: Record<string, unknown>;\n} {\n\treturn {\n\t\trowId: opportunity.Id,\n\t\trow: {\n\t\t\tsf_id: opportunity.Id,\n\t\t\tname: opportunity.Name ?? null,\n\t\t\tstage_name: opportunity.StageName ?? null,\n\t\t\tamount: opportunity.Amount ?? null,\n\t\t\tclose_date: opportunity.CloseDate ?? null,\n\t\t\tprobability: opportunity.Probability ?? null,\n\t\t\taccount_id: opportunity.AccountId ?? null,\n\t\t\taccount_name: opportunity.Account?.Name ?? null,\n\t\t\ttype: opportunity.Type ?? null,\n\t\t\tlead_source: opportunity.LeadSource ?? null,\n\t\t\tis_closed: opportunity.IsClosed ?? null,\n\t\t\tis_won: opportunity.IsWon ?? null,\n\t\t\towner_name: opportunity.Owner?.Name ?? null,\n\t\t\tcreated_date: opportunity.CreatedDate ?? null,\n\t\t\tlast_modified_date: opportunity.LastModifiedDate ?? null,\n\t\t},\n\t};\n}\n\n/**\n * Map a Salesforce Lead to a flat row for the `sf_leads` table.\n *\n * The row ID is the Salesforce record Id.\n */\nexport function mapLead(lead: SfLead): { rowId: string; row: Record<string, unknown> } {\n\treturn {\n\t\trowId: lead.Id,\n\t\trow: {\n\t\t\tsf_id: lead.Id,\n\t\t\tfirst_name: lead.FirstName ?? null,\n\t\t\tlast_name: lead.LastName ?? null,\n\t\t\tcompany: lead.Company ?? null,\n\t\t\temail: lead.Email ?? null,\n\t\t\tphone: lead.Phone ?? null,\n\t\t\ttitle: lead.Title ?? null,\n\t\t\tstatus: lead.Status ?? null,\n\t\t\tlead_source: lead.LeadSource ?? null,\n\t\t\tis_converted: lead.IsConverted ?? null,\n\t\t\tconverted_account_id: lead.ConvertedAccountId ?? null,\n\t\t\tconverted_contact_id: lead.ConvertedContactId ?? null,\n\t\t\tconverted_opportunity_id: lead.ConvertedOpportunityId ?? null,\n\t\t\towner_name: lead.Owner?.Name ?? null,\n\t\t\tcreated_date: lead.CreatedDate ?? null,\n\t\t\tlast_modified_date: lead.LastModifiedDate ?? null,\n\t\t},\n\t};\n}\n","// ---------------------------------------------------------------------------\n// SalesforceSourcePoller — polls Salesforce CRM and pushes deltas to SyncGateway\n// ---------------------------------------------------------------------------\n\nimport { BaseSourcePoller, extractDelta, type PushTarget } from \"@lakesync/core\";\nimport { SalesforceClient } from \"./client\";\nimport { mapAccount, mapContact, mapLead, mapOpportunity } from \"./mapping\";\nimport type {\n\tSalesforceConnectorConfig,\n\tSalesforceIngestConfig,\n\tSfAccount,\n\tSfContact,\n\tSfLead,\n\tSfOpportunity,\n} from \"./types\";\n\nconst DEFAULT_INTERVAL_MS = 30_000;\n\n// ---------------------------------------------------------------------------\n// SOQL field lists\n// ---------------------------------------------------------------------------\n\nconst ACCOUNT_FIELDS = [\n\t\"Id\",\n\t\"Name\",\n\t\"Type\",\n\t\"Industry\",\n\t\"Website\",\n\t\"Phone\",\n\t\"BillingCity\",\n\t\"BillingState\",\n\t\"BillingCountry\",\n\t\"AnnualRevenue\",\n\t\"NumberOfEmployees\",\n\t\"Owner.Name\",\n\t\"CreatedDate\",\n\t\"LastModifiedDate\",\n].join(\", \");\n\nconst CONTACT_FIELDS = [\n\t\"Id\",\n\t\"FirstName\",\n\t\"LastName\",\n\t\"Email\",\n\t\"Phone\",\n\t\"Title\",\n\t\"AccountId\",\n\t\"Account.Name\",\n\t\"MailingCity\",\n\t\"MailingState\",\n\t\"MailingCountry\",\n\t\"Owner.Name\",\n\t\"CreatedDate\",\n\t\"LastModifiedDate\",\n].join(\", \");\n\nconst OPPORTUNITY_FIELDS = [\n\t\"Id\",\n\t\"Name\",\n\t\"StageName\",\n\t\"Amount\",\n\t\"CloseDate\",\n\t\"Probability\",\n\t\"AccountId\",\n\t\"Account.Name\",\n\t\"Type\",\n\t\"LeadSource\",\n\t\"IsClosed\",\n\t\"IsWon\",\n\t\"Owner.Name\",\n\t\"CreatedDate\",\n\t\"LastModifiedDate\",\n].join(\", \");\n\nconst LEAD_FIELDS = [\n\t\"Id\",\n\t\"FirstName\",\n\t\"LastName\",\n\t\"Company\",\n\t\"Email\",\n\t\"Phone\",\n\t\"Title\",\n\t\"Status\",\n\t\"LeadSource\",\n\t\"IsConverted\",\n\t\"ConvertedAccountId\",\n\t\"ConvertedContactId\",\n\t\"ConvertedOpportunityId\",\n\t\"Owner.Name\",\n\t\"CreatedDate\",\n\t\"LastModifiedDate\",\n].join(\", \");\n\n/**\n * Polls Salesforce CRM for accounts, contacts, opportunities, and leads\n * and pushes detected changes into a gateway via streaming accumulation.\n *\n * Uses {@link BaseSourcePoller.accumulateDelta} to push deltas in\n * memory-bounded chunks instead of collecting all deltas in a single array.\n */\nexport class SalesforceSourcePoller extends BaseSourcePoller {\n\tprivate readonly connectionConfig: SalesforceConnectorConfig;\n\tprivate readonly client: SalesforceClient;\n\n\t/** Per-entity cursors: max LastModifiedDate from the last poll. */\n\tprivate cursors: Record<string, string | undefined> = {\n\t\taccounts: undefined,\n\t\tcontacts: undefined,\n\t\topportunities: undefined,\n\t\tleads: undefined,\n\t};\n\n\tconstructor(\n\t\tconnectionConfig: SalesforceConnectorConfig,\n\t\tingestConfig: SalesforceIngestConfig | undefined,\n\t\tname: string,\n\t\tgateway: PushTarget,\n\t\tclient?: SalesforceClient,\n\t) {\n\t\tsuper({\n\t\t\tname,\n\t\t\tintervalMs: ingestConfig?.intervalMs ?? DEFAULT_INTERVAL_MS,\n\t\t\tgateway,\n\t\t\tmemory: {\n\t\t\t\tchunkSize: ingestConfig?.chunkSize,\n\t\t\t\tmemoryBudgetBytes: ingestConfig?.memoryBudgetBytes,\n\t\t\t},\n\t\t});\n\t\tthis.connectionConfig = connectionConfig;\n\t\tthis.client = client ?? new SalesforceClient(connectionConfig);\n\t}\n\n\t/** Execute a single poll cycle across all enabled entity types. */\n\tasync poll(): Promise<void> {\n\t\tconst includeAccounts = this.connectionConfig.includeAccounts ?? true;\n\t\tif (includeAccounts) {\n\t\t\tawait this.pollEntity<SfAccount>(\n\t\t\t\t\"Account\",\n\t\t\t\tACCOUNT_FIELDS,\n\t\t\t\t\"accounts\",\n\t\t\t\t\"sf_accounts\",\n\t\t\t\tmapAccount,\n\t\t\t);\n\t\t}\n\n\t\tconst includeContacts = this.connectionConfig.includeContacts ?? true;\n\t\tif (includeContacts) {\n\t\t\tawait this.pollEntity<SfContact>(\n\t\t\t\t\"Contact\",\n\t\t\t\tCONTACT_FIELDS,\n\t\t\t\t\"contacts\",\n\t\t\t\t\"sf_contacts\",\n\t\t\t\tmapContact,\n\t\t\t);\n\t\t}\n\n\t\tconst includeOpportunities = this.connectionConfig.includeOpportunities ?? true;\n\t\tif (includeOpportunities) {\n\t\t\tawait this.pollEntity<SfOpportunity>(\n\t\t\t\t\"Opportunity\",\n\t\t\t\tOPPORTUNITY_FIELDS,\n\t\t\t\t\"opportunities\",\n\t\t\t\t\"sf_opportunities\",\n\t\t\t\tmapOpportunity,\n\t\t\t);\n\t\t}\n\n\t\tconst includeLeads = this.connectionConfig.includeLeads ?? true;\n\t\tif (includeLeads) {\n\t\t\tawait this.pollEntity<SfLead>(\"Lead\", LEAD_FIELDS, \"leads\", \"sf_leads\", mapLead);\n\t\t}\n\n\t\tawait this.flushAccumulator();\n\t}\n\n\t// -----------------------------------------------------------------------\n\t// Generic entity polling via LastModifiedDate cursor\n\t// -----------------------------------------------------------------------\n\n\tprivate async pollEntity<T extends { Id: string; LastModifiedDate: string | null }>(\n\t\tsObjectType: string,\n\t\tfields: string,\n\t\tcursorKey: string,\n\t\ttable: string,\n\t\tmapFn: (record: T) => { rowId: string; row: Record<string, unknown> },\n\t): Promise<void> {\n\t\tconst cursor = this.cursors[cursorKey];\n\t\tconst soql = this.buildSoql(sObjectType, fields, cursor);\n\n\t\tconst result = await this.client.query<T>(soql);\n\t\tif (!result.ok) return;\n\n\t\tconst records = result.value;\n\t\tif (records.length === 0) return;\n\n\t\tlet maxLastModified = cursor;\n\n\t\tfor (const record of records) {\n\t\t\tconst { rowId, row } = mapFn(record);\n\n\t\t\tconst delta = await extractDelta(null, row, {\n\t\t\t\ttable,\n\t\t\t\trowId,\n\t\t\t\tclientId: this.clientId,\n\t\t\t\thlc: this.hlc.now(),\n\t\t\t});\n\n\t\t\tif (delta) {\n\t\t\t\tawait this.accumulateDelta(delta);\n\t\t\t}\n\n\t\t\tconst lastModified = record.LastModifiedDate;\n\t\t\tif (lastModified && (!maxLastModified || lastModified > maxLastModified)) {\n\t\t\t\tmaxLastModified = lastModified;\n\t\t\t}\n\t\t}\n\n\t\tthis.cursors[cursorKey] = maxLastModified;\n\t}\n\n\t// -----------------------------------------------------------------------\n\t// SOQL query builder\n\t// -----------------------------------------------------------------------\n\n\tprivate buildSoql(sObjectType: string, fields: string, cursor: string | undefined): string {\n\t\tconst clauses: string[] = [];\n\n\t\tif (cursor) {\n\t\t\tclauses.push(`LastModifiedDate > ${cursor}`);\n\t\t}\n\n\t\tif (this.connectionConfig.soqlFilter) {\n\t\t\tclauses.push(this.connectionConfig.soqlFilter);\n\t\t}\n\n\t\tconst where = clauses.length > 0 ? ` WHERE ${clauses.join(\" AND \")}` : \"\";\n\t\treturn `SELECT ${fields} FROM ${sObjectType}${where} ORDER BY LastModifiedDate ASC`;\n\t}\n}\n"],"mappings":"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","names":["responseBody"]}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { L as LakeSyncError, R as Result, H as HLCTimestamp } from './result-CojzlFE2.js';
|
|
2
|
+
|
|
3
|
+
/** Error during action execution (may be retryable). */
|
|
4
|
+
declare class ActionExecutionError extends LakeSyncError {
|
|
5
|
+
readonly retryable: boolean;
|
|
6
|
+
constructor(message: string, retryable: boolean, cause?: Error);
|
|
7
|
+
}
|
|
8
|
+
/** The requested action type is not supported by the connector. */
|
|
9
|
+
declare class ActionNotSupportedError extends LakeSyncError {
|
|
10
|
+
constructor(message: string, cause?: Error);
|
|
11
|
+
}
|
|
12
|
+
/** Action payload failed structural validation. */
|
|
13
|
+
declare class ActionValidationError extends LakeSyncError {
|
|
14
|
+
constructor(message: string, cause?: Error);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Supported comparison operators for sync rule filters */
|
|
18
|
+
type SyncRuleOp = "eq" | "in" | "neq" | "gt" | "lt" | "gte" | "lte";
|
|
19
|
+
/** A single filter condition within a bucket definition */
|
|
20
|
+
interface SyncRuleFilter {
|
|
21
|
+
/** Column name to match against */
|
|
22
|
+
column: string;
|
|
23
|
+
/** Comparison operator */
|
|
24
|
+
op: SyncRuleOp;
|
|
25
|
+
/** Literal value or JWT claim reference (prefixed with "jwt:") */
|
|
26
|
+
value: string;
|
|
27
|
+
}
|
|
28
|
+
/** A named bucket defining which rows a client should receive */
|
|
29
|
+
interface BucketDefinition {
|
|
30
|
+
/** Unique bucket name */
|
|
31
|
+
name: string;
|
|
32
|
+
/** Tables this bucket applies to. Empty array = all tables. */
|
|
33
|
+
tables: string[];
|
|
34
|
+
/** Row-level filter conditions (conjunctive — all must match). Empty = no row filtering. */
|
|
35
|
+
filters: SyncRuleFilter[];
|
|
36
|
+
}
|
|
37
|
+
/** Top-level sync rules configuration */
|
|
38
|
+
interface SyncRulesConfig {
|
|
39
|
+
/** Configuration version (for future schema evolution) */
|
|
40
|
+
version: number;
|
|
41
|
+
/** Bucket definitions */
|
|
42
|
+
buckets: BucketDefinition[];
|
|
43
|
+
}
|
|
44
|
+
/** Resolved JWT claims as a flat record */
|
|
45
|
+
type ResolvedClaims = Record<string, string | string[]>;
|
|
46
|
+
/** Context for evaluating sync rules against a specific client */
|
|
47
|
+
interface SyncRulesContext {
|
|
48
|
+
/** Resolved JWT claims for the requesting client */
|
|
49
|
+
claims: ResolvedClaims;
|
|
50
|
+
/** The sync rules configuration to evaluate */
|
|
51
|
+
rules: SyncRulesConfig;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Describes an action type supported by a connector. */
|
|
55
|
+
interface ActionDescriptor {
|
|
56
|
+
/** The action type identifier (e.g. "create_pr", "send_message"). */
|
|
57
|
+
actionType: string;
|
|
58
|
+
/** Human-readable description of what this action does. */
|
|
59
|
+
description: string;
|
|
60
|
+
/** Optional JSON Schema for the action's params. */
|
|
61
|
+
paramsSchema?: Record<string, unknown>;
|
|
62
|
+
}
|
|
63
|
+
/** Authentication context passed to action handlers for permission checks. */
|
|
64
|
+
interface AuthContext {
|
|
65
|
+
/** Resolved JWT claims for resource-level permission checks. */
|
|
66
|
+
claims: ResolvedClaims;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Interface for connectors that can execute imperative actions.
|
|
70
|
+
*
|
|
71
|
+
* Separate from `DatabaseAdapter` — not all connectors support actions
|
|
72
|
+
* (e.g. S3 doesn't). A connector can implement `DatabaseAdapter` (read/write
|
|
73
|
+
* data), `ActionHandler` (execute commands), or both.
|
|
74
|
+
*/
|
|
75
|
+
interface ActionHandler {
|
|
76
|
+
/** Descriptors for all action types this handler supports. */
|
|
77
|
+
readonly supportedActions: ActionDescriptor[];
|
|
78
|
+
/** Execute a single action against the external system. */
|
|
79
|
+
executeAction(action: Action, context?: AuthContext): Promise<Result<ActionResult, ActionExecutionError | ActionNotSupportedError>>;
|
|
80
|
+
}
|
|
81
|
+
/** Type guard: check whether an object implements the ActionHandler interface. */
|
|
82
|
+
declare function isActionHandler(obj: unknown): obj is ActionHandler;
|
|
83
|
+
|
|
84
|
+
/** Discovery response listing available connectors and their supported actions. */
|
|
85
|
+
interface ActionDiscovery {
|
|
86
|
+
/** Map of connector name to its supported action descriptors. */
|
|
87
|
+
connectors: Record<string, ActionDescriptor[]>;
|
|
88
|
+
}
|
|
89
|
+
/** An imperative action to execute against an external system. */
|
|
90
|
+
interface Action {
|
|
91
|
+
/** Unique action identifier (deterministic SHA-256 hash). */
|
|
92
|
+
actionId: string;
|
|
93
|
+
/** Client that initiated the action. */
|
|
94
|
+
clientId: string;
|
|
95
|
+
/** HLC timestamp when the action was created. */
|
|
96
|
+
hlc: HLCTimestamp;
|
|
97
|
+
/** Target connector name (e.g. "github", "slack", "linear"). */
|
|
98
|
+
connector: string;
|
|
99
|
+
/** Action type within the connector (e.g. "create_pr", "send_message"). */
|
|
100
|
+
actionType: string;
|
|
101
|
+
/** Action parameters — connector-specific payload. */
|
|
102
|
+
params: Record<string, unknown>;
|
|
103
|
+
/** Optional idempotency key for at-most-once delivery. */
|
|
104
|
+
idempotencyKey?: string;
|
|
105
|
+
}
|
|
106
|
+
/** Successful result of executing an action. */
|
|
107
|
+
interface ActionResult {
|
|
108
|
+
/** The action that was executed. */
|
|
109
|
+
actionId: string;
|
|
110
|
+
/** Result data returned by the connector. */
|
|
111
|
+
data: Record<string, unknown>;
|
|
112
|
+
/** Server HLC after processing. */
|
|
113
|
+
serverHlc: HLCTimestamp;
|
|
114
|
+
}
|
|
115
|
+
/** Error result of executing an action. */
|
|
116
|
+
interface ActionErrorResult {
|
|
117
|
+
/** The action that failed. */
|
|
118
|
+
actionId: string;
|
|
119
|
+
/** Error code. */
|
|
120
|
+
code: string;
|
|
121
|
+
/** Human-readable error message. */
|
|
122
|
+
message: string;
|
|
123
|
+
/** Whether the client can retry this action. */
|
|
124
|
+
retryable: boolean;
|
|
125
|
+
}
|
|
126
|
+
/** Batch of actions pushed by a client. */
|
|
127
|
+
interface ActionPush {
|
|
128
|
+
/** Client identifier. */
|
|
129
|
+
clientId: string;
|
|
130
|
+
/** Actions to execute. */
|
|
131
|
+
actions: Action[];
|
|
132
|
+
}
|
|
133
|
+
/** Gateway response to an action push. */
|
|
134
|
+
interface ActionResponse {
|
|
135
|
+
/** Results for each action (success or error). */
|
|
136
|
+
results: Array<ActionResult | ActionErrorResult>;
|
|
137
|
+
/** Server HLC after processing. */
|
|
138
|
+
serverHlc: HLCTimestamp;
|
|
139
|
+
}
|
|
140
|
+
/** Type guard: check whether a result is an error. */
|
|
141
|
+
declare function isActionError(result: ActionResult | ActionErrorResult): result is ActionErrorResult;
|
|
142
|
+
|
|
143
|
+
export { type Action as A, type BucketDefinition as B, type ResolvedClaims as R, type SyncRulesConfig as S, ActionValidationError as a, type SyncRulesContext as b, type ActionDescriptor as c, type ActionDiscovery as d, type ActionErrorResult as e, ActionExecutionError as f, type ActionHandler as g, ActionNotSupportedError as h, type ActionPush as i, type ActionResponse as j, type ActionResult as k, type AuthContext as l, type SyncRuleFilter as m, type SyncRuleOp as n, isActionError as o, isActionHandler as p };
|