catalyst-relay 0.5.12 → 0.5.14
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 +30 -16
- package/dist/index.d.mts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +92 -16
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +92 -16
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -188,7 +188,9 @@ curl -X POST http://localhost:3000/login \
|
|
|
188
188
|
| `asdcls` | Access Control | DCLS/DL |
|
|
189
189
|
| `aclass` | ABAP Class | CLAS/OC |
|
|
190
190
|
| `asprog` | ABAP Program | PROG/P |
|
|
191
|
+
| `asinc` | ABAP Include | PROG/I |
|
|
191
192
|
| `astabldt` | Table | TABL/DT |
|
|
193
|
+
| `astablds` | Structure | STRU/D |
|
|
192
194
|
|
|
193
195
|
## Library Mode API Reference
|
|
194
196
|
|
|
@@ -199,21 +201,28 @@ curl -X POST http://localhost:3000/login \
|
|
|
199
201
|
| `login()` | Authenticate and create session |
|
|
200
202
|
| `logout()` | End session |
|
|
201
203
|
| `refreshSession()` | Manually refresh session (keepalive) |
|
|
204
|
+
| `exportSessionState()` | Serialize session for transfer to another process |
|
|
205
|
+
| `importSessionState(state)` | Restore a previously exported session |
|
|
202
206
|
| `read(objects)` | Batch read with content |
|
|
203
|
-
| `create(object, package, transport?)` | Create new object |
|
|
204
|
-
| `update(object, transport?)` | Update existing object |
|
|
205
|
-
| `upsert(objects, package, transport?)` | Create or update |
|
|
206
|
-
| `activate(objects)` |
|
|
207
|
-
| `
|
|
208
|
-
| `
|
|
209
|
-
| `
|
|
207
|
+
| `create(object, package, transport?)` | Create a new object |
|
|
208
|
+
| `update(object, transport?)` | Update an existing object |
|
|
209
|
+
| `upsert(objects, package, transport?)` | Create or update a batch |
|
|
210
|
+
| `activate(objects)` | Run-based activation; mixed extensions allowed |
|
|
211
|
+
| `checkSyntax(objects)` | Syntax check (single extension per batch) |
|
|
212
|
+
| `delete(objects, transport?)` | Multi-delete with dependency ordering; returns `DeleteResult[]` |
|
|
213
|
+
| `getPackages(options?)` | List packages (filter, includeDescriptions) |
|
|
214
|
+
| `getPackageStats(nameOrNames)` | Package description and recursive object count |
|
|
210
215
|
| `getTree(query)` | Browse package tree (supports owner filter) |
|
|
211
216
|
| `getTransports(package)` | List transports |
|
|
212
217
|
| `createTransport(config)` | Create transport |
|
|
218
|
+
| `deleteTransport(id, removeObjects?)` | Delete transport (optionally clear contents first) |
|
|
219
|
+
| `removeFromTransport(id, objectName)` | Remove a single object from a transport |
|
|
220
|
+
| `viewTransportObjects(id)` | List tasks and objects on a transport |
|
|
221
|
+
| `getInactiveObjects()` | List objects/transports awaiting activation |
|
|
213
222
|
| `previewData(query)` | Query table/view |
|
|
214
|
-
| `getDistinctValues(
|
|
215
|
-
| `countRows(
|
|
216
|
-
| `search(query,
|
|
223
|
+
| `getDistinctValues(name, parameters, column, type?)` | Distinct values with counts |
|
|
224
|
+
| `countRows(name, type, parameters?)` | Row count |
|
|
225
|
+
| `search(query, options?)` | Search objects (`{ types?, includePackages? }`) |
|
|
217
226
|
| `whereUsed(object)` | Find dependencies |
|
|
218
227
|
| `gitDiff(objects)` | Compare with server |
|
|
219
228
|
| `getObjectConfig()` | Supported object types |
|
|
@@ -261,11 +270,11 @@ if (error) {
|
|
|
261
270
|
#### Searching Objects
|
|
262
271
|
|
|
263
272
|
```typescript
|
|
264
|
-
const [results, error] = await client.search('ZSNAP*', ['DDLS
|
|
273
|
+
const [results, error] = await client.search('ZSNAP*', { types: ['DDLS', 'CLAS'] });
|
|
265
274
|
|
|
266
275
|
if (!error) {
|
|
267
276
|
for (const result of results) {
|
|
268
|
-
console.log(`${result.name} (${result.
|
|
277
|
+
console.log(`${result.name} (${result.objectType})`);
|
|
269
278
|
}
|
|
270
279
|
}
|
|
271
280
|
```
|
|
@@ -294,12 +303,17 @@ const [data, error] = await client.previewData({
|
|
|
294
303
|
| GET | `/packages` | List available packages |
|
|
295
304
|
| GET | `/packages/:name/stats` | Get package metadata and count |
|
|
296
305
|
| POST | `/tree` | Browse package tree (supports owner filter) |
|
|
297
|
-
| GET | `/transports/:package` | List transports |
|
|
306
|
+
| GET | `/transports/:package` | List transports for a package |
|
|
298
307
|
| POST | `/transports` | Create transport |
|
|
308
|
+
| DELETE | `/transports/:transportId` | Delete a transport (`?removeObjects=true` to clear first) |
|
|
309
|
+
| GET | `/transports/:transportId/objects` | List tasks/objects on a transport |
|
|
310
|
+
| PUT | `/transports/:transportId/objects` | Remove a single object from a transport |
|
|
311
|
+
| GET | `/inactive-objects` | List objects/transports awaiting activation |
|
|
299
312
|
| POST | `/objects/read` | Batch read objects |
|
|
300
313
|
| POST | `/objects/upsert/:package/:transport?` | Create/update objects |
|
|
301
314
|
| POST | `/objects/activate` | Activate objects |
|
|
302
|
-
|
|
|
315
|
+
| POST | `/objects/check` | Syntax check objects |
|
|
316
|
+
| DELETE | `/objects/:transport?` | Multi-delete with dependency ordering |
|
|
303
317
|
| POST | `/preview/data` | Query table/view data |
|
|
304
318
|
| POST | `/preview/distinct` | Get distinct values |
|
|
305
319
|
| POST | `/preview/count` | Count rows |
|
|
@@ -346,7 +360,7 @@ curl -X POST http://localhost:3000/preview/data \
|
|
|
346
360
|
curl -X POST "http://localhost:3000/search/ZSNAP*" \
|
|
347
361
|
-H "Content-Type: application/json" \
|
|
348
362
|
-H "x-session-id: abc123" \
|
|
349
|
-
-d '
|
|
363
|
+
-d '["DDLS", "CLAS"]'
|
|
350
364
|
```
|
|
351
365
|
|
|
352
366
|
## Error Handling
|
|
@@ -483,4 +497,4 @@ Egan Bosch
|
|
|
483
497
|
|
|
484
498
|
---
|
|
485
499
|
|
|
486
|
-
*Last updated: v0.
|
|
500
|
+
*Last updated: v0.5.13*
|
package/dist/index.d.mts
CHANGED
|
@@ -597,6 +597,9 @@ interface TransportObject {
|
|
|
597
597
|
|
|
598
598
|
interface TaskContents {
|
|
599
599
|
taskId: string;
|
|
600
|
+
owner?: string;
|
|
601
|
+
description?: string;
|
|
602
|
+
status?: string;
|
|
600
603
|
objects: TransportObject[];
|
|
601
604
|
}
|
|
602
605
|
|
|
@@ -674,6 +677,7 @@ interface ADTClient {
|
|
|
674
677
|
previewData(query: PreviewSQL): AsyncResult<DataFrame>;
|
|
675
678
|
getDistinctValues(objectName: string, parameters: Parameter[], column: string, objectType?: 'table' | 'view'): AsyncResult<DistinctResult>;
|
|
676
679
|
countRows(objectName: string, objectType: 'table' | 'view', parameters?: Parameter[]): AsyncResult<number>;
|
|
680
|
+
freestyleQuery(sqlQuery: string, limit?: number, timeout?: number): AsyncResult<DataFrame>;
|
|
677
681
|
search(query: string, options?: SearchOptions): AsyncResult<SearchResult[]>;
|
|
678
682
|
whereUsed(object: ObjectRef): AsyncResult<Dependency[]>;
|
|
679
683
|
createTransport(config: TransportConfig): AsyncResult<string>;
|
package/dist/index.d.ts
CHANGED
|
@@ -597,6 +597,9 @@ interface TransportObject {
|
|
|
597
597
|
|
|
598
598
|
interface TaskContents {
|
|
599
599
|
taskId: string;
|
|
600
|
+
owner?: string;
|
|
601
|
+
description?: string;
|
|
602
|
+
status?: string;
|
|
600
603
|
objects: TransportObject[];
|
|
601
604
|
}
|
|
602
605
|
|
|
@@ -674,6 +677,7 @@ interface ADTClient {
|
|
|
674
677
|
previewData(query: PreviewSQL): AsyncResult<DataFrame>;
|
|
675
678
|
getDistinctValues(objectName: string, parameters: Parameter[], column: string, objectType?: 'table' | 'view'): AsyncResult<DistinctResult>;
|
|
676
679
|
countRows(objectName: string, objectType: 'table' | 'view', parameters?: Parameter[]): AsyncResult<number>;
|
|
680
|
+
freestyleQuery(sqlQuery: string, limit?: number, timeout?: number): AsyncResult<DataFrame>;
|
|
677
681
|
search(query: string, options?: SearchOptions): AsyncResult<SearchResult[]>;
|
|
678
682
|
whereUsed(object: ObjectRef): AsyncResult<Dependency[]>;
|
|
679
683
|
createTransport(config: TransportConfig): AsyncResult<string>;
|
package/dist/index.js
CHANGED
|
@@ -1614,7 +1614,9 @@ async function updateObject(client, object, lockHandle, transport) {
|
|
|
1614
1614
|
|
|
1615
1615
|
// src/core/adt/craud/activation.ts
|
|
1616
1616
|
var MAX_POLL_ATTEMPTS = 30;
|
|
1617
|
+
var POLL_RETRY_DELAY_MS = 1e3;
|
|
1617
1618
|
var RUN_ID_REGEX = /\/activation\/runs\/([^?/]+)/;
|
|
1619
|
+
var BACKGROUND_RUN_MEDIA_TYPE = "application/vnd.sap.adt.backgroundrun.v1+xml";
|
|
1618
1620
|
async function activateObjects(client, objects) {
|
|
1619
1621
|
if (objects.length === 0) {
|
|
1620
1622
|
return ok([]);
|
|
@@ -1640,7 +1642,7 @@ async function activateObjects(client, objects) {
|
|
|
1640
1642
|
},
|
|
1641
1643
|
headers: {
|
|
1642
1644
|
"Content-Type": "application/xml",
|
|
1643
|
-
"Accept":
|
|
1645
|
+
"Accept": BACKGROUND_RUN_MEDIA_TYPE
|
|
1644
1646
|
},
|
|
1645
1647
|
body
|
|
1646
1648
|
});
|
|
@@ -1658,22 +1660,25 @@ async function activateObjects(client, objects) {
|
|
|
1658
1660
|
}
|
|
1659
1661
|
const runId = runIdMatch[1];
|
|
1660
1662
|
debug(`Activation run ID: ${runId}`);
|
|
1661
|
-
let pollAttempt =
|
|
1662
|
-
while (pollAttempt < MAX_POLL_ATTEMPTS) {
|
|
1663
|
+
for (let pollAttempt = 1; pollAttempt <= MAX_POLL_ATTEMPTS; pollAttempt++) {
|
|
1663
1664
|
const [pollRes, pollErr] = await client.request({
|
|
1664
1665
|
method: "GET",
|
|
1665
1666
|
path: `/sap/bc/adt/activation/runs/${runId}`,
|
|
1666
1667
|
params: { "withLongPolling": "true" },
|
|
1667
|
-
headers: { "Accept":
|
|
1668
|
+
headers: { "Accept": BACKGROUND_RUN_MEDIA_TYPE }
|
|
1668
1669
|
});
|
|
1669
1670
|
if (pollErr) return err(pollErr);
|
|
1670
|
-
debug(`Activation poll attempt ${pollAttempt
|
|
1671
|
+
debug(`Activation poll attempt ${pollAttempt} status: ${pollRes.status}`);
|
|
1671
1672
|
if (pollRes.ok) break;
|
|
1672
|
-
|
|
1673
|
+
if (pollRes.status >= 400 && pollRes.status < 500) {
|
|
1674
|
+
const errText = await pollRes.text();
|
|
1675
|
+
return err(new Error(`Activation run ${runId} polling rejected (${pollRes.status}): ${extractError(errText)}`));
|
|
1676
|
+
}
|
|
1673
1677
|
if (pollAttempt >= MAX_POLL_ATTEMPTS) {
|
|
1674
1678
|
const errText = await pollRes.text();
|
|
1675
|
-
return err(new Error(`Activation run ${runId} did not complete: ${extractError(errText)}`));
|
|
1679
|
+
return err(new Error(`Activation run ${runId} did not complete after ${MAX_POLL_ATTEMPTS} attempts: ${extractError(errText)}`));
|
|
1676
1680
|
}
|
|
1681
|
+
await new Promise((resolve) => setTimeout(resolve, POLL_RETRY_DELAY_MS));
|
|
1677
1682
|
}
|
|
1678
1683
|
const [resultsRes, resultsErr] = await client.request({
|
|
1679
1684
|
method: "GET",
|
|
@@ -2376,16 +2381,64 @@ function parseDataPreview(xml, maxRows, isTable) {
|
|
|
2376
2381
|
const namespace = "http://www.sap.com/adt/dataPreview";
|
|
2377
2382
|
const metadataElements = doc.getElementsByTagNameNS(namespace, "metadata");
|
|
2378
2383
|
const columns = [];
|
|
2384
|
+
const SAP_TYPE_MAP = {
|
|
2385
|
+
"8": "integer",
|
|
2386
|
+
// Int8
|
|
2387
|
+
"I": "integer",
|
|
2388
|
+
// Integer
|
|
2389
|
+
"P": "decimal",
|
|
2390
|
+
// Packed decimal
|
|
2391
|
+
"F": "float",
|
|
2392
|
+
// Floating point
|
|
2393
|
+
"D": "date",
|
|
2394
|
+
// Date (YYYYMMDD)
|
|
2395
|
+
"T": "time",
|
|
2396
|
+
// Time (HHMMSS)
|
|
2397
|
+
"S": "timestamp",
|
|
2398
|
+
// Timestamp
|
|
2399
|
+
"C": "string",
|
|
2400
|
+
// Character
|
|
2401
|
+
"N": "string",
|
|
2402
|
+
// Numeric character string
|
|
2403
|
+
"V": "string",
|
|
2404
|
+
// Variable-length character
|
|
2405
|
+
"X": "binary"
|
|
2406
|
+
// Raw binary/hex
|
|
2407
|
+
};
|
|
2379
2408
|
for (let i = 0; i < metadataElements.length; i++) {
|
|
2380
2409
|
const meta = metadataElements[i];
|
|
2381
2410
|
if (!meta) continue;
|
|
2382
2411
|
const nameAttr = isTable ? "name" : "camelCaseName";
|
|
2383
2412
|
const name = meta.getAttributeNS(namespace, nameAttr) || meta.getAttribute("name");
|
|
2384
|
-
const
|
|
2413
|
+
const colType = meta.getAttributeNS(namespace, "colType") || meta.getAttribute("colType");
|
|
2414
|
+
const rawType = meta.getAttributeNS(namespace, "type") || meta.getAttribute("type");
|
|
2415
|
+
const isKeyFigure = meta.getAttributeNS(namespace, "isKeyFigure") === "true";
|
|
2416
|
+
let dataType;
|
|
2417
|
+
if (colType && colType.trim() !== "") {
|
|
2418
|
+
dataType = colType;
|
|
2419
|
+
} else if (isKeyFigure) {
|
|
2420
|
+
dataType = "decimal";
|
|
2421
|
+
} else if (rawType && SAP_TYPE_MAP[rawType]) {
|
|
2422
|
+
dataType = SAP_TYPE_MAP[rawType];
|
|
2423
|
+
} else {
|
|
2424
|
+
dataType = "string";
|
|
2425
|
+
}
|
|
2426
|
+
const allAttrs = {};
|
|
2427
|
+
for (let j = 0; j < meta.attributes.length; j++) {
|
|
2428
|
+
const attr = meta.attributes[j];
|
|
2429
|
+
if (!attr) {
|
|
2430
|
+
continue;
|
|
2431
|
+
}
|
|
2432
|
+
allAttrs[attr.name] = attr.value;
|
|
2433
|
+
}
|
|
2385
2434
|
if (!name || !dataType) continue;
|
|
2386
2435
|
columns.push({ name, dataType });
|
|
2387
2436
|
}
|
|
2388
2437
|
const dataSetElements = doc.getElementsByTagNameNS(namespace, "dataSet");
|
|
2438
|
+
for (let i = 0; i < dataSetElements.length; i++) {
|
|
2439
|
+
const dataSet = dataSetElements[i];
|
|
2440
|
+
if (!dataSet) continue;
|
|
2441
|
+
}
|
|
2389
2442
|
if (columns.length === 0 && dataSetElements.length > 0) {
|
|
2390
2443
|
for (let i = 0; i < dataSetElements.length; i++) {
|
|
2391
2444
|
const dataSet = dataSetElements[i];
|
|
@@ -2468,7 +2521,7 @@ async function previewData(client, query) {
|
|
|
2468
2521
|
|
|
2469
2522
|
// src/core/adt/data_extraction/freestyle.ts
|
|
2470
2523
|
var DEFAULT_ROW_LIMIT = 100;
|
|
2471
|
-
async function freestyleQuery(client, sqlQuery, limit = DEFAULT_ROW_LIMIT) {
|
|
2524
|
+
async function freestyleQuery(client, sqlQuery, limit = DEFAULT_ROW_LIMIT, timeout) {
|
|
2472
2525
|
debug(`Freestyle query: ${sqlQuery}`);
|
|
2473
2526
|
const [response, requestErr] = await client.request({
|
|
2474
2527
|
method: "POST",
|
|
@@ -2478,16 +2531,21 @@ async function freestyleQuery(client, sqlQuery, limit = DEFAULT_ROW_LIMIT) {
|
|
|
2478
2531
|
},
|
|
2479
2532
|
headers: {
|
|
2480
2533
|
"Accept": "application/xml, application/vnd.sap.adt.datapreview.table.v1+xml",
|
|
2481
|
-
"Content-Type": "text/plain"
|
|
2534
|
+
"Content-Type": "text/plain",
|
|
2535
|
+
// Override stateful base header: each preview request is independent; stateless
|
|
2536
|
+
// lets SAP route to any work process and recycle it after the request, preventing
|
|
2537
|
+
// GENERATE_SUBPOOL_DIR_FULL (36-pool limit per work process).
|
|
2538
|
+
"X-sap-adt-sessiontype": "stateless"
|
|
2482
2539
|
},
|
|
2483
|
-
body: sqlQuery
|
|
2540
|
+
body: sqlQuery,
|
|
2541
|
+
...timeout !== void 0 && { timeout }
|
|
2484
2542
|
});
|
|
2485
2543
|
if (requestErr) return err(requestErr);
|
|
2486
2544
|
if (!response.ok) {
|
|
2487
2545
|
const text2 = await response.text();
|
|
2488
2546
|
debug(`Freestyle query error response: ${text2.substring(0, 500)}`);
|
|
2489
2547
|
const errorMsg = extractError(text2);
|
|
2490
|
-
return err(new Error(`Freestyle query failed: ${errorMsg}
|
|
2548
|
+
return err(new Error(`Freestyle query failed: ${errorMsg}`, { cause: text2 }));
|
|
2491
2549
|
}
|
|
2492
2550
|
const text = await response.text();
|
|
2493
2551
|
const [dataFrame, parseErr] = parseDataPreview(text, limit, true);
|
|
@@ -2748,7 +2806,16 @@ function parseTransportTasks(doc) {
|
|
|
2748
2806
|
position: el.getAttribute("tm:position") || ""
|
|
2749
2807
|
});
|
|
2750
2808
|
}
|
|
2751
|
-
|
|
2809
|
+
const owner = taskEl.getAttribute("tm:owner");
|
|
2810
|
+
const description = taskEl.getAttribute("tm:desc");
|
|
2811
|
+
const status = taskEl.getAttribute("tm:status");
|
|
2812
|
+
tasks.push({
|
|
2813
|
+
taskId,
|
|
2814
|
+
...owner ? { owner } : {},
|
|
2815
|
+
...description ? { description } : {},
|
|
2816
|
+
...status ? { status } : {},
|
|
2817
|
+
objects
|
|
2818
|
+
});
|
|
2752
2819
|
}
|
|
2753
2820
|
return tasks;
|
|
2754
2821
|
}
|
|
@@ -3142,6 +3209,12 @@ async function countRows2(state, requestor, objectName, objectType, parameters =
|
|
|
3142
3209
|
return countRows(requestor, objectName, objectType, parameters);
|
|
3143
3210
|
}
|
|
3144
3211
|
|
|
3212
|
+
// src/client/methods/preview/freestyleQuery.ts
|
|
3213
|
+
async function freestyleQuery2(state, requestor, sqlQuery, limit, timeout) {
|
|
3214
|
+
if (!state.session) return err(new Error("Not logged in"));
|
|
3215
|
+
return freestyleQuery(requestor, sqlQuery, limit, timeout);
|
|
3216
|
+
}
|
|
3217
|
+
|
|
3145
3218
|
// src/client/methods/search/search.ts
|
|
3146
3219
|
async function search(state, requestor, query, options) {
|
|
3147
3220
|
if (!state.session) return err(new Error("Not logged in"));
|
|
@@ -3325,7 +3398,7 @@ function buildUrl2(baseUrl, path, params) {
|
|
|
3325
3398
|
// src/client/methods/internal/request.ts
|
|
3326
3399
|
async function executeRequest(deps, options, selfRequest) {
|
|
3327
3400
|
const { state, ssoCerts, getCookieHeader, storeCookies: storeCookies2 } = deps;
|
|
3328
|
-
const { method, path, params, headers: customHeaders, body } = options;
|
|
3401
|
+
const { method, path, params, headers: customHeaders, body, timeout: requestTimeout } = options;
|
|
3329
3402
|
const { config } = state;
|
|
3330
3403
|
debug(`Request ${method} ${path} - CSRF token in state: ${state.csrfToken?.substring(0, 20) || "null"}...`);
|
|
3331
3404
|
const headers = buildRequestHeaders(
|
|
@@ -3352,7 +3425,7 @@ async function executeRequest(deps, options, selfRequest) {
|
|
|
3352
3425
|
cert: ssoCerts?.cert,
|
|
3353
3426
|
key: ssoCerts?.key,
|
|
3354
3427
|
rejectUnauthorized: !config.insecure,
|
|
3355
|
-
timeout: config.timeout ?? DEFAULT_TIMEOUT
|
|
3428
|
+
timeout: requestTimeout ?? config.timeout ?? DEFAULT_TIMEOUT
|
|
3356
3429
|
});
|
|
3357
3430
|
storeCookies2(response);
|
|
3358
3431
|
if (response.status === 403) {
|
|
@@ -3375,7 +3448,7 @@ async function executeRequest(deps, options, selfRequest) {
|
|
|
3375
3448
|
cert: ssoCerts?.cert,
|
|
3376
3449
|
key: ssoCerts?.key,
|
|
3377
3450
|
rejectUnauthorized: !config.insecure,
|
|
3378
|
-
timeout: config.timeout ?? DEFAULT_TIMEOUT
|
|
3451
|
+
timeout: requestTimeout ?? config.timeout ?? DEFAULT_TIMEOUT
|
|
3379
3452
|
});
|
|
3380
3453
|
storeCookies2(retryResponse);
|
|
3381
3454
|
return ok(retryResponse);
|
|
@@ -3546,6 +3619,9 @@ var ADTClientImpl = class {
|
|
|
3546
3619
|
async countRows(objectName, objectType, parameters = []) {
|
|
3547
3620
|
return countRows2(this.state, this.requestor, objectName, objectType, parameters);
|
|
3548
3621
|
}
|
|
3622
|
+
async freestyleQuery(sqlQuery, limit, timeout) {
|
|
3623
|
+
return freestyleQuery2(this.state, this.requestor, sqlQuery, limit, timeout);
|
|
3624
|
+
}
|
|
3549
3625
|
// --- Search ---
|
|
3550
3626
|
async search(query, options) {
|
|
3551
3627
|
return search(this.state, this.requestor, query, options);
|