catalyst-relay 0.5.13 → 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 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)` | Compile and validate |
207
- | `delete(objects, transport?)` | Remove objects |
208
- | `getPackages()` | List packages |
209
- | `getPackageStats(name)` | Get package metadata and object count |
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(object, column)` | Distinct values |
215
- | `countRows(object, type)` | Row count |
216
- | `search(query, types?)` | Search objects |
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/DF', 'CLAS/OC']);
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.type})`);
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
- | DELETE | `/objects/:transport?` | Delete objects |
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 '{ "types": ["DDLS/DF", "CLAS/OC"] }'
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.4.5*
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
@@ -2381,16 +2381,64 @@ function parseDataPreview(xml, maxRows, isTable) {
2381
2381
  const namespace = "http://www.sap.com/adt/dataPreview";
2382
2382
  const metadataElements = doc.getElementsByTagNameNS(namespace, "metadata");
2383
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
+ };
2384
2408
  for (let i = 0; i < metadataElements.length; i++) {
2385
2409
  const meta = metadataElements[i];
2386
2410
  if (!meta) continue;
2387
2411
  const nameAttr = isTable ? "name" : "camelCaseName";
2388
2412
  const name = meta.getAttributeNS(namespace, nameAttr) || meta.getAttribute("name");
2389
- const dataType = meta.getAttributeNS(namespace, "colType") || meta.getAttribute("colType");
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
+ }
2390
2434
  if (!name || !dataType) continue;
2391
2435
  columns.push({ name, dataType });
2392
2436
  }
2393
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
+ }
2394
2442
  if (columns.length === 0 && dataSetElements.length > 0) {
2395
2443
  for (let i = 0; i < dataSetElements.length; i++) {
2396
2444
  const dataSet = dataSetElements[i];
@@ -2473,7 +2521,7 @@ async function previewData(client, query) {
2473
2521
 
2474
2522
  // src/core/adt/data_extraction/freestyle.ts
2475
2523
  var DEFAULT_ROW_LIMIT = 100;
2476
- async function freestyleQuery(client, sqlQuery, limit = DEFAULT_ROW_LIMIT) {
2524
+ async function freestyleQuery(client, sqlQuery, limit = DEFAULT_ROW_LIMIT, timeout) {
2477
2525
  debug(`Freestyle query: ${sqlQuery}`);
2478
2526
  const [response, requestErr] = await client.request({
2479
2527
  method: "POST",
@@ -2483,16 +2531,21 @@ async function freestyleQuery(client, sqlQuery, limit = DEFAULT_ROW_LIMIT) {
2483
2531
  },
2484
2532
  headers: {
2485
2533
  "Accept": "application/xml, application/vnd.sap.adt.datapreview.table.v1+xml",
2486
- "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"
2487
2539
  },
2488
- body: sqlQuery
2540
+ body: sqlQuery,
2541
+ ...timeout !== void 0 && { timeout }
2489
2542
  });
2490
2543
  if (requestErr) return err(requestErr);
2491
2544
  if (!response.ok) {
2492
2545
  const text2 = await response.text();
2493
2546
  debug(`Freestyle query error response: ${text2.substring(0, 500)}`);
2494
2547
  const errorMsg = extractError(text2);
2495
- return err(new Error(`Freestyle query failed: ${errorMsg}`));
2548
+ return err(new Error(`Freestyle query failed: ${errorMsg}`, { cause: text2 }));
2496
2549
  }
2497
2550
  const text = await response.text();
2498
2551
  const [dataFrame, parseErr] = parseDataPreview(text, limit, true);
@@ -2753,7 +2806,16 @@ function parseTransportTasks(doc) {
2753
2806
  position: el.getAttribute("tm:position") || ""
2754
2807
  });
2755
2808
  }
2756
- tasks.push({ taskId, objects });
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
+ });
2757
2819
  }
2758
2820
  return tasks;
2759
2821
  }
@@ -3147,6 +3209,12 @@ async function countRows2(state, requestor, objectName, objectType, parameters =
3147
3209
  return countRows(requestor, objectName, objectType, parameters);
3148
3210
  }
3149
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
+
3150
3218
  // src/client/methods/search/search.ts
3151
3219
  async function search(state, requestor, query, options) {
3152
3220
  if (!state.session) return err(new Error("Not logged in"));
@@ -3330,7 +3398,7 @@ function buildUrl2(baseUrl, path, params) {
3330
3398
  // src/client/methods/internal/request.ts
3331
3399
  async function executeRequest(deps, options, selfRequest) {
3332
3400
  const { state, ssoCerts, getCookieHeader, storeCookies: storeCookies2 } = deps;
3333
- const { method, path, params, headers: customHeaders, body } = options;
3401
+ const { method, path, params, headers: customHeaders, body, timeout: requestTimeout } = options;
3334
3402
  const { config } = state;
3335
3403
  debug(`Request ${method} ${path} - CSRF token in state: ${state.csrfToken?.substring(0, 20) || "null"}...`);
3336
3404
  const headers = buildRequestHeaders(
@@ -3357,7 +3425,7 @@ async function executeRequest(deps, options, selfRequest) {
3357
3425
  cert: ssoCerts?.cert,
3358
3426
  key: ssoCerts?.key,
3359
3427
  rejectUnauthorized: !config.insecure,
3360
- timeout: config.timeout ?? DEFAULT_TIMEOUT
3428
+ timeout: requestTimeout ?? config.timeout ?? DEFAULT_TIMEOUT
3361
3429
  });
3362
3430
  storeCookies2(response);
3363
3431
  if (response.status === 403) {
@@ -3380,7 +3448,7 @@ async function executeRequest(deps, options, selfRequest) {
3380
3448
  cert: ssoCerts?.cert,
3381
3449
  key: ssoCerts?.key,
3382
3450
  rejectUnauthorized: !config.insecure,
3383
- timeout: config.timeout ?? DEFAULT_TIMEOUT
3451
+ timeout: requestTimeout ?? config.timeout ?? DEFAULT_TIMEOUT
3384
3452
  });
3385
3453
  storeCookies2(retryResponse);
3386
3454
  return ok(retryResponse);
@@ -3551,6 +3619,9 @@ var ADTClientImpl = class {
3551
3619
  async countRows(objectName, objectType, parameters = []) {
3552
3620
  return countRows2(this.state, this.requestor, objectName, objectType, parameters);
3553
3621
  }
3622
+ async freestyleQuery(sqlQuery, limit, timeout) {
3623
+ return freestyleQuery2(this.state, this.requestor, sqlQuery, limit, timeout);
3624
+ }
3554
3625
  // --- Search ---
3555
3626
  async search(query, options) {
3556
3627
  return search(this.state, this.requestor, query, options);