@jsforce/jsforce-node 3.1.0 → 3.2.1
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 +12 -1
- package/lib/VERSION.d.ts +1 -1
- package/lib/VERSION.js +1 -1
- package/lib/api/bulk.d.ts +2 -1
- package/lib/api/bulk.js +5 -1
- package/lib/api/bulk2.d.ts +4 -6
- package/lib/api/bulk2.js +14 -12
- package/lib/api/chatter.js +2 -2
- package/lib/api/metadata.d.ts +3 -3
- package/lib/api/metadata.js +1 -1
- package/lib/api/soap.d.ts +4 -4
- package/lib/api/streaming/extension.js +1 -3
- package/lib/api/streaming.js +5 -5
- package/lib/api/wsdl/wsdl2schema.js +1 -1
- package/lib/browser/request.js +1 -1
- package/lib/cache.js +1 -1
- package/lib/connection.d.ts +2 -2
- package/lib/connection.js +31 -13
- package/lib/http-api.js +1 -0
- package/lib/oauth2.js +2 -3
- package/lib/query.d.ts +6 -10
- package/lib/query.js +2 -1
- package/lib/registry/base.js +7 -0
- package/lib/registry/sfdx.js +2 -0
- package/lib/registry/types.d.ts +2 -2
- package/lib/request.js +93 -25
- package/lib/soap.js +4 -4
- package/lib/soql-builder.js +1 -1
- package/lib/transport.js +1 -1
- package/lib/types/common.d.ts +3 -0
- package/lib/types/projection.d.ts +4 -4
- package/lib/types/schema.d.ts +4 -4
- package/lib/types/standard-schema.d.ts +950 -950
- package/lib/util/logger.d.ts +6 -6
- package/package.json +4 -1
package/README.md
CHANGED
|
@@ -24,12 +24,22 @@ Supported Salesforce APIs are the following:
|
|
|
24
24
|
|
|
25
25
|
## Documentation
|
|
26
26
|
|
|
27
|
-
See documentation in
|
|
27
|
+
See documentation in https://jsforce.github.io/
|
|
28
|
+
|
|
29
|
+
v3 API reference:
|
|
30
|
+
https://jsforce.github.io/jsforce/
|
|
31
|
+
|
|
32
|
+
v1 API reference:
|
|
33
|
+
https://jsforce.github.io/jsforce/doc/
|
|
28
34
|
|
|
29
35
|
## Releases
|
|
30
36
|
|
|
31
37
|
See [Releases](https://github.com/jsforce/jsforce/releases).
|
|
32
38
|
|
|
39
|
+
## Node-specific release
|
|
40
|
+
|
|
41
|
+
See [jsforce-node](./JSFORCE-NODE.md).
|
|
42
|
+
|
|
33
43
|
## License
|
|
34
44
|
|
|
35
45
|
See [license](LICENSE) (MIT License).
|
|
@@ -45,6 +55,7 @@ If you have any questions first file it on [issues](https://github.com/jsforce/j
|
|
|
45
55
|
## How to build/run tests:
|
|
46
56
|
See [DEVELOPING.md](./DEVELOPING.md)
|
|
47
57
|
|
|
58
|
+
|
|
48
59
|
## Contributions
|
|
49
60
|
|
|
50
61
|
Your contributions are welcome: both by reporting issues on [GitHub issues](https://github.com/jsforce/jsforce/issues) or pull-requesting patches.
|
package/lib/VERSION.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const _default: "2.
|
|
1
|
+
declare const _default: "3.2.1";
|
|
2
2
|
export default _default;
|
package/lib/VERSION.js
CHANGED
package/lib/api/bulk.d.ts
CHANGED
|
@@ -17,7 +17,7 @@ export type BulkOptions = {
|
|
|
17
17
|
concurrencyMode?: 'Serial' | 'Parallel';
|
|
18
18
|
assignmentRuleId?: string;
|
|
19
19
|
};
|
|
20
|
-
export type JobState = 'Open' | 'Closed' | 'Aborted' | 'Failed' | 'Unknown';
|
|
20
|
+
export type JobState = 'Open' | 'Closed' | 'Aborted' | 'Failed' | 'Unknown' | 'NotProcessed';
|
|
21
21
|
export type JobInfo = {
|
|
22
22
|
id: string;
|
|
23
23
|
object: string;
|
|
@@ -42,6 +42,7 @@ export type BulkQueryBatchResult = Array<{
|
|
|
42
42
|
export type BulkIngestBatchResult = Array<{
|
|
43
43
|
id: string | null;
|
|
44
44
|
success: boolean;
|
|
45
|
+
created: boolean;
|
|
45
46
|
errors: string[];
|
|
46
47
|
}>;
|
|
47
48
|
export type BatchResult<Opr extends BulkOperation> = Opr extends 'query' | 'queryAll' ? BulkQueryBatchResult : BulkIngestBatchResult;
|
package/lib/api/bulk.js
CHANGED
|
@@ -472,6 +472,9 @@ class Batch extends stream_1.Writable {
|
|
|
472
472
|
else if (res.state === 'Completed') {
|
|
473
473
|
this.retrieve();
|
|
474
474
|
}
|
|
475
|
+
else if (res.state === 'NotProcessed') {
|
|
476
|
+
this.emit('error', new Error('Job has been aborted'));
|
|
477
|
+
}
|
|
475
478
|
else {
|
|
476
479
|
this.emit('inProgress', res);
|
|
477
480
|
setTimeout(poll, interval);
|
|
@@ -508,6 +511,7 @@ class Batch extends stream_1.Writable {
|
|
|
508
511
|
results = res.map((ret) => ({
|
|
509
512
|
id: ret.Id || null,
|
|
510
513
|
success: ret.Success === 'true',
|
|
514
|
+
created: ret.Created === 'true',
|
|
511
515
|
errors: ret.Error ? [ret.Error] : [],
|
|
512
516
|
}));
|
|
513
517
|
}
|
|
@@ -558,7 +562,7 @@ class BulkApi extends http_api_1.default {
|
|
|
558
562
|
}
|
|
559
563
|
isSessionExpired(response) {
|
|
560
564
|
return (response.statusCode === 400 &&
|
|
561
|
-
|
|
565
|
+
response.body.includes('<exceptionCode>InvalidSessionId</exceptionCode>'));
|
|
562
566
|
}
|
|
563
567
|
hasErrorInResponseBody(body) {
|
|
564
568
|
return !!body.error;
|
package/lib/api/bulk2.d.ts
CHANGED
|
@@ -47,7 +47,7 @@ export type IngestJobV2FailedResults<S extends Schema> = Array<{
|
|
|
47
47
|
sf__Error: string;
|
|
48
48
|
sf__Id: string;
|
|
49
49
|
} & S>;
|
|
50
|
-
export type IngestJobV2UnprocessedRecords<S extends Schema> =
|
|
50
|
+
export type IngestJobV2UnprocessedRecords<S extends Schema> = S[] | string;
|
|
51
51
|
export type IngestJobV2Results<S extends Schema> = {
|
|
52
52
|
successfulResults: IngestJobV2SuccessfulResults<S>;
|
|
53
53
|
failedResults: IngestJobV2FailedResults<S>;
|
|
@@ -137,7 +137,7 @@ export declare class BulkV2<S extends Schema> {
|
|
|
137
137
|
* Default timeout: 10000ms
|
|
138
138
|
*
|
|
139
139
|
* @param soql SOQL query
|
|
140
|
-
* @param
|
|
140
|
+
* @param options
|
|
141
141
|
*
|
|
142
142
|
* @returns {RecordStream} - Record stream, convertible to a CSV data stream
|
|
143
143
|
*/
|
|
@@ -156,8 +156,7 @@ export declare class QueryJobV2<S extends Schema> extends EventEmitter {
|
|
|
156
156
|
private error;
|
|
157
157
|
private jobInfo?;
|
|
158
158
|
private locator?;
|
|
159
|
-
constructor(conn: Connection<S>, options: ExistingQueryJobV2Options);
|
|
160
|
-
constructor(conn: Connection<S>, options: CreateQueryJobV2Options);
|
|
159
|
+
constructor(conn: Connection<S>, options: ExistingQueryJobV2Options | CreateQueryJobV2Options);
|
|
161
160
|
/**
|
|
162
161
|
* Get the query job ID.
|
|
163
162
|
*
|
|
@@ -228,8 +227,7 @@ export declare class IngestJobV2<S extends Schema> extends EventEmitter {
|
|
|
228
227
|
/**
|
|
229
228
|
*
|
|
230
229
|
*/
|
|
231
|
-
constructor(conn: Connection<S>, options: ExistingIngestJobOptions);
|
|
232
|
-
constructor(conn: Connection<S>, options: CreateIngestJobV2Options);
|
|
230
|
+
constructor(conn: Connection<S>, options: CreateIngestJobV2Options | ExistingIngestJobOptions);
|
|
233
231
|
/**
|
|
234
232
|
* Get the query job ID.
|
|
235
233
|
*
|
package/lib/api/bulk2.js
CHANGED
|
@@ -30,7 +30,8 @@ class BulkApiV2 extends http_api_1.default {
|
|
|
30
30
|
'errorCode' in body[0]);
|
|
31
31
|
}
|
|
32
32
|
isSessionExpired(response) {
|
|
33
|
-
return (response.statusCode === 401 &&
|
|
33
|
+
return (response.statusCode === 401 &&
|
|
34
|
+
response.body.includes('INVALID_SESSION_ID'));
|
|
34
35
|
}
|
|
35
36
|
parseError(body) {
|
|
36
37
|
return {
|
|
@@ -110,20 +111,18 @@ class BulkV2 {
|
|
|
110
111
|
options.pollTimeout = this.pollTimeout;
|
|
111
112
|
if (!options.pollInterval)
|
|
112
113
|
options.pollInterval = this.pollInterval;
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
operation: options.operation,
|
|
116
|
-
});
|
|
114
|
+
const { pollInterval, pollTimeout, input, ...createJobOpts } = options;
|
|
115
|
+
const job = this.createJob(createJobOpts);
|
|
117
116
|
try {
|
|
118
117
|
await job.open();
|
|
119
|
-
await job.uploadData(
|
|
118
|
+
await job.uploadData(input);
|
|
120
119
|
await job.close();
|
|
121
|
-
await job.poll(
|
|
120
|
+
await job.poll(pollInterval, pollTimeout);
|
|
122
121
|
return await job.getAllResults();
|
|
123
122
|
}
|
|
124
123
|
catch (error) {
|
|
125
124
|
const err = error;
|
|
126
|
-
this.logger.error(`bulk load failed due to: ${err}`);
|
|
125
|
+
this.logger.error(`bulk load failed due to: ${err.message}`);
|
|
127
126
|
if (err.name !== 'JobPollingTimeoutError') {
|
|
128
127
|
// fires off one last attempt to clean up and ignores the result | error
|
|
129
128
|
job.delete().catch((ignored) => ignored);
|
|
@@ -137,7 +136,7 @@ class BulkV2 {
|
|
|
137
136
|
* Default timeout: 10000ms
|
|
138
137
|
*
|
|
139
138
|
* @param soql SOQL query
|
|
140
|
-
* @param
|
|
139
|
+
* @param options
|
|
141
140
|
*
|
|
142
141
|
* @returns {RecordStream} - Record stream, convertible to a CSV data stream
|
|
143
142
|
*/
|
|
@@ -163,7 +162,7 @@ class BulkV2 {
|
|
|
163
162
|
}
|
|
164
163
|
catch (error) {
|
|
165
164
|
const err = error;
|
|
166
|
-
this.logger.error(`bulk query failed due to: ${err}`);
|
|
165
|
+
this.logger.error(`bulk query failed due to: ${err.message}`);
|
|
167
166
|
if (err.name !== 'JobPollingTimeoutError') {
|
|
168
167
|
// fires off one last attempt to clean up and ignores the result | error
|
|
169
168
|
queryJob.delete().catch((ignored) => ignored);
|
|
@@ -280,7 +279,7 @@ class QueryJobV2 extends events_1.EventEmitter {
|
|
|
280
279
|
const jobId = this.id;
|
|
281
280
|
const startTime = Date.now();
|
|
282
281
|
const endTime = startTime + timeout;
|
|
283
|
-
this.logger.debug(
|
|
282
|
+
this.logger.debug('Start polling for job status');
|
|
284
283
|
this.logger.debug(`Polling options: timeout:${timeout}ms | interval: ${interval}ms.`);
|
|
285
284
|
if (timeout === 0) {
|
|
286
285
|
throw new JobPollingTimeoutError(`Skipping polling because of timeout = 0ms. Job Id = ${jobId}`, jobId);
|
|
@@ -401,6 +400,9 @@ class IngestJobV2 extends events_1.EventEmitter {
|
|
|
401
400
|
bulkJobUnprocessedRecords;
|
|
402
401
|
error;
|
|
403
402
|
jobInfo;
|
|
403
|
+
/**
|
|
404
|
+
*
|
|
405
|
+
*/
|
|
404
406
|
constructor(conn, options) {
|
|
405
407
|
super();
|
|
406
408
|
this.connection = conn;
|
|
@@ -540,7 +542,7 @@ class IngestJobV2 extends events_1.EventEmitter {
|
|
|
540
542
|
if (timeout === 0) {
|
|
541
543
|
throw new JobPollingTimeoutError(`Skipping polling because of timeout = 0ms. Job Id = ${jobId}`, jobId);
|
|
542
544
|
}
|
|
543
|
-
this.logger.debug(
|
|
545
|
+
this.logger.debug('Start polling for job status');
|
|
544
546
|
this.logger.debug(`Polling options: timeout:${timeout}ms | interval: ${interval}ms.`);
|
|
545
547
|
while (endTime > Date.now()) {
|
|
546
548
|
try {
|
package/lib/api/chatter.js
CHANGED
|
@@ -173,13 +173,13 @@ class Chatter {
|
|
|
173
173
|
* @private
|
|
174
174
|
*/
|
|
175
175
|
_normalizeUrl(url) {
|
|
176
|
-
if (url.
|
|
176
|
+
if (url.startsWith('/chatter/') || url.startsWith('/connect/')) {
|
|
177
177
|
return '/services/data/v' + this._conn.version + url;
|
|
178
178
|
}
|
|
179
179
|
else if (/^\/v[\d]+\.[\d]+\//.test(url)) {
|
|
180
180
|
return '/services/data' + url;
|
|
181
181
|
}
|
|
182
|
-
else if (url.
|
|
182
|
+
else if (!url.startsWith('/services/') && url.startsWith('/')) {
|
|
183
183
|
return '/services/data/v' + this._conn.version + '/chatter' + url;
|
|
184
184
|
}
|
|
185
185
|
else {
|
package/lib/api/metadata.d.ts
CHANGED
|
@@ -17,7 +17,7 @@ export * from './metadata/schema';
|
|
|
17
17
|
type MetadataType_<K extends keyof ApiSchemaTypes = keyof ApiSchemaTypes> = K extends keyof ApiSchemaTypes ? ApiSchemaTypes[K] extends Metadata ? K : never : never;
|
|
18
18
|
export type MetadataType = MetadataType_;
|
|
19
19
|
export type MetadataDefinition<T extends string, M extends Metadata = Metadata> = Metadata extends M ? T extends keyof ApiSchemaTypes & MetadataType ? ApiSchemaTypes[T] extends Metadata ? ApiSchemaTypes[T] : Metadata : Metadata : M;
|
|
20
|
-
type DeepPartial<T> = T extends any[] ? DeepPartial<T[number]
|
|
20
|
+
type DeepPartial<T> = T extends any[] ? Array<DeepPartial<T[number]>> : T extends object ? {
|
|
21
21
|
[K in keyof T]?: DeepPartial<T[K]>;
|
|
22
22
|
} : T;
|
|
23
23
|
export type InputMetadataDefinition<T extends string, M extends Metadata = Metadata> = DeepPartial<MetadataDefinition<T, M>>;
|
|
@@ -59,9 +59,9 @@ export declare class MetadataApi<S extends Schema> {
|
|
|
59
59
|
/**
|
|
60
60
|
* Update one or more metadata components in the organization.
|
|
61
61
|
*/
|
|
62
|
-
update<M extends Metadata = Metadata, T extends string = string, MD extends InputMetadataDefinition<T, M> = InputMetadataDefinition<T, M>>(type: T, metadata: Partial<MD
|
|
62
|
+
update<M extends Metadata = Metadata, T extends string = string, MD extends InputMetadataDefinition<T, M> = InputMetadataDefinition<T, M>>(type: T, metadata: Array<Partial<MD>>): Promise<SaveResult[]>;
|
|
63
63
|
update<M extends Metadata = Metadata, T extends string = string, MD extends InputMetadataDefinition<T, M> = InputMetadataDefinition<T, M>>(type: T, metadata: Partial<MD>): Promise<SaveResult>;
|
|
64
|
-
update<M extends Metadata = Metadata, T extends string = string, MD extends InputMetadataDefinition<T, M> = InputMetadataDefinition<T, M>>(type: T, metadata: Partial<MD> | Partial<MD
|
|
64
|
+
update<M extends Metadata = Metadata, T extends string = string, MD extends InputMetadataDefinition<T, M> = InputMetadataDefinition<T, M>>(type: T, metadata: Partial<MD> | Array<Partial<MD>>): Promise<SaveResult | SaveResult[]>;
|
|
65
65
|
/**
|
|
66
66
|
* Upsert one or more components in your organization's data.
|
|
67
67
|
*/
|
package/lib/api/metadata.js
CHANGED
|
@@ -295,7 +295,7 @@ class AsyncResultLocator extends events_1.EventEmitter {
|
|
|
295
295
|
async check() {
|
|
296
296
|
const result = await this._promise;
|
|
297
297
|
this._id = result.id;
|
|
298
|
-
return
|
|
298
|
+
return this._meta.checkStatus(result.id);
|
|
299
299
|
}
|
|
300
300
|
/**
|
|
301
301
|
* Polling until async call status becomes complete or error
|
package/lib/api/soap.d.ts
CHANGED
|
@@ -15,15 +15,15 @@ export declare class SoapApi<S extends Schema> {
|
|
|
15
15
|
/**
|
|
16
16
|
* Converts a Lead into an Account, Contact, or (optionally) an Opportunity.
|
|
17
17
|
*/
|
|
18
|
-
convertLead(leadConverts: Partial<LeadConvert
|
|
18
|
+
convertLead(leadConverts: Array<Partial<LeadConvert>>): Promise<LeadConvertResult[]>;
|
|
19
19
|
convertLead(leadConvert: Partial<LeadConvert>): Promise<LeadConvertResult>;
|
|
20
|
-
convertLead(leadConvert: Partial<LeadConvert> | Partial<LeadConvert
|
|
20
|
+
convertLead(leadConvert: Partial<LeadConvert> | Array<Partial<LeadConvert>>): Promise<LeadConvertResult | LeadConvertResult[]>;
|
|
21
21
|
/**
|
|
22
22
|
* Merge up to three records into one
|
|
23
23
|
*/
|
|
24
|
-
merge(mergeRequests: Partial<MergeRequest
|
|
24
|
+
merge(mergeRequests: Array<Partial<MergeRequest>>): Promise<MergeResult[]>;
|
|
25
25
|
merge(mergeRequest: Partial<MergeRequest>): Promise<MergeResult>;
|
|
26
|
-
merge(mergeRequest: Partial<MergeRequest> | Partial<MergeRequest
|
|
26
|
+
merge(mergeRequest: Partial<MergeRequest> | Array<Partial<MergeRequest>>): Promise<MergeResult | MergeResult[]>;
|
|
27
27
|
/**
|
|
28
28
|
* Delete records from the recycle bin immediately
|
|
29
29
|
*/
|
|
@@ -125,9 +125,7 @@ class Replay {
|
|
|
125
125
|
}
|
|
126
126
|
}
|
|
127
127
|
else if (message.channel === this._channel &&
|
|
128
|
-
message.data
|
|
129
|
-
message.data.event &&
|
|
130
|
-
message.data.event.replayId) {
|
|
128
|
+
message.data?.event?.replayId) {
|
|
131
129
|
this._replay = message.data.event.replayId;
|
|
132
130
|
}
|
|
133
131
|
callback(message);
|
package/lib/api/streaming.js
CHANGED
|
@@ -121,13 +121,13 @@ class Streaming extends events_1.EventEmitter {
|
|
|
121
121
|
/* @private */
|
|
122
122
|
_createClient(forChannelName, extensions) {
|
|
123
123
|
// forChannelName is advisory, for an API workaround. It does not restrict or select the channel.
|
|
124
|
-
const needsReplayFix = typeof forChannelName === 'string' && forChannelName.
|
|
124
|
+
const needsReplayFix = typeof forChannelName === 'string' && forChannelName.startsWith('/u/');
|
|
125
125
|
const endpointUrl = [
|
|
126
126
|
this._conn.instanceUrl,
|
|
127
127
|
// special endpoint "/cometd/replay/xx.x" is only available in 36.0.
|
|
128
128
|
// See https://releasenotes.docs.salesforce.com/en-us/summer16/release-notes/rn_api_streaming_classic_replay.htm
|
|
129
129
|
'cometd' +
|
|
130
|
-
(needsReplayFix
|
|
130
|
+
(needsReplayFix && this._conn.version === '36.0'
|
|
131
131
|
? '/replay'
|
|
132
132
|
: ''),
|
|
133
133
|
this._conn.version,
|
|
@@ -149,7 +149,7 @@ class Streaming extends events_1.EventEmitter {
|
|
|
149
149
|
}
|
|
150
150
|
/** @private **/
|
|
151
151
|
_getFayeClient(channelName) {
|
|
152
|
-
const isGeneric = channelName.
|
|
152
|
+
const isGeneric = channelName.startsWith('/u/');
|
|
153
153
|
const clientType = isGeneric ? 'generic' : 'pushTopic';
|
|
154
154
|
if (!this._fayeClients[clientType]) {
|
|
155
155
|
this._fayeClients[clientType] = this._createClient(channelName);
|
|
@@ -175,7 +175,7 @@ class Streaming extends events_1.EventEmitter {
|
|
|
175
175
|
* Subscribe topic/channel
|
|
176
176
|
*/
|
|
177
177
|
subscribe(name, listener) {
|
|
178
|
-
const channelName = name.
|
|
178
|
+
const channelName = name.startsWith('/') ? name : '/topic/' + name;
|
|
179
179
|
const fayeClient = this._getFayeClient(channelName);
|
|
180
180
|
return fayeClient.subscribe(channelName, listener);
|
|
181
181
|
}
|
|
@@ -183,7 +183,7 @@ class Streaming extends events_1.EventEmitter {
|
|
|
183
183
|
* Unsubscribe topic
|
|
184
184
|
*/
|
|
185
185
|
unsubscribe(name, subscription) {
|
|
186
|
-
const channelName = name.
|
|
186
|
+
const channelName = name.startsWith('/') ? name : '/topic/' + name;
|
|
187
187
|
const fayeClient = this._getFayeClient(channelName);
|
|
188
188
|
fayeClient.unsubscribe(channelName, subscription);
|
|
189
189
|
return this;
|
|
@@ -297,7 +297,7 @@ async function dumpSchema(schemas, outFile) {
|
|
|
297
297
|
value = value['?'];
|
|
298
298
|
}
|
|
299
299
|
}
|
|
300
|
-
if (typeof value === 'string' && value
|
|
300
|
+
if (typeof value === 'string' && value.startsWith('?')) {
|
|
301
301
|
nillable = true;
|
|
302
302
|
value = value.substring(1);
|
|
303
303
|
}
|
package/lib/browser/request.js
CHANGED
|
@@ -83,7 +83,7 @@ async function startFetchRequest(request, options, input, output, emitter, count
|
|
|
83
83
|
...{ allowHTTP1ForStreamingUpload: true }, // Chrome allows request stream only in HTTP2/QUIC unless this opt-in flag
|
|
84
84
|
}), options.timeout, () => controller?.abort());
|
|
85
85
|
const headers = {};
|
|
86
|
-
// @ts-expect-error
|
|
86
|
+
// @ts-expect-error no .keys()?
|
|
87
87
|
for (const headerName of res.headers.keys()) {
|
|
88
88
|
headers[headerName.toLowerCase()] = res.headers.get(headerName);
|
|
89
89
|
}
|
package/lib/cache.js
CHANGED
package/lib/connection.d.ts
CHANGED
|
@@ -306,8 +306,7 @@ export declare class Connection<S extends Schema = Schema> extends EventEmitter
|
|
|
306
306
|
/**
|
|
307
307
|
* Get SObject instance
|
|
308
308
|
*/
|
|
309
|
-
sobject<N extends SObjectNames<S>>(type: N): SObject<S, N>;
|
|
310
|
-
sobject<N extends SObjectNames<S>>(type: string): SObject<S, N>;
|
|
309
|
+
sobject<N extends SObjectNames<S>>(type: string | N): SObject<S, N>;
|
|
311
310
|
/**
|
|
312
311
|
* Get identity information of current user
|
|
313
312
|
*/
|
|
@@ -352,5 +351,6 @@ export declare class Connection<S extends Schema = Schema> extends EventEmitter
|
|
|
352
351
|
* Module which manages process rules and approval processes
|
|
353
352
|
*/
|
|
354
353
|
process: Process<S>;
|
|
354
|
+
private isLightningInstance;
|
|
355
355
|
}
|
|
356
356
|
export default Connection;
|
package/lib/connection.js
CHANGED
|
@@ -69,7 +69,7 @@ function esc(str) {
|
|
|
69
69
|
*/
|
|
70
70
|
function parseSignedRequest(sr) {
|
|
71
71
|
if (typeof sr === 'string') {
|
|
72
|
-
if (sr
|
|
72
|
+
if (sr.startsWith('{')) {
|
|
73
73
|
// might be JSON
|
|
74
74
|
return JSON.parse(sr);
|
|
75
75
|
} // might be original base64-encoded signed request
|
|
@@ -224,6 +224,9 @@ class Connection extends events_1.EventEmitter {
|
|
|
224
224
|
const { loginUrl, instanceUrl, version, oauth2, maxRequest, logLevel, proxyUrl, httpProxy, } = config;
|
|
225
225
|
this.loginUrl = loginUrl || defaultConnectionConfig.loginUrl;
|
|
226
226
|
this.instanceUrl = instanceUrl || defaultConnectionConfig.instanceUrl;
|
|
227
|
+
if (this.isLightningInstance()) {
|
|
228
|
+
throw new Error('lightning URLs are not valid as instance URLs');
|
|
229
|
+
}
|
|
227
230
|
this.version = version || defaultConnectionConfig.version;
|
|
228
231
|
this.oauth2 =
|
|
229
232
|
oauth2 instanceof oauth2_1.default
|
|
@@ -360,7 +363,7 @@ class Connection extends events_1.EventEmitter {
|
|
|
360
363
|
*/
|
|
361
364
|
async login(username, password) {
|
|
362
365
|
this._refreshDelegate = new session_refresh_delegate_1.default(this, createUsernamePasswordRefreshFn(username, password));
|
|
363
|
-
if (this.oauth2
|
|
366
|
+
if (this.oauth2?.clientId && this.oauth2.clientSecret) {
|
|
364
367
|
return this.loginByOAuth2(username, password);
|
|
365
368
|
}
|
|
366
369
|
return this.loginBySoap(username, password);
|
|
@@ -417,6 +420,17 @@ class Connection extends events_1.EventEmitter {
|
|
|
417
420
|
const faultstring = m && m[1];
|
|
418
421
|
throw new Error(faultstring || response.body);
|
|
419
422
|
}
|
|
423
|
+
// the API will return 200 and a restriced token when using an expired password:
|
|
424
|
+
// https://developer.salesforce.com/docs/atlas.en-us.api.meta/api/sforce_api_calls_login_loginresult.htm
|
|
425
|
+
//
|
|
426
|
+
// we need to throw here to avoid a possible infinite loop with session refresh where:
|
|
427
|
+
// 1. login happens, `this.accessToken` is set to the restricted token
|
|
428
|
+
// 2. requests happen, get back 401
|
|
429
|
+
// 3. trigger session-refresh (username/password login has a default session refresh delegate function)
|
|
430
|
+
// 4. gets stuck refreshing a restricted token
|
|
431
|
+
if (response.body.match(/<passwordExpired>true<\/passwordExpired>/g)) {
|
|
432
|
+
throw new Error('Unable to login because the used password has expired.');
|
|
433
|
+
}
|
|
420
434
|
this._logger.debug(`SOAP response = ${response.body}`);
|
|
421
435
|
m = response.body.match(/<serverUrl>([^<]+)<\/serverUrl>/);
|
|
422
436
|
const serverUrl = m && m[1];
|
|
@@ -607,11 +621,11 @@ class Connection extends events_1.EventEmitter {
|
|
|
607
621
|
* @private
|
|
608
622
|
*/
|
|
609
623
|
_normalizeUrl(url) {
|
|
610
|
-
if (url
|
|
611
|
-
if (url.
|
|
624
|
+
if (url.startsWith('/')) {
|
|
625
|
+
if (url.startsWith(this.instanceUrl + '/services/')) {
|
|
612
626
|
return url;
|
|
613
627
|
}
|
|
614
|
-
if (url.
|
|
628
|
+
if (url.startsWith('/services/')) {
|
|
615
629
|
return this.instanceUrl + url;
|
|
616
630
|
}
|
|
617
631
|
return this._baseUrl() + url;
|
|
@@ -722,13 +736,13 @@ class Connection extends events_1.EventEmitter {
|
|
|
722
736
|
/** @private */
|
|
723
737
|
async _createSingle(type, record, options) {
|
|
724
738
|
const { Id, type: rtype, attributes, ...rec } = record;
|
|
725
|
-
const sobjectType = type ||
|
|
739
|
+
const sobjectType = type || attributes?.type || rtype;
|
|
726
740
|
if (!sobjectType) {
|
|
727
741
|
throw new Error('No SObject Type defined in record');
|
|
728
742
|
}
|
|
729
743
|
const url = [this._baseUrl(), 'sobjects', sobjectType].join('/');
|
|
730
744
|
let contentType, body;
|
|
731
|
-
if (options
|
|
745
|
+
if (options?.multipartFileFields) {
|
|
732
746
|
// Send the record as a multipart/form-data request. Useful for fields containing large binary blobs.
|
|
733
747
|
const form = new form_data_1.default();
|
|
734
748
|
// Extract the fields requested to be sent separately from the JSON
|
|
@@ -785,7 +799,7 @@ class Connection extends events_1.EventEmitter {
|
|
|
785
799
|
}
|
|
786
800
|
const _records = records.map((record) => {
|
|
787
801
|
const { Id, type: rtype, attributes, ...rec } = record;
|
|
788
|
-
const sobjectType = type ||
|
|
802
|
+
const sobjectType = type || attributes?.type || rtype;
|
|
789
803
|
if (!sobjectType) {
|
|
790
804
|
throw new Error('No SObject Type defined in record');
|
|
791
805
|
}
|
|
@@ -828,7 +842,7 @@ class Connection extends events_1.EventEmitter {
|
|
|
828
842
|
if (!id) {
|
|
829
843
|
throw new Error('Record id is not found in record.');
|
|
830
844
|
}
|
|
831
|
-
const sobjectType = type ||
|
|
845
|
+
const sobjectType = type || attributes?.type || rtype;
|
|
832
846
|
if (!sobjectType) {
|
|
833
847
|
throw new Error('No SObject Type defined in record');
|
|
834
848
|
}
|
|
@@ -875,7 +889,7 @@ class Connection extends events_1.EventEmitter {
|
|
|
875
889
|
if (!id) {
|
|
876
890
|
throw new Error('Record id is not found in record.');
|
|
877
891
|
}
|
|
878
|
-
const sobjectType = type ||
|
|
892
|
+
const sobjectType = type || attributes?.type || rtype;
|
|
879
893
|
if (!sobjectType) {
|
|
880
894
|
throw new Error('No SObject Type defined in record');
|
|
881
895
|
}
|
|
@@ -1018,8 +1032,7 @@ class Connection extends events_1.EventEmitter {
|
|
|
1018
1032
|
return body;
|
|
1019
1033
|
}
|
|
1020
1034
|
sobject(type) {
|
|
1021
|
-
const so = this.sobjects[type] ||
|
|
1022
|
-
new sobject_1.default(this, type);
|
|
1035
|
+
const so = this.sobjects[type] || new sobject_1.default(this, type);
|
|
1023
1036
|
this.sobjects[type] = so;
|
|
1024
1037
|
return so;
|
|
1025
1038
|
}
|
|
@@ -1027,7 +1040,7 @@ class Connection extends events_1.EventEmitter {
|
|
|
1027
1040
|
* Get identity information of current user
|
|
1028
1041
|
*/
|
|
1029
1042
|
async identity(options = {}) {
|
|
1030
|
-
let url = this.userInfo
|
|
1043
|
+
let url = this.userInfo?.url;
|
|
1031
1044
|
if (!url) {
|
|
1032
1045
|
const res = await this.request({
|
|
1033
1046
|
method: 'GET',
|
|
@@ -1148,6 +1161,11 @@ class Connection extends events_1.EventEmitter {
|
|
|
1148
1161
|
* Module which manages process rules and approval processes
|
|
1149
1162
|
*/
|
|
1150
1163
|
process = new process_1.default(this);
|
|
1164
|
+
isLightningInstance() {
|
|
1165
|
+
return (this.instanceUrl.includes('.lightning.force.com') ||
|
|
1166
|
+
this.instanceUrl.includes('.lightning.crmforce.mil') ||
|
|
1167
|
+
this.instanceUrl.includes('.lightning.sfcrmapps.cn'));
|
|
1168
|
+
}
|
|
1151
1169
|
}
|
|
1152
1170
|
exports.Connection = Connection;
|
|
1153
1171
|
exports.default = Connection;
|
package/lib/http-api.js
CHANGED
|
@@ -178,6 +178,7 @@ class HttpApi extends events_1.EventEmitter {
|
|
|
178
178
|
/**
|
|
179
179
|
* @private
|
|
180
180
|
*/
|
|
181
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
181
182
|
async parseResponseBody(response) {
|
|
182
183
|
const contentType = this.getResponseContentType(response) || '';
|
|
183
184
|
const parseBody = /^(text|application)\/xml(;|$)/.test(contentType)
|
package/lib/oauth2.js
CHANGED
|
@@ -101,8 +101,7 @@ class OAuth2 {
|
|
|
101
101
|
if (this.codeVerifier) {
|
|
102
102
|
// code verifier must be a base 64 url encoded hash of 128 bytes of random data. Our random data is also
|
|
103
103
|
// base 64 url encoded. See Connection.create();
|
|
104
|
-
|
|
105
|
-
params.code_challenge = codeChallenge;
|
|
104
|
+
params.code_challenge = base64UrlEscape((0, crypto_1.createHash)('sha256').update(this.codeVerifier).digest('base64'));
|
|
106
105
|
}
|
|
107
106
|
const _params = {
|
|
108
107
|
...params,
|
|
@@ -111,7 +110,7 @@ class OAuth2 {
|
|
|
111
110
|
redirect_uri: this.redirectUri,
|
|
112
111
|
};
|
|
113
112
|
return (this.authzServiceUrl +
|
|
114
|
-
(this.authzServiceUrl.
|
|
113
|
+
(this.authzServiceUrl.includes('?') ? '&' : '?') +
|
|
115
114
|
querystring_1.default.stringify(_params));
|
|
116
115
|
}
|
|
117
116
|
/**
|
package/lib/query.d.ts
CHANGED
|
@@ -32,9 +32,9 @@ type ConditionSet<R extends Record> = {
|
|
|
32
32
|
[K in keyof R]?: CondValue<R[K]>;
|
|
33
33
|
};
|
|
34
34
|
export type QueryCondition<S extends Schema, N extends SObjectNames<S>> = {
|
|
35
|
-
$or: QueryCondition<S, N
|
|
35
|
+
$or: Array<QueryCondition<S, N>>;
|
|
36
36
|
} | {
|
|
37
|
-
$and: QueryCondition<S, N
|
|
37
|
+
$and: Array<QueryCondition<S, N>>;
|
|
38
38
|
} | ConditionSet<SObjectRecord<S, N>>;
|
|
39
39
|
export type QuerySort<S extends Schema, N extends SObjectNames<S>, R extends SObjectRecord<S, N> = SObjectRecord<S, N>> = {
|
|
40
40
|
[K in keyof R]?: SortDir;
|
|
@@ -110,7 +110,7 @@ export declare class Query<S extends Schema, N extends SObjectNames<S>, R extend
|
|
|
110
110
|
_soql: Optional<string>;
|
|
111
111
|
_locator: Optional<string>;
|
|
112
112
|
_config: SOQLQueryConfig;
|
|
113
|
-
_children: SubQuery<S, N, R, QRT, any, any, any
|
|
113
|
+
_children: Array<SubQuery<S, N, R, QRT, any, any, any>>;
|
|
114
114
|
_options: QueryOptions;
|
|
115
115
|
_executed: boolean;
|
|
116
116
|
_finished: boolean;
|
|
@@ -149,10 +149,8 @@ export declare class Query<S extends Schema, N extends SObjectNames<S>, R extend
|
|
|
149
149
|
/**
|
|
150
150
|
* Set query sort with direction
|
|
151
151
|
*/
|
|
152
|
-
sort(sort: QuerySort<S, N>): this;
|
|
153
|
-
sort(sort: string): this;
|
|
154
|
-
sort(sort: SObjectFieldNames<S, N>, dir: SortDir): this;
|
|
155
|
-
sort(sort: string, dir: SortDir): this;
|
|
152
|
+
sort(sort: QuerySort<S, N> | string): this;
|
|
153
|
+
sort(sort: SObjectFieldNames<S, N> | string, dir: SortDir): this;
|
|
156
154
|
/**
|
|
157
155
|
* Synonym of Query#sort()
|
|
158
156
|
*/
|
|
@@ -322,9 +320,7 @@ export declare class SubQuery<S extends Schema, PN extends SObjectNames<S>, PR e
|
|
|
322
320
|
* Set query sort with direction
|
|
323
321
|
*/
|
|
324
322
|
sort(sort: QuerySort<S, CN>): this;
|
|
325
|
-
sort(sort: string): this;
|
|
326
|
-
sort(sort: SObjectFieldNames<S, CN>, dir: SortDir): this;
|
|
327
|
-
sort(sort: string, dir: SortDir): this;
|
|
323
|
+
sort(sort: string | SObjectFieldNames<S, CN>, dir: SortDir): this;
|
|
328
324
|
/**
|
|
329
325
|
* Synonym of SubQuery#sort()
|
|
330
326
|
*/
|
package/lib/query.js
CHANGED
|
@@ -85,7 +85,7 @@ class Query extends events_1.EventEmitter {
|
|
|
85
85
|
: locator;
|
|
86
86
|
}
|
|
87
87
|
else {
|
|
88
|
-
this._logger.debug(`config is QueryConfig: ${config}`);
|
|
88
|
+
this._logger.debug(`config is QueryConfig: ${JSON.stringify(config)}`);
|
|
89
89
|
const { fields, includes, sort, ..._config } = config;
|
|
90
90
|
this._config = _config;
|
|
91
91
|
this.select(fields);
|
|
@@ -383,6 +383,7 @@ class Query extends events_1.EventEmitter {
|
|
|
383
383
|
this._finished ||
|
|
384
384
|
data.done ||
|
|
385
385
|
!autoFetch ||
|
|
386
|
+
this.records.length === maxFetch ||
|
|
386
387
|
// this is what the response looks like when there are no results
|
|
387
388
|
(data.records.length === 0 && data.done === undefined);
|
|
388
389
|
// streaming record instances
|
package/lib/registry/base.js
CHANGED
|
@@ -20,6 +20,7 @@ class BaseRegistry {
|
|
|
20
20
|
return (this._registryConfig.connections ||
|
|
21
21
|
(this._registryConfig.connections = {}));
|
|
22
22
|
}
|
|
23
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
23
24
|
async getConnectionNames() {
|
|
24
25
|
return Object.keys(this._getConnections());
|
|
25
26
|
}
|
|
@@ -45,6 +46,7 @@ class BaseRegistry {
|
|
|
45
46
|
}
|
|
46
47
|
return connConfig_;
|
|
47
48
|
}
|
|
49
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
48
50
|
async saveConnectionConfig(name, connConfig) {
|
|
49
51
|
const connections = this._getConnections();
|
|
50
52
|
const { oauth2, ...connConfig_ } = connConfig;
|
|
@@ -70,23 +72,28 @@ class BaseRegistry {
|
|
|
70
72
|
}
|
|
71
73
|
return null;
|
|
72
74
|
}
|
|
75
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
73
76
|
async setDefaultConnection(name) {
|
|
74
77
|
this._registryConfig['default'] = name;
|
|
75
78
|
this._saveConfig();
|
|
76
79
|
}
|
|
80
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
77
81
|
async removeConnectionConfig(name) {
|
|
78
82
|
const connections = this._getConnections();
|
|
79
83
|
delete connections[name];
|
|
80
84
|
this._saveConfig();
|
|
81
85
|
}
|
|
86
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
82
87
|
async getClientConfig(name) {
|
|
83
88
|
const clients = this._getClients();
|
|
84
89
|
const clientConfig = clients[name];
|
|
85
90
|
return clientConfig && { ...clientConfig };
|
|
86
91
|
}
|
|
92
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
87
93
|
async getClientNames() {
|
|
88
94
|
return Object.keys(this._getClients());
|
|
89
95
|
}
|
|
96
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
90
97
|
async registerClientConfig(name, clientConfig) {
|
|
91
98
|
const clients = this._getClients();
|
|
92
99
|
clients[name] = clientConfig;
|