@jsforce/jsforce-node 0.0.1 → 3.0.0-next.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/LICENSE +22 -0
- package/README.md +54 -0
- package/index.d.ts +4 -0
- package/index.js +1 -0
- package/lib/VERSION.d.ts +2 -0
- package/lib/VERSION.js +3 -0
- package/lib/api/analytics/types.d.ts +509 -0
- package/lib/api/analytics/types.js +2 -0
- package/lib/api/analytics.d.ts +163 -0
- package/lib/api/analytics.js +342 -0
- package/lib/api/apex.d.ts +44 -0
- package/lib/api/apex.js +86 -0
- package/lib/api/bulk.d.ts +444 -0
- package/lib/api/bulk.js +1372 -0
- package/lib/api/chatter.d.ts +133 -0
- package/lib/api/chatter.js +248 -0
- package/lib/api/metadata/schema.d.ts +16117 -0
- package/lib/api/metadata/schema.js +9094 -0
- package/lib/api/metadata.d.ts +189 -0
- package/lib/api/metadata.js +406 -0
- package/lib/api/soap/schema.d.ts +3167 -0
- package/lib/api/soap/schema.js +1787 -0
- package/lib/api/soap.d.ts +76 -0
- package/lib/api/soap.js +155 -0
- package/lib/api/streaming/extension.d.ts +94 -0
- package/lib/api/streaming/extension.js +151 -0
- package/lib/api/streaming.d.ts +160 -0
- package/lib/api/streaming.js +252 -0
- package/lib/api/tooling.d.ts +284 -0
- package/lib/api/tooling.js +202 -0
- package/lib/api/wsdl/wsdl2schema.d.ts +1 -0
- package/lib/api/wsdl/wsdl2schema.js +354 -0
- package/lib/browser/canvas.d.ts +12 -0
- package/lib/browser/canvas.js +77 -0
- package/lib/browser/client.d.ts +82 -0
- package/lib/browser/client.js +244 -0
- package/lib/browser/jsonp.d.ts +12 -0
- package/lib/browser/jsonp.js +69 -0
- package/lib/browser/registry.d.ts +3 -0
- package/lib/browser/registry.js +5 -0
- package/lib/browser/request.d.ts +10 -0
- package/lib/browser/request.js +202 -0
- package/lib/cache.d.ts +74 -0
- package/lib/cache.js +159 -0
- package/lib/connection.d.ts +355 -0
- package/lib/connection.js +1153 -0
- package/lib/core.d.ts +17 -0
- package/lib/core.js +55 -0
- package/lib/csv.d.ts +23 -0
- package/lib/csv.js +35 -0
- package/lib/date.d.ts +82 -0
- package/lib/date.js +201 -0
- package/lib/http-api.d.ts +75 -0
- package/lib/http-api.js +257 -0
- package/lib/index.d.ts +12 -0
- package/lib/index.js +31 -0
- package/lib/jsforce.d.ts +26 -0
- package/lib/jsforce.js +67 -0
- package/lib/jwtOAuth2.d.ts +8 -0
- package/lib/jwtOAuth2.js +23 -0
- package/lib/oauth2.d.ts +92 -0
- package/lib/oauth2.js +245 -0
- package/lib/process.d.ts +157 -0
- package/lib/process.js +143 -0
- package/lib/query.d.ts +341 -0
- package/lib/query.js +817 -0
- package/lib/quick-action.d.ts +44 -0
- package/lib/quick-action.js +46 -0
- package/lib/record-reference.d.ts +46 -0
- package/lib/record-reference.js +65 -0
- package/lib/record-stream.d.ts +83 -0
- package/lib/record-stream.js +233 -0
- package/lib/registry/base.d.ts +43 -0
- package/lib/registry/base.js +96 -0
- package/lib/registry/empty.d.ts +7 -0
- package/lib/registry/empty.js +13 -0
- package/lib/registry/file.d.ts +11 -0
- package/lib/registry/file.js +51 -0
- package/lib/registry/index.d.ts +8 -0
- package/lib/registry/index.js +21 -0
- package/lib/registry/sfdx.d.ts +56 -0
- package/lib/registry/sfdx.js +133 -0
- package/lib/registry/types.d.ts +47 -0
- package/lib/registry/types.js +2 -0
- package/lib/request-helper.d.ts +23 -0
- package/lib/request-helper.js +102 -0
- package/lib/request.d.ts +11 -0
- package/lib/request.js +75 -0
- package/lib/session-refresh-delegate.d.ts +31 -0
- package/lib/session-refresh-delegate.js +69 -0
- package/lib/soap.d.ts +60 -0
- package/lib/soap.js +246 -0
- package/lib/sobject.d.ts +258 -0
- package/lib/sobject.js +376 -0
- package/lib/soql-builder.d.ts +25 -0
- package/lib/soql-builder.js +226 -0
- package/lib/transport.d.ts +63 -0
- package/lib/transport.js +175 -0
- package/lib/types/common.d.ts +560 -0
- package/lib/types/common.js +2 -0
- package/lib/types/index.d.ts +7 -0
- package/lib/types/index.js +23 -0
- package/lib/types/projection.d.ts +26 -0
- package/lib/types/projection.js +2 -0
- package/lib/types/record.d.ts +44 -0
- package/lib/types/record.js +2 -0
- package/lib/types/schema.d.ts +50 -0
- package/lib/types/schema.js +2 -0
- package/lib/types/soap.d.ts +43 -0
- package/lib/types/soap.js +2 -0
- package/lib/types/standard-schema.d.ts +16199 -0
- package/lib/types/standard-schema.js +2 -0
- package/lib/types/util.d.ts +7 -0
- package/lib/types/util.js +2 -0
- package/lib/util/formatter.d.ts +8 -0
- package/lib/util/formatter.js +24 -0
- package/lib/util/function.d.ts +32 -0
- package/lib/util/function.js +52 -0
- package/lib/util/logger.d.ts +29 -0
- package/lib/util/logger.js +102 -0
- package/lib/util/promise.d.ts +19 -0
- package/lib/util/promise.js +25 -0
- package/lib/util/stream.d.ts +12 -0
- package/lib/util/stream.js +88 -0
- package/package.json +260 -6
- package/typings/faye/index.d.ts +16 -0
- package/typings/index.d.ts +1 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
/**
|
|
3
|
+
* @file Browser client connection management class
|
|
4
|
+
* @author Shinichi Tomita <shinichi.tomita@gmail.com>
|
|
5
|
+
*/
|
|
6
|
+
import { EventEmitter } from 'events';
|
|
7
|
+
import Connection, { ConnectionConfig } from '../connection';
|
|
8
|
+
import { TokenResponse } from '../oauth2';
|
|
9
|
+
/**
|
|
10
|
+
*
|
|
11
|
+
*/
|
|
12
|
+
export type LoginOptions = {
|
|
13
|
+
scope?: string;
|
|
14
|
+
size?: {
|
|
15
|
+
width: number;
|
|
16
|
+
height: number;
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
*
|
|
21
|
+
*/
|
|
22
|
+
export declare class BrowserClient extends EventEmitter {
|
|
23
|
+
_prefix: string;
|
|
24
|
+
_config: ConnectionConfig | undefined;
|
|
25
|
+
_connection: Connection | undefined;
|
|
26
|
+
/**
|
|
27
|
+
*
|
|
28
|
+
*/
|
|
29
|
+
constructor(prefix?: string);
|
|
30
|
+
get connection(): Connection;
|
|
31
|
+
/**
|
|
32
|
+
*
|
|
33
|
+
*/
|
|
34
|
+
init(config: ConnectionConfig): void;
|
|
35
|
+
/**
|
|
36
|
+
*
|
|
37
|
+
*/
|
|
38
|
+
login(options?: LoginOptions): Promise<{
|
|
39
|
+
status: string;
|
|
40
|
+
}>;
|
|
41
|
+
/**
|
|
42
|
+
*
|
|
43
|
+
*/
|
|
44
|
+
isLoggedIn(): boolean;
|
|
45
|
+
/**
|
|
46
|
+
*
|
|
47
|
+
*/
|
|
48
|
+
logout(): void;
|
|
49
|
+
/**
|
|
50
|
+
* @private
|
|
51
|
+
*/
|
|
52
|
+
_getTokens(): {
|
|
53
|
+
accessToken: string | null;
|
|
54
|
+
instanceUrl: string | null;
|
|
55
|
+
userInfo: {
|
|
56
|
+
id: string;
|
|
57
|
+
organizationId: string;
|
|
58
|
+
url: string;
|
|
59
|
+
} | undefined;
|
|
60
|
+
} | null;
|
|
61
|
+
/**
|
|
62
|
+
* @private
|
|
63
|
+
*/
|
|
64
|
+
_storeTokens(params: TokenResponse): void;
|
|
65
|
+
/**
|
|
66
|
+
* @private
|
|
67
|
+
*/
|
|
68
|
+
_removeTokens(): void;
|
|
69
|
+
/**
|
|
70
|
+
* @private
|
|
71
|
+
*/
|
|
72
|
+
_getError(): any;
|
|
73
|
+
/**
|
|
74
|
+
* @private
|
|
75
|
+
*/
|
|
76
|
+
_storeError(err: any): void;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
*
|
|
80
|
+
*/
|
|
81
|
+
declare const client: BrowserClient;
|
|
82
|
+
export default client;
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.BrowserClient = void 0;
|
|
7
|
+
/**
|
|
8
|
+
* @file Browser client connection management class
|
|
9
|
+
* @author Shinichi Tomita <shinichi.tomita@gmail.com>
|
|
10
|
+
*/
|
|
11
|
+
const events_1 = require("events");
|
|
12
|
+
const connection_1 = __importDefault(require("../connection"));
|
|
13
|
+
const oauth2_1 = __importDefault(require("../oauth2"));
|
|
14
|
+
/**
|
|
15
|
+
* @private
|
|
16
|
+
*/
|
|
17
|
+
function popupWin(url, w, h) {
|
|
18
|
+
const left = screen.width / 2 - w / 2;
|
|
19
|
+
const top = screen.height / 2 - h / 2;
|
|
20
|
+
return window.open(url, undefined, `location=yes,toolbar=no,status=no,menubar=no,width=${w},height=${h},top=${top},left=${left}`);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* @private
|
|
24
|
+
*/
|
|
25
|
+
function handleCallbackResponse() {
|
|
26
|
+
const res = checkCallbackResponse();
|
|
27
|
+
const state = localStorage.getItem('jsforce_state');
|
|
28
|
+
if (res && state && res.body.get('state') === state) {
|
|
29
|
+
localStorage.removeItem('jsforce_state');
|
|
30
|
+
const [prefix, promptType] = state.split('.');
|
|
31
|
+
const cli = new BrowserClient(prefix);
|
|
32
|
+
if (res.success) {
|
|
33
|
+
cli._storeTokens(Object.fromEntries(res.body));
|
|
34
|
+
location.hash = '';
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
cli._storeError(res.body);
|
|
38
|
+
}
|
|
39
|
+
if (promptType === 'popup') {
|
|
40
|
+
window.close();
|
|
41
|
+
}
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* @private
|
|
47
|
+
*/
|
|
48
|
+
function checkCallbackResponse() {
|
|
49
|
+
let params;
|
|
50
|
+
if (window.location.hash) {
|
|
51
|
+
params = new URLSearchParams(window.location.hash.substring(1));
|
|
52
|
+
if (params.get('access_token')) {
|
|
53
|
+
return { success: true, body: params };
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
else if (window.location.search) {
|
|
57
|
+
params = new URLSearchParams(window.location.search.substring(1));
|
|
58
|
+
if (params.get('error')) {
|
|
59
|
+
return { success: false, body: params };
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
*
|
|
65
|
+
*/
|
|
66
|
+
const DEFAULT_POPUP_WIN_WIDTH = 912;
|
|
67
|
+
const DEFAULT_POPUP_WIN_HEIGHT = 513;
|
|
68
|
+
/** @private **/
|
|
69
|
+
let clientIdx = 0;
|
|
70
|
+
/**
|
|
71
|
+
*
|
|
72
|
+
*/
|
|
73
|
+
class BrowserClient extends events_1.EventEmitter {
|
|
74
|
+
_prefix;
|
|
75
|
+
_config;
|
|
76
|
+
_connection;
|
|
77
|
+
/**
|
|
78
|
+
*
|
|
79
|
+
*/
|
|
80
|
+
constructor(prefix) {
|
|
81
|
+
super();
|
|
82
|
+
this._prefix = prefix || 'jsforce' + clientIdx++;
|
|
83
|
+
}
|
|
84
|
+
get connection() {
|
|
85
|
+
if (!this._connection) {
|
|
86
|
+
this._connection = new connection_1.default(this._config);
|
|
87
|
+
}
|
|
88
|
+
return this._connection;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
*
|
|
92
|
+
*/
|
|
93
|
+
init(config) {
|
|
94
|
+
if (handleCallbackResponse()) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
this._config = config;
|
|
98
|
+
const tokens = this._getTokens();
|
|
99
|
+
if (tokens) {
|
|
100
|
+
this.connection._establish(tokens);
|
|
101
|
+
setTimeout(() => {
|
|
102
|
+
this.emit('connect', this.connection);
|
|
103
|
+
}, 10);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
*
|
|
108
|
+
*/
|
|
109
|
+
login(options = {}) {
|
|
110
|
+
const { scope, size } = options;
|
|
111
|
+
const oauth2 = new oauth2_1.default(this._config ?? {});
|
|
112
|
+
const rand = Math.random().toString(36).substring(2);
|
|
113
|
+
const state = [this._prefix, 'popup', rand].join('.');
|
|
114
|
+
localStorage.setItem('jsforce_state', state);
|
|
115
|
+
const authzUrl = oauth2.getAuthorizationUrl({
|
|
116
|
+
response_type: 'token',
|
|
117
|
+
state,
|
|
118
|
+
...(scope ? { scope } : {}),
|
|
119
|
+
});
|
|
120
|
+
const pw = popupWin(authzUrl, size?.width ?? DEFAULT_POPUP_WIN_WIDTH, size?.height ?? DEFAULT_POPUP_WIN_HEIGHT);
|
|
121
|
+
return new Promise((resolve, reject) => {
|
|
122
|
+
if (!pw) {
|
|
123
|
+
const state = [this._prefix, 'redirect', rand].join('.');
|
|
124
|
+
localStorage.setItem('jsforce_state', state);
|
|
125
|
+
const authzUrl = oauth2.getAuthorizationUrl({
|
|
126
|
+
response_type: 'token',
|
|
127
|
+
state,
|
|
128
|
+
...(scope ? { scope } : {}),
|
|
129
|
+
});
|
|
130
|
+
location.href = authzUrl;
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
this._removeTokens();
|
|
134
|
+
const pid = setInterval(() => {
|
|
135
|
+
try {
|
|
136
|
+
if (!pw || pw.closed) {
|
|
137
|
+
clearInterval(pid);
|
|
138
|
+
const tokens = this._getTokens();
|
|
139
|
+
if (tokens) {
|
|
140
|
+
this.connection._establish(tokens);
|
|
141
|
+
this.emit('connect', this.connection);
|
|
142
|
+
resolve({ status: 'connect' });
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
const err = this._getError();
|
|
146
|
+
if (err) {
|
|
147
|
+
reject(new Error(err.error + ': ' + err.error_description));
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
resolve({ status: 'cancel' });
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
catch (e) {
|
|
156
|
+
//
|
|
157
|
+
}
|
|
158
|
+
}, 1000);
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
*
|
|
163
|
+
*/
|
|
164
|
+
isLoggedIn() {
|
|
165
|
+
return !!this.connection.accessToken;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
*
|
|
169
|
+
*/
|
|
170
|
+
logout() {
|
|
171
|
+
this.connection.logout();
|
|
172
|
+
this._removeTokens();
|
|
173
|
+
this.emit('disconnect');
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* @private
|
|
177
|
+
*/
|
|
178
|
+
_getTokens() {
|
|
179
|
+
const regexp = new RegExp('(^|;\\s*)' + this._prefix + '_loggedin=true(;|$)');
|
|
180
|
+
if (document.cookie.match(regexp)) {
|
|
181
|
+
const issuedAt = Number(localStorage.getItem(this._prefix + '_issued_at'));
|
|
182
|
+
// 2 hours
|
|
183
|
+
if (Date.now() < issuedAt + 2 * 60 * 60 * 1000) {
|
|
184
|
+
let userInfo;
|
|
185
|
+
const idUrl = localStorage.getItem(this._prefix + '_id');
|
|
186
|
+
if (idUrl) {
|
|
187
|
+
const [id, organizationId] = idUrl.split('/').reverse();
|
|
188
|
+
userInfo = { id, organizationId, url: idUrl };
|
|
189
|
+
}
|
|
190
|
+
return {
|
|
191
|
+
accessToken: localStorage.getItem(this._prefix + '_access_token'),
|
|
192
|
+
instanceUrl: localStorage.getItem(this._prefix + '_instance_url'),
|
|
193
|
+
userInfo,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* @private
|
|
201
|
+
*/
|
|
202
|
+
_storeTokens(params) {
|
|
203
|
+
localStorage.setItem(this._prefix + '_access_token', params.access_token);
|
|
204
|
+
localStorage.setItem(this._prefix + '_instance_url', params.instance_url);
|
|
205
|
+
localStorage.setItem(this._prefix + '_issued_at', params.issued_at);
|
|
206
|
+
localStorage.setItem(this._prefix + '_id', params.id);
|
|
207
|
+
document.cookie = this._prefix + '_loggedin=true;';
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* @private
|
|
211
|
+
*/
|
|
212
|
+
_removeTokens() {
|
|
213
|
+
localStorage.removeItem(this._prefix + '_access_token');
|
|
214
|
+
localStorage.removeItem(this._prefix + '_instance_url');
|
|
215
|
+
localStorage.removeItem(this._prefix + '_issued_at');
|
|
216
|
+
localStorage.removeItem(this._prefix + '_id');
|
|
217
|
+
document.cookie = this._prefix + '_loggedin=';
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* @private
|
|
221
|
+
*/
|
|
222
|
+
_getError() {
|
|
223
|
+
try {
|
|
224
|
+
const err = JSON.parse(localStorage.getItem(this._prefix + '_error') ?? '');
|
|
225
|
+
localStorage.removeItem(this._prefix + '_error');
|
|
226
|
+
return err;
|
|
227
|
+
}
|
|
228
|
+
catch (e) {
|
|
229
|
+
//
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* @private
|
|
234
|
+
*/
|
|
235
|
+
_storeError(err) {
|
|
236
|
+
localStorage.setItem(this._prefix + '_error', JSON.stringify(err));
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
exports.BrowserClient = BrowserClient;
|
|
240
|
+
/**
|
|
241
|
+
*
|
|
242
|
+
*/
|
|
243
|
+
const client = new BrowserClient();
|
|
244
|
+
exports.default = client;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
/**
|
|
3
|
+
*
|
|
4
|
+
*/
|
|
5
|
+
import { Transform } from 'stream';
|
|
6
|
+
import { HttpRequest } from '../types';
|
|
7
|
+
declare function createRequest(jsonpParam?: string, timeout?: number): (params: HttpRequest) => Transform;
|
|
8
|
+
declare const _default: {
|
|
9
|
+
supported: boolean;
|
|
10
|
+
createRequest: typeof createRequest;
|
|
11
|
+
};
|
|
12
|
+
export default _default;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
/**
|
|
4
|
+
*
|
|
5
|
+
*/
|
|
6
|
+
const stream_1 = require("stream");
|
|
7
|
+
let _index = 0;
|
|
8
|
+
async function processJsonpRequest(params, jsonpParam, timeout) {
|
|
9
|
+
if (params.method.toUpperCase() !== 'GET') {
|
|
10
|
+
throw new Error('JSONP only supports GET request.');
|
|
11
|
+
}
|
|
12
|
+
_index += 1;
|
|
13
|
+
const cbFuncName = `_jsforce_jsonpCallback_${_index}`;
|
|
14
|
+
const callbacks = window;
|
|
15
|
+
let url = params.url;
|
|
16
|
+
url += url.indexOf('?') > 0 ? '&' : '?';
|
|
17
|
+
url += `${jsonpParam}=${cbFuncName}`;
|
|
18
|
+
const script = document.createElement('script');
|
|
19
|
+
script.type = 'text/javascript';
|
|
20
|
+
script.src = url;
|
|
21
|
+
if (document.documentElement) {
|
|
22
|
+
document.documentElement.appendChild(script);
|
|
23
|
+
}
|
|
24
|
+
let pid;
|
|
25
|
+
try {
|
|
26
|
+
const res = await new Promise((resolve, reject) => {
|
|
27
|
+
pid = setTimeout(() => {
|
|
28
|
+
reject(new Error('JSONP call time out.'));
|
|
29
|
+
}, timeout);
|
|
30
|
+
callbacks[cbFuncName] = resolve;
|
|
31
|
+
});
|
|
32
|
+
return {
|
|
33
|
+
statusCode: 200,
|
|
34
|
+
headers: { 'content-type': 'application/json' },
|
|
35
|
+
body: JSON.stringify(res),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
finally {
|
|
39
|
+
clearTimeout(pid);
|
|
40
|
+
if (document.documentElement) {
|
|
41
|
+
document.documentElement.removeChild(script);
|
|
42
|
+
}
|
|
43
|
+
delete callbacks[cbFuncName];
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function createRequest(jsonpParam = 'callback', timeout = 10000) {
|
|
47
|
+
return (params) => {
|
|
48
|
+
const stream = new stream_1.Transform({
|
|
49
|
+
transform(chunk, encoding, callback) {
|
|
50
|
+
callback();
|
|
51
|
+
},
|
|
52
|
+
flush() {
|
|
53
|
+
(async () => {
|
|
54
|
+
const response = await processJsonpRequest(params, jsonpParam, timeout);
|
|
55
|
+
stream.emit('response', response);
|
|
56
|
+
stream.emit('complete', response);
|
|
57
|
+
stream.push(response.body);
|
|
58
|
+
stream.push(null);
|
|
59
|
+
})();
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
stream.end();
|
|
63
|
+
return stream;
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
exports.default = {
|
|
67
|
+
supported: typeof window !== 'undefined' && typeof document !== 'undefined',
|
|
68
|
+
createRequest,
|
|
69
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { HttpRequest, HttpRequestOptions } from '../types';
|
|
3
|
+
/**
|
|
4
|
+
*
|
|
5
|
+
*/
|
|
6
|
+
export declare function setDefaults(defaults_: HttpRequestOptions): void;
|
|
7
|
+
/**
|
|
8
|
+
*
|
|
9
|
+
*/
|
|
10
|
+
export default function request(req: HttpRequest, options_?: HttpRequestOptions): import("stream").Duplex;
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.setDefaults = void 0;
|
|
4
|
+
const request_helper_1 = require("../request-helper");
|
|
5
|
+
const stream_1 = require("../util/stream");
|
|
6
|
+
/**
|
|
7
|
+
* As the request streming is not yet supported on major browsers,
|
|
8
|
+
* it is set to false for now.
|
|
9
|
+
*/
|
|
10
|
+
const supportsReadableStream = false;
|
|
11
|
+
/*
|
|
12
|
+
(async () => {
|
|
13
|
+
try {
|
|
14
|
+
if (
|
|
15
|
+
typeof fetch === 'function' &&
|
|
16
|
+
typeof Request === 'function' &&
|
|
17
|
+
typeof ReadableStream === 'function'
|
|
18
|
+
) {
|
|
19
|
+
// this feature detection requires dummy POST request
|
|
20
|
+
const req = new Request('data:text/plain,', {
|
|
21
|
+
method: 'POST',
|
|
22
|
+
body: new ReadableStream(),
|
|
23
|
+
});
|
|
24
|
+
// if it has content-type header it doesn't regard body as stream
|
|
25
|
+
if (req.headers.has('Content-Type')) {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
await (await fetch(req)).text();
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
} catch (e) {
|
|
32
|
+
// error might occur in env with CSP without connect-src data:
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
return false;
|
|
36
|
+
})();
|
|
37
|
+
*/
|
|
38
|
+
/**
|
|
39
|
+
*
|
|
40
|
+
*/
|
|
41
|
+
function toWhatwgReadableStream(ins) {
|
|
42
|
+
return new ReadableStream({
|
|
43
|
+
start(controller) {
|
|
44
|
+
ins.on('data', (chunk) => controller.enqueue(chunk));
|
|
45
|
+
ins.on('end', () => controller.close());
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
*
|
|
51
|
+
*/
|
|
52
|
+
async function readWhatwgReadableStream(rs, outs) {
|
|
53
|
+
const reader = rs.getReader();
|
|
54
|
+
async function readAndWrite() {
|
|
55
|
+
const { done, value } = await reader.read();
|
|
56
|
+
if (done) {
|
|
57
|
+
outs.end();
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
outs.write(value);
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
while (await readAndWrite())
|
|
64
|
+
;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
*
|
|
68
|
+
*/
|
|
69
|
+
async function startFetchRequest(request, options, input, output, emitter, counter = 0) {
|
|
70
|
+
const { followRedirect } = options;
|
|
71
|
+
const { url, body: reqBody, ...rreq } = request;
|
|
72
|
+
const body = input && /^(post|put|patch)$/i.test(request.method)
|
|
73
|
+
? supportsReadableStream
|
|
74
|
+
? toWhatwgReadableStream(input)
|
|
75
|
+
: await (0, stream_1.readAll)(input)
|
|
76
|
+
: undefined;
|
|
77
|
+
const controller = typeof AbortController !== 'undefined' ? new AbortController() : undefined;
|
|
78
|
+
const res = await (0, request_helper_1.executeWithTimeout)(() => fetch(url, {
|
|
79
|
+
...rreq,
|
|
80
|
+
...(body ? { body } : {}),
|
|
81
|
+
redirect: 'manual',
|
|
82
|
+
...(controller ? { signal: controller.signal } : {}),
|
|
83
|
+
...{ allowHTTP1ForStreamingUpload: true }, // Chrome allows request stream only in HTTP2/QUIC unless this opt-in flag
|
|
84
|
+
}), options.timeout, () => controller?.abort());
|
|
85
|
+
const headers = {};
|
|
86
|
+
// @ts-expect-error
|
|
87
|
+
for (const headerName of res.headers.keys()) {
|
|
88
|
+
headers[headerName.toLowerCase()] = res.headers.get(headerName);
|
|
89
|
+
}
|
|
90
|
+
const response = {
|
|
91
|
+
statusCode: res.status,
|
|
92
|
+
headers,
|
|
93
|
+
};
|
|
94
|
+
if (followRedirect && (0, request_helper_1.isRedirect)(response.statusCode)) {
|
|
95
|
+
try {
|
|
96
|
+
(0, request_helper_1.performRedirectRequest)(request, response, followRedirect, counter, (req) => startFetchRequest(req, options, undefined, output, emitter, counter + 1));
|
|
97
|
+
}
|
|
98
|
+
catch (err) {
|
|
99
|
+
emitter.emit('error', err);
|
|
100
|
+
}
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
emitter.emit('response', response);
|
|
104
|
+
if (res.body) {
|
|
105
|
+
readWhatwgReadableStream(res.body, output);
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
output.end();
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
*
|
|
113
|
+
*/
|
|
114
|
+
function getResponseHeaderNames(xhr) {
|
|
115
|
+
const headerLines = (xhr.getAllResponseHeaders() || '')
|
|
116
|
+
.split(/[\r\n]+/)
|
|
117
|
+
.filter((l) => l.trim() !== '');
|
|
118
|
+
return headerLines.map((headerLine) => headerLine.split(/\s*:/)[0].toLowerCase());
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
*
|
|
122
|
+
*/
|
|
123
|
+
async function startXmlHttpRequest(request, options, input, output, emitter, counter = 0) {
|
|
124
|
+
const { method, url, headers: reqHeaders } = request;
|
|
125
|
+
const { followRedirect } = options;
|
|
126
|
+
const reqBody = input && /^(post|put|patch)$/i.test(method) ? await (0, stream_1.readAll)(input) : null;
|
|
127
|
+
const xhr = new XMLHttpRequest();
|
|
128
|
+
await (0, request_helper_1.executeWithTimeout)(() => {
|
|
129
|
+
xhr.open(method, url);
|
|
130
|
+
if (reqHeaders) {
|
|
131
|
+
for (const header in reqHeaders) {
|
|
132
|
+
xhr.setRequestHeader(header, reqHeaders[header]);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (options.timeout) {
|
|
136
|
+
xhr.timeout = options.timeout;
|
|
137
|
+
}
|
|
138
|
+
xhr.responseType = 'arraybuffer';
|
|
139
|
+
xhr.send(reqBody);
|
|
140
|
+
return new Promise((resolve, reject) => {
|
|
141
|
+
xhr.onload = () => resolve();
|
|
142
|
+
xhr.onerror = reject;
|
|
143
|
+
xhr.ontimeout = reject;
|
|
144
|
+
xhr.onabort = reject;
|
|
145
|
+
});
|
|
146
|
+
}, options.timeout, () => xhr.abort());
|
|
147
|
+
const headerNames = getResponseHeaderNames(xhr);
|
|
148
|
+
const headers = headerNames.reduce((headers, headerName) => ({
|
|
149
|
+
...headers,
|
|
150
|
+
[headerName]: xhr.getResponseHeader(headerName) || '',
|
|
151
|
+
}), {});
|
|
152
|
+
const response = {
|
|
153
|
+
statusCode: xhr.status,
|
|
154
|
+
headers: headers,
|
|
155
|
+
};
|
|
156
|
+
if (followRedirect && (0, request_helper_1.isRedirect)(response.statusCode)) {
|
|
157
|
+
try {
|
|
158
|
+
(0, request_helper_1.performRedirectRequest)(request, response, followRedirect, counter, (req) => startXmlHttpRequest(req, options, undefined, output, emitter, counter + 1));
|
|
159
|
+
}
|
|
160
|
+
catch (err) {
|
|
161
|
+
emitter.emit('error', err);
|
|
162
|
+
}
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
let body;
|
|
166
|
+
if (!response.statusCode) {
|
|
167
|
+
response.statusCode = 400;
|
|
168
|
+
body = Buffer.from('Access Declined');
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
body = Buffer.from(xhr.response);
|
|
172
|
+
}
|
|
173
|
+
emitter.emit('response', response);
|
|
174
|
+
output.write(body);
|
|
175
|
+
output.end();
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
*
|
|
179
|
+
*/
|
|
180
|
+
let defaults = {};
|
|
181
|
+
/**
|
|
182
|
+
*
|
|
183
|
+
*/
|
|
184
|
+
function setDefaults(defaults_) {
|
|
185
|
+
defaults = defaults_;
|
|
186
|
+
}
|
|
187
|
+
exports.setDefaults = setDefaults;
|
|
188
|
+
/**
|
|
189
|
+
*
|
|
190
|
+
*/
|
|
191
|
+
function request(req, options_ = {}) {
|
|
192
|
+
const options = { ...defaults, ...options_ };
|
|
193
|
+
const { input, output, stream } = (0, request_helper_1.createHttpRequestHandlerStreams)(req, options);
|
|
194
|
+
if (typeof window !== 'undefined' && typeof window.fetch === 'function') {
|
|
195
|
+
startFetchRequest(req, options, input, output, stream);
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
startXmlHttpRequest(req, options, input, output, stream);
|
|
199
|
+
}
|
|
200
|
+
return stream;
|
|
201
|
+
}
|
|
202
|
+
exports.default = request;
|
package/lib/cache.d.ts
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
/**
|
|
3
|
+
* @file Manages asynchronous method response cache
|
|
4
|
+
* @author Shinichi Tomita <shinichi.tomita@gmail.com>
|
|
5
|
+
*/
|
|
6
|
+
import { EventEmitter } from 'events';
|
|
7
|
+
/**
|
|
8
|
+
* type def
|
|
9
|
+
*/
|
|
10
|
+
export type CachingOptions = {
|
|
11
|
+
key?: string | ((...args: any[]) => string);
|
|
12
|
+
namespace?: string;
|
|
13
|
+
strategy: 'NOCACHE' | 'HIT' | 'IMMEDIATE';
|
|
14
|
+
};
|
|
15
|
+
type CacheValue<T> = {
|
|
16
|
+
error?: Error;
|
|
17
|
+
result: T;
|
|
18
|
+
};
|
|
19
|
+
export type CachedFunction<Fn> = Fn & {
|
|
20
|
+
clear: (...args: any[]) => void;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Class for managing cache entry
|
|
24
|
+
*
|
|
25
|
+
* @private
|
|
26
|
+
* @class
|
|
27
|
+
* @constructor
|
|
28
|
+
* @template T
|
|
29
|
+
*/
|
|
30
|
+
declare class CacheEntry<T> extends EventEmitter {
|
|
31
|
+
_fetching: boolean;
|
|
32
|
+
_value: CacheValue<T> | void;
|
|
33
|
+
/**
|
|
34
|
+
* Get value in the cache entry
|
|
35
|
+
*
|
|
36
|
+
* @param {() => Promise<T>} [callback] - Callback function callbacked the cache entry updated
|
|
37
|
+
* @returns {T|undefined}
|
|
38
|
+
*/
|
|
39
|
+
get(callback?: (v: T) => any): CacheValue<T> | void;
|
|
40
|
+
/**
|
|
41
|
+
* Set value in the cache entry
|
|
42
|
+
*/
|
|
43
|
+
set(value: CacheValue<T>): void;
|
|
44
|
+
/**
|
|
45
|
+
* Clear cached value
|
|
46
|
+
*/
|
|
47
|
+
clear(): void;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Caching manager for async methods
|
|
51
|
+
*
|
|
52
|
+
* @class
|
|
53
|
+
* @constructor
|
|
54
|
+
*/
|
|
55
|
+
export declare class Cache {
|
|
56
|
+
private _entries;
|
|
57
|
+
/**
|
|
58
|
+
* retrive cache entry, or create if not exists.
|
|
59
|
+
*
|
|
60
|
+
* @param {String} [key] - Key of cache entry
|
|
61
|
+
* @returns {CacheEntry}
|
|
62
|
+
*/
|
|
63
|
+
get(key: string): CacheEntry<any>;
|
|
64
|
+
/**
|
|
65
|
+
* clear cache entries prefix matching given key
|
|
66
|
+
*/
|
|
67
|
+
clear(key?: string): void;
|
|
68
|
+
/**
|
|
69
|
+
* Enable caching for async call fn to lookup the response cache first,
|
|
70
|
+
* then invoke original if no cached value.
|
|
71
|
+
*/
|
|
72
|
+
createCachedFunction<Fn extends Function>(fn: Fn, scope: any, options?: CachingOptions): CachedFunction<Fn>;
|
|
73
|
+
}
|
|
74
|
+
export default Cache;
|