@abapify/adt-tui 0.4.34 → 0.4.36
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 -47
- package/dist/index.d.mts +4 -4
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +118 -86
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,71 +1,36 @@
|
|
|
1
1
|
# @abapify/adt-tui
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
a `PageRenderer`, a hypermedia link parser, and a generic page — that other
|
|
6
|
-
packages and CLI commands use to build interactive ADT explorers in the
|
|
7
|
-
terminal.
|
|
3
|
+
[](https://www.npmjs.com/package/@abapify/adt-tui)
|
|
4
|
+
[](https://github.com/abapify/adt-cli/blob/main/LICENSE)
|
|
8
5
|
|
|
9
|
-
|
|
6
|
+
Terminal UI components for ADT - page-based navigation with Ink.
|
|
10
7
|
|
|
11
8
|
## Install
|
|
12
9
|
|
|
13
10
|
```bash
|
|
14
|
-
npm
|
|
15
|
-
# or
|
|
16
|
-
bun add @abapify/adt-tui
|
|
11
|
+
npm install @abapify/adt-tui
|
|
17
12
|
```
|
|
18
13
|
|
|
19
|
-
`@abapify/adt-client` is a peer dependency and must be installed by the host.
|
|
20
|
-
|
|
21
14
|
## Usage
|
|
22
15
|
|
|
23
|
-
Boot the TUI from a CLI command, passing in an initial fetch function
|
|
24
|
-
(typically backed by the ADT client) and a start URI:
|
|
25
|
-
|
|
26
16
|
```ts
|
|
27
17
|
import { run } from '@abapify/adt-tui';
|
|
28
18
|
|
|
29
19
|
await run({
|
|
30
|
-
fetch: (uri) => client.
|
|
20
|
+
fetch: (uri) => client.fetch(uri),
|
|
31
21
|
startUrl: '/sap/bc/adt/repository/nodestructure',
|
|
32
22
|
});
|
|
33
23
|
```
|
|
34
24
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
```tsx
|
|
38
|
-
import { App, Navigator, NavigationProvider } from '@abapify/adt-tui';
|
|
39
|
-
import { render } from 'ink';
|
|
40
|
-
|
|
41
|
-
render(
|
|
42
|
-
<NavigationProvider>
|
|
43
|
-
<App fetch={fetchFn} startUrl="/sap/bc/adt/discovery" />
|
|
44
|
-
</NavigationProvider>,
|
|
45
|
-
);
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
Custom pages implement the `PageComponent` contract and return a
|
|
49
|
-
`PageResult` describing which link to follow next; the framework takes
|
|
50
|
-
care of history, rendering, and back-navigation.
|
|
51
|
-
|
|
52
|
-
## Role in the monorepo
|
|
25
|
+
## Features
|
|
53
26
|
|
|
54
|
-
- Ink-based
|
|
55
|
-
|
|
56
|
-
page/route types.
|
|
57
|
-
- Consumed by interactive `adt-cli` commands and experiments; it deliberately
|
|
58
|
-
contains no ADT-specific business logic beyond link parsing, so commands
|
|
59
|
-
bring their own pages.
|
|
60
|
-
- Depends on `@abapify/adt-contracts` for response typing and keeps
|
|
61
|
-
`@abapify/adt-client` as a peer dependency to avoid duplicating the
|
|
62
|
-
client in host applications.
|
|
27
|
+
- Ink-based terminal UI for browsing ADT resources
|
|
28
|
+
- Page-based navigation with link following
|
|
63
29
|
|
|
64
|
-
##
|
|
30
|
+
## Documentation
|
|
65
31
|
|
|
66
|
-
-
|
|
67
|
-
- [Full docs](https://adt-cli.netlify.app)
|
|
32
|
+
**[adt-cli.netlify.app/sdk/packages/adt-tui](https://adt-cli.netlify.app/sdk/packages/adt-tui)**
|
|
68
33
|
|
|
69
|
-
|
|
34
|
+
---
|
|
70
35
|
|
|
71
|
-
|
|
36
|
+
Part of [**abapify**](https://github.com/abapify/adt-cli) — open, typed tooling for SAP ABAP Development Tools (ADT): CLI, MCP server, and SDK.
|
package/dist/index.d.mts
CHANGED
|
@@ -162,7 +162,7 @@ interface AppProps {
|
|
|
162
162
|
/**
|
|
163
163
|
* Main TUI Application
|
|
164
164
|
*/
|
|
165
|
-
declare function App({ startUrl, fetch, router: _router, systemName }: AppProps): React.ReactNode;
|
|
165
|
+
declare function App({ startUrl, fetch, router: _router, systemName }: Readonly<AppProps>): React.ReactNode;
|
|
166
166
|
interface NavigatorProps {
|
|
167
167
|
/** Optional starting URL - if not provided, shows prompt */
|
|
168
168
|
startUrl?: string;
|
|
@@ -174,7 +174,7 @@ interface NavigatorProps {
|
|
|
174
174
|
/**
|
|
175
175
|
* Main Navigator component
|
|
176
176
|
*/
|
|
177
|
-
declare function Navigator({ startUrl, fetch, systemName }: NavigatorProps): React.ReactNode;
|
|
177
|
+
declare function Navigator({ startUrl, fetch, systemName }: Readonly<NavigatorProps>): React.ReactNode;
|
|
178
178
|
interface PageRendererProps {
|
|
179
179
|
result: PageResult;
|
|
180
180
|
onNavigate: (url: string) => void;
|
|
@@ -184,7 +184,7 @@ interface PageRendererProps {
|
|
|
184
184
|
/**
|
|
185
185
|
* PageRenderer component
|
|
186
186
|
*/
|
|
187
|
-
declare function PageRenderer({ result, onNavigate, onBack, onAction }: PageRendererProps): React.ReactNode;
|
|
187
|
+
declare function PageRenderer({ result, onNavigate, onBack, onAction }: Readonly<PageRendererProps>): React.ReactNode;
|
|
188
188
|
interface NavigationContextValue {
|
|
189
189
|
/** Current navigation entry */
|
|
190
190
|
current: NavigationEntry | null;
|
|
@@ -210,7 +210,7 @@ interface NavigationProviderProps {
|
|
|
210
210
|
/** Initial URL to load */
|
|
211
211
|
initialUrl?: string;
|
|
212
212
|
}
|
|
213
|
-
declare function NavigationProvider({ children, fetch, parseResponse, initialUrl }: NavigationProviderProps): React$1.ReactNode;
|
|
213
|
+
declare function NavigationProvider({ children, fetch, parseResponse, initialUrl }: Readonly<NavigationProviderProps>): React$1.ReactNode;
|
|
214
214
|
/**
|
|
215
215
|
* Hook to access navigation context
|
|
216
216
|
*/
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":["React","parseResponse"],"sources":["../src/lib/types.ts","../src/lib/router.ts","../src/run.tsx","../src/App.tsx","../src/Navigator.tsx","../src/lib/PageRenderer.tsx","../src/lib/context.tsx","../src/lib/parser.ts","../src/pages/GenericPage.tsx"],"mappings":";;;;;;;UASiB;EACf;EACA;EACA;EACA;;;;;UAMe;EACf;EACA,MAAM;EACN,OAAO;EACP;EACA;;;;;UAMe;EACf;EACA,UAAU;EACV,WAAW;;;;;KAMD,WACV,aACA;EACE;EACA,UAAU;EACV;MAEC;;;;UAKY;;EAEf;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;IAAW;IAAc;;;;;;UAMV;;EAEf;;EAEA;;EAEA;;EAEA;;EAEA,SAAS,MAAM;;EAEf,MAAM;;EAEN;;;;;UAMe;;EAEf;;EAEA,UAAU;;;;;KAMA,iBAAiB,OAAO,cAAc;;;;UAKjC;;EAEf,SAAS;;EAET,MAAM;;EAEN;;;;;cCrGW;mBACM;UACT;;;;EAKR,SAAS,OAAO;;;;EAQhB,YAAY,MAAM;;;;EAQlB,MAAM;IAAgB,MAAM;IAAe;;;;;EAqB3C,cAAc;IAAQ;IAAiB;;;UC3CxB;;EAEf;;EAEA,OAAO;;EAEP,SAAS;;EAET;;EAEA;;;;;;;iBAQoB,IAAI,SAAS,aAAa;UCnB/B;;EAEf;;EAEA,OAAO;;EAEP,SAAS;;EAET;;;;;iBAMc,MACd,UACA,OACA,QAAQ,SACR,cACC,
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":["React","parseResponse"],"sources":["../src/lib/types.ts","../src/lib/router.ts","../src/run.tsx","../src/App.tsx","../src/Navigator.tsx","../src/lib/PageRenderer.tsx","../src/lib/context.tsx","../src/lib/parser.ts","../src/pages/GenericPage.tsx"],"mappings":";;;;;;;UASiB;EACf;EACA;EACA;EACA;;;;;UAMe;EACf;EACA,MAAM;EACN,OAAO;EACP;EACA;;;;;UAMe;EACf;EACA,UAAU;EACV,WAAW;;;;;KAMD,WACV,aACA;EACE;EACA,UAAU;EACV;MAEC;;;;UAKY;;EAEf;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;IAAW;IAAc;;;;;;UAMV;;EAEf;;EAEA;;EAEA;;EAEA;;EAEA,SAAS,MAAM;;EAEf,MAAM;;EAEN;;;;;UAMe;;EAEf;;EAEA,UAAU;;;;;KAMA,iBAAiB,OAAO,cAAc;;;;UAKjC;;EAEf,SAAS;;EAET,MAAM;;EAEN;;;;;cCrGW;mBACM;UACT;;;;EAKR,SAAS,OAAO;;;;EAQhB,YAAY,MAAM;;;;EAQlB,MAAM;IAAgB,MAAM;IAAe;;;;;EAqB3C,cAAc;IAAQ;IAAiB;;;UC3CxB;;EAEf;;EAEA,OAAO;;EAEP,SAAS;;EAET;;EAEA;;;;;;;iBAQoB,IAAI,SAAS,aAAa;UCnB/B;;EAEf;;EAEA,OAAO;;EAEP,SAAS;;EAET;;;;;iBAMc,MACd,UACA,OACA,QAAQ,SACR,cACC,SAAS,YAAY,MAAM;UCRpB;;EAER;;EAEA,OAAO;;EAEP;;;;;iBA2Ic,YACd,UACA,OACA,cACC,SAAS,kBAAkB,MAAM;UC7J1B;EACR,QAAQ;EACR,aAAa;EACb;EACA,YAAY;;;;;iBAqCE,eACd,QACA,YACA,QACA,YACC,SAAS,qBAAqB,MAAM;UC5C7B;;EAER,SAAS;;EAET,SAAS;;EAET,WAAW,gBAAgB;;EAE3B;;EAEA;;EAEA;;EAEA,OAAO;;UAKC;EACR,UAAU;;EAEV,OAAO;;EAEP,gBAAgB,gBAAgB;;EAEhC;;iBAGc,qBACd,UACA,OACA,eACA,cACC,SAAS,2BAA2BA,QAAM;;;;iBA6E7B,iBAAiB;;;;iBCzBjBC,gBAAc,cAAc;;;;iBAiB5B,cAAc;;;;iBAQd,gBAAgB,OAAO;EACrC,YAAY;EACZ,SAAS;EACT,MAAM;;;;;iBC9GQ,cAAc,KAAK,YAAY,YAAY"}
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Box, Text, render, useInput } from "ink";
|
|
2
|
-
import React, { createContext, useCallback, useContext, useEffect, useMemo, useState } from "react";
|
|
2
|
+
import React, { createContext, useCallback, useContext, useDebugValue, useEffect, useMemo, useState } from "react";
|
|
3
3
|
import Spinner from "ink-spinner";
|
|
4
4
|
import TextInput from "ink-text-input";
|
|
5
5
|
import { existsSync, writeFileSync } from "node:fs";
|
|
@@ -59,16 +59,25 @@ function NavigationProvider({ children, fetch, parseResponse, initialUrl }) {
|
|
|
59
59
|
current,
|
|
60
60
|
navigate
|
|
61
61
|
]);
|
|
62
|
+
const value = useMemo(() => ({
|
|
63
|
+
current,
|
|
64
|
+
history,
|
|
65
|
+
navigate,
|
|
66
|
+
back,
|
|
67
|
+
loading,
|
|
68
|
+
error,
|
|
69
|
+
fetch
|
|
70
|
+
}), [
|
|
71
|
+
current,
|
|
72
|
+
history,
|
|
73
|
+
navigate,
|
|
74
|
+
back,
|
|
75
|
+
loading,
|
|
76
|
+
error,
|
|
77
|
+
fetch
|
|
78
|
+
]);
|
|
62
79
|
return /* @__PURE__ */ jsx(NavigationContext.Provider, {
|
|
63
|
-
value
|
|
64
|
-
current,
|
|
65
|
-
history,
|
|
66
|
-
navigate,
|
|
67
|
-
back,
|
|
68
|
-
loading,
|
|
69
|
-
error,
|
|
70
|
-
fetch
|
|
71
|
-
},
|
|
80
|
+
value,
|
|
72
81
|
children
|
|
73
82
|
});
|
|
74
83
|
}
|
|
@@ -95,6 +104,24 @@ const parser = new XMLParser({
|
|
|
95
104
|
trimValues: true
|
|
96
105
|
});
|
|
97
106
|
/**
|
|
107
|
+
* Push all `atom:link` entries from a single link element (or array of them)
|
|
108
|
+
* onto `links`.
|
|
109
|
+
*/
|
|
110
|
+
function pushLinkEntries(linkData, links) {
|
|
111
|
+
const linkArray = Array.isArray(linkData) ? linkData : [linkData];
|
|
112
|
+
for (const link of linkArray) if (link && typeof link === "object") {
|
|
113
|
+
const linkObj = link;
|
|
114
|
+
const href = linkObj["@_href"];
|
|
115
|
+
const rel = linkObj["@_rel"];
|
|
116
|
+
if (href && rel) links.push({
|
|
117
|
+
href,
|
|
118
|
+
rel,
|
|
119
|
+
type: linkObj["@_type"],
|
|
120
|
+
title: linkObj["@_title"]
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
98
125
|
* Extract all atom:link elements from parsed XML object
|
|
99
126
|
*/
|
|
100
127
|
function extractLinks(obj, links = []) {
|
|
@@ -103,21 +130,7 @@ function extractLinks(obj, links = []) {
|
|
|
103
130
|
else {
|
|
104
131
|
const record = obj;
|
|
105
132
|
const linkKey = Object.keys(record).find((k) => k === "atom:link" || k === "link" || k.endsWith(":link"));
|
|
106
|
-
if (linkKey)
|
|
107
|
-
const linkData = record[linkKey];
|
|
108
|
-
const linkArray = Array.isArray(linkData) ? linkData : [linkData];
|
|
109
|
-
for (const link of linkArray) if (link && typeof link === "object") {
|
|
110
|
-
const linkObj = link;
|
|
111
|
-
const href = linkObj["@_href"];
|
|
112
|
-
const rel = linkObj["@_rel"];
|
|
113
|
-
if (href && rel) links.push({
|
|
114
|
-
href,
|
|
115
|
-
rel,
|
|
116
|
-
type: linkObj["@_type"],
|
|
117
|
-
title: linkObj["@_title"]
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
}
|
|
133
|
+
if (linkKey) pushLinkEntries(record[linkKey], links);
|
|
121
134
|
for (const key of Object.keys(record)) if (key !== linkKey) extractLinks(record[key], links);
|
|
122
135
|
}
|
|
123
136
|
return links;
|
|
@@ -286,39 +299,45 @@ function load(xml) {
|
|
|
286
299
|
return transformToTransportData(transportmanagmentSingle.parse(xml));
|
|
287
300
|
}
|
|
288
301
|
/**
|
|
302
|
+
* Normalize an XSD element that may appear once or many times.
|
|
303
|
+
*/
|
|
304
|
+
function asArray(value) {
|
|
305
|
+
return Array.isArray(value) ? value : [value];
|
|
306
|
+
}
|
|
307
|
+
/** Child tasks for a request view (always empty for task views). */
|
|
308
|
+
function extractTasks(request, isTask) {
|
|
309
|
+
const tasks = [];
|
|
310
|
+
if (request.task && !isTask) for (const task of asArray(request.task)) tasks.push({
|
|
311
|
+
number: task.number ?? "",
|
|
312
|
+
owner: task.owner ?? "",
|
|
313
|
+
desc: task.desc ?? "",
|
|
314
|
+
status: task.status ?? "",
|
|
315
|
+
statusText: task.status_text ?? "",
|
|
316
|
+
uri: task.uri ?? ""
|
|
317
|
+
});
|
|
318
|
+
return tasks;
|
|
319
|
+
}
|
|
320
|
+
/** Objects carried by the task element (task views only). */
|
|
321
|
+
function extractObjects(request, isTask) {
|
|
322
|
+
const objects = [];
|
|
323
|
+
if (!request.task || !isTask) return objects;
|
|
324
|
+
for (const task of asArray(request.task)) if (task.abap_object) for (const obj of asArray(task.abap_object)) objects.push({
|
|
325
|
+
pgmid: obj.pgmid ?? "",
|
|
326
|
+
type: obj.type ?? "",
|
|
327
|
+
name: obj.name ?? "",
|
|
328
|
+
info: obj.obj_info ?? obj.obj_desc ?? "",
|
|
329
|
+
lockStatus: obj.lock_status ?? "",
|
|
330
|
+
uri: obj.uri
|
|
331
|
+
});
|
|
332
|
+
return objects;
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
289
335
|
* Transform parsed XSD data to TransportData
|
|
290
336
|
*/
|
|
291
337
|
function transformToTransportData(data) {
|
|
292
338
|
const request = data.root?.request;
|
|
293
339
|
if (!request) throw new Error("No request element found in transport data");
|
|
294
340
|
const isTask = !!request.parent;
|
|
295
|
-
const tasks = [];
|
|
296
|
-
if (request.task && !isTask) {
|
|
297
|
-
const taskArray = Array.isArray(request.task) ? request.task : [request.task];
|
|
298
|
-
for (const task of taskArray) tasks.push({
|
|
299
|
-
number: task.number ?? "",
|
|
300
|
-
owner: task.owner ?? "",
|
|
301
|
-
desc: task.desc ?? "",
|
|
302
|
-
status: task.status ?? "",
|
|
303
|
-
statusText: task.status_text ?? "",
|
|
304
|
-
uri: task.uri ?? ""
|
|
305
|
-
});
|
|
306
|
-
}
|
|
307
|
-
const objects = [];
|
|
308
|
-
if (request.task && isTask) {
|
|
309
|
-
const taskArray = Array.isArray(request.task) ? request.task : [request.task];
|
|
310
|
-
for (const task of taskArray) if (task.abap_object) {
|
|
311
|
-
const objArray = Array.isArray(task.abap_object) ? task.abap_object : [task.abap_object];
|
|
312
|
-
for (const obj of objArray) objects.push({
|
|
313
|
-
pgmid: obj.pgmid ?? "",
|
|
314
|
-
type: obj.type ?? "",
|
|
315
|
-
name: obj.name ?? "",
|
|
316
|
-
info: obj.obj_info ?? obj.obj_desc ?? "",
|
|
317
|
-
lockStatus: obj.lock_status ?? "",
|
|
318
|
-
uri: obj.uri
|
|
319
|
-
});
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
341
|
return {
|
|
323
342
|
isTask,
|
|
324
343
|
number: request.number ?? "",
|
|
@@ -330,8 +349,8 @@ function transformToTransportData(data) {
|
|
|
330
349
|
statusText: request.status_text ?? "",
|
|
331
350
|
target: request.target ?? "",
|
|
332
351
|
targetDesc: request.target_desc ?? "",
|
|
333
|
-
tasks,
|
|
334
|
-
objects,
|
|
352
|
+
tasks: extractTasks(request, isTask),
|
|
353
|
+
objects: extractObjects(request, isTask),
|
|
335
354
|
raw: data
|
|
336
355
|
};
|
|
337
356
|
}
|
|
@@ -355,15 +374,9 @@ function getStatusColor(status) {
|
|
|
355
374
|
}
|
|
356
375
|
}
|
|
357
376
|
/**
|
|
358
|
-
*
|
|
377
|
+
* Build the menu: back, parent link, tasks, objects, and link-driven actions.
|
|
359
378
|
*/
|
|
360
|
-
function
|
|
361
|
-
let transport;
|
|
362
|
-
try {
|
|
363
|
-
transport = load(response.raw);
|
|
364
|
-
} catch (_e) {
|
|
365
|
-
return null;
|
|
366
|
-
}
|
|
379
|
+
function buildTransportMenu(transport, response) {
|
|
367
380
|
const menu = [{
|
|
368
381
|
key: "back",
|
|
369
382
|
label: "Back",
|
|
@@ -422,6 +435,19 @@ function transportPage({ url: _url, response }) {
|
|
|
422
435
|
href: link.href
|
|
423
436
|
});
|
|
424
437
|
}
|
|
438
|
+
return menu;
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* Transport Page - returns PageResult for framework rendering
|
|
442
|
+
*/
|
|
443
|
+
function transportPage({ url: _url, response }) {
|
|
444
|
+
let transport;
|
|
445
|
+
try {
|
|
446
|
+
transport = load(response.raw);
|
|
447
|
+
} catch (_e) {
|
|
448
|
+
return null;
|
|
449
|
+
}
|
|
450
|
+
const menu = buildTransportMenu(transport, response);
|
|
425
451
|
const content = /* @__PURE__ */ jsxs(Box, {
|
|
426
452
|
flexDirection: "column",
|
|
427
453
|
children: [
|
|
@@ -504,7 +530,7 @@ function transportPage({ url: _url, response }) {
|
|
|
504
530
|
const isTask = transport.isTask;
|
|
505
531
|
return {
|
|
506
532
|
title: `${isTask ? "Task" : "Transport Request"}: ${transport.number}`,
|
|
507
|
-
icon: isTask ? "
|
|
533
|
+
icon: isTask ? "📋" : "📦",
|
|
508
534
|
color: isTask ? "yellow" : "cyan",
|
|
509
535
|
subtitle: isTask && transport.parent ? `Parent: ${transport.parent}` : void 0,
|
|
510
536
|
content,
|
|
@@ -520,7 +546,7 @@ function transportPage({ url: _url, response }) {
|
|
|
520
546
|
*/
|
|
521
547
|
const routes = [{
|
|
522
548
|
pattern: "/sap/bc/adt/cts/transportrequests/:slug",
|
|
523
|
-
regex:
|
|
549
|
+
regex: /^\/sap\/bc\/adt\/cts\/transportrequests\/([^/]+)$/,
|
|
524
550
|
page: transportPage
|
|
525
551
|
}];
|
|
526
552
|
/**
|
|
@@ -572,14 +598,14 @@ function genericPage({ url, response }) {
|
|
|
572
598
|
}), /* @__PURE__ */ jsx(Box, {
|
|
573
599
|
marginTop: 1,
|
|
574
600
|
flexDirection: "column",
|
|
575
|
-
children: summary.length > 0 ? summary.map((item
|
|
601
|
+
children: summary.length > 0 ? summary.map((item) => /* @__PURE__ */ jsxs(Text, { children: [
|
|
576
602
|
/* @__PURE__ */ jsxs(Text, {
|
|
577
603
|
color: "yellow",
|
|
578
604
|
children: [item.key, ":"]
|
|
579
605
|
}),
|
|
580
606
|
" ",
|
|
581
607
|
item.value
|
|
582
|
-
] },
|
|
608
|
+
] }, item.key)) : /* @__PURE__ */ jsx(Text, {
|
|
583
609
|
dimColor: true,
|
|
584
610
|
children: "No summary available"
|
|
585
611
|
})
|
|
@@ -595,6 +621,29 @@ function genericPage({ url, response }) {
|
|
|
595
621
|
};
|
|
596
622
|
}
|
|
597
623
|
/**
|
|
624
|
+
* Collect one interesting `@_` attribute into the summary (dedup + allow-list).
|
|
625
|
+
*/
|
|
626
|
+
function collectSummaryAttr(summary, seen, key, value) {
|
|
627
|
+
if (!(key.startsWith("@_") && typeof value === "string") && typeof value !== "number") return;
|
|
628
|
+
const cleanKey = key.replace("@_", "").replace(/^\w+:/, "");
|
|
629
|
+
if (seen.has(cleanKey) || cleanKey === "version" || cleanKey === "encoding") return;
|
|
630
|
+
seen.add(cleanKey);
|
|
631
|
+
if ([
|
|
632
|
+
"number",
|
|
633
|
+
"owner",
|
|
634
|
+
"desc",
|
|
635
|
+
"status",
|
|
636
|
+
"status_text",
|
|
637
|
+
"type",
|
|
638
|
+
"target",
|
|
639
|
+
"name",
|
|
640
|
+
"uri"
|
|
641
|
+
].some((f) => cleanKey.includes(f))) summary.push({
|
|
642
|
+
key: cleanKey,
|
|
643
|
+
value: String(value)
|
|
644
|
+
});
|
|
645
|
+
}
|
|
646
|
+
/**
|
|
598
647
|
* Extract key fields for summary display
|
|
599
648
|
*/
|
|
600
649
|
function extractSummary(data) {
|
|
@@ -609,25 +658,7 @@ function extractSummary(data) {
|
|
|
609
658
|
const record = obj;
|
|
610
659
|
for (const [key, value] of Object.entries(record)) {
|
|
611
660
|
if (key.includes("link") || key === "?xml") continue;
|
|
612
|
-
|
|
613
|
-
const cleanKey = key.replace("@_", "").replace(/^\w+:/, "");
|
|
614
|
-
if (seen.has(cleanKey) || cleanKey === "version" || cleanKey === "encoding") continue;
|
|
615
|
-
seen.add(cleanKey);
|
|
616
|
-
if ([
|
|
617
|
-
"number",
|
|
618
|
-
"owner",
|
|
619
|
-
"desc",
|
|
620
|
-
"status",
|
|
621
|
-
"status_text",
|
|
622
|
-
"type",
|
|
623
|
-
"target",
|
|
624
|
-
"name",
|
|
625
|
-
"uri"
|
|
626
|
-
].some((f) => cleanKey.includes(f))) summary.push({
|
|
627
|
-
key: cleanKey,
|
|
628
|
-
value: String(value)
|
|
629
|
-
});
|
|
630
|
-
}
|
|
661
|
+
collectSummaryAttr(summary, seen, key, value);
|
|
631
662
|
if (typeof value === "object") extract(value, key);
|
|
632
663
|
}
|
|
633
664
|
}
|
|
@@ -785,7 +816,8 @@ function UrlPrompt({ onSubmit }) {
|
|
|
785
816
|
* Main TUI Application
|
|
786
817
|
*/
|
|
787
818
|
function App({ startUrl, fetch, router: _router, systemName }) {
|
|
788
|
-
const [, setResizeKey] = useState(0);
|
|
819
|
+
const [resizeKey, setResizeKey] = useState(0);
|
|
820
|
+
useDebugValue(resizeKey);
|
|
789
821
|
useEffect(() => {
|
|
790
822
|
const handleResize = () => {
|
|
791
823
|
setResizeKey((k) => k + 1);
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["Page0"],"sources":["../src/lib/context.tsx","../src/lib/parser.ts","../src/lib/PageRenderer.tsx","../src/pages/sap/bc/adt/cts/transportrequests/[slug].loader.ts","../src/pages/sap/bc/adt/cts/transportrequests/[slug].tsx","../src/pages/_routes.ts","../src/pages/GenericPage.tsx","../src/Navigator.tsx","../src/App.tsx","../src/run.tsx"],"sourcesContent":["/**\n * Navigation Context\n *\n * React context for sharing navigation state across components.\n */\n\nimport React, {\n createContext,\n useContext,\n useState,\n useCallback,\n type ReactNode,\n} from 'react';\nimport type { NavigationEntry, ParsedResponse, FetchFn } from './types';\n\ninterface NavigationContextValue {\n /** Current navigation entry */\n current: NavigationEntry | null;\n /** Navigation history */\n history: NavigationEntry[];\n /** Navigate to URL */\n navigate: (url: string) => Promise<void>;\n /** Go back */\n back: () => void;\n /** Is loading */\n loading: boolean;\n /** Error message */\n error: string | null;\n /** Fetch function */\n fetch: FetchFn;\n}\n\nconst NavigationContext = createContext<NavigationContextValue | null>(null);\n\ninterface NavigationProviderProps {\n children: ReactNode;\n /** Fetch function for making requests */\n fetch: FetchFn;\n /** Parse response function */\n parseResponse: (xml: string) => ParsedResponse;\n /** Initial URL to load */\n initialUrl?: string;\n}\n\nexport function NavigationProvider({\n children,\n fetch,\n parseResponse,\n initialUrl,\n}: NavigationProviderProps): React.ReactNode {\n const [current, setCurrent] = useState<NavigationEntry | null>(null);\n const [history, setHistory] = useState<NavigationEntry[]>([]);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const navigate = useCallback(\n async (url: string) => {\n setLoading(true);\n setError(null);\n\n try {\n const response = await fetch(url, {\n method: 'GET',\n headers: { Accept: '*/*' },\n });\n\n const parsed = parseResponse(response);\n const entry: NavigationEntry = {\n url,\n response: parsed,\n timestamp: new Date(),\n };\n\n // Push current to history if exists\n if (current) {\n setHistory((prev: NavigationEntry[]) => [...prev, current]);\n }\n\n setCurrent(entry);\n } catch (err) {\n setError(err instanceof Error ? err.message : String(err));\n } finally {\n setLoading(false);\n }\n },\n [current, fetch, parseResponse],\n );\n\n const back = useCallback(() => {\n if (history.length === 0) return;\n\n const prev = history[history.length - 1];\n setHistory((h: NavigationEntry[]) => h.slice(0, -1));\n setCurrent(prev);\n }, [history]);\n\n // Load initial URL on mount\n React.useEffect(() => {\n if (initialUrl && !current) {\n navigate(initialUrl);\n }\n }, [initialUrl, current, navigate]);\n\n return (\n <NavigationContext.Provider\n value={{\n current,\n history,\n navigate,\n back,\n loading,\n error,\n fetch,\n }}\n >\n {children}\n </NavigationContext.Provider>\n );\n}\n\n/**\n * Hook to access navigation context\n */\nexport function useNavigation(): NavigationContextValue {\n const context = useContext(NavigationContext);\n if (!context) {\n throw new Error('useNavigation must be used within NavigationProvider');\n }\n return context;\n}\n","/**\n * XML Response Parser\n *\n * Parses ADT XML responses and extracts hypermedia links.\n */\n\nimport { XMLParser } from 'fast-xml-parser';\nimport type { ParsedResponse, HypermediaLink } from './types';\n\nconst parser = new XMLParser({\n ignoreAttributes: false,\n attributeNamePrefix: '@_',\n removeNSPrefix: false,\n parseAttributeValue: true,\n trimValues: true,\n});\n\n/**\n * Extract all atom:link elements from parsed XML object\n */\nfunction extractLinks(\n obj: unknown,\n links: HypermediaLink[] = [],\n): HypermediaLink[] {\n if (!obj || typeof obj !== 'object') return links;\n\n if (Array.isArray(obj)) {\n for (const item of obj) {\n extractLinks(item, links);\n }\n } else {\n const record = obj as Record<string, unknown>;\n\n const linkKey = Object.keys(record).find(\n (k) => k === 'atom:link' || k === 'link' || k.endsWith(':link'),\n );\n\n if (linkKey) {\n const linkData = record[linkKey];\n const linkArray = Array.isArray(linkData) ? linkData : [linkData];\n\n for (const link of linkArray) {\n if (link && typeof link === 'object') {\n const linkObj = link as Record<string, unknown>;\n const href = linkObj['@_href'] as string;\n const rel = linkObj['@_rel'] as string;\n\n if (href && rel) {\n links.push({\n href,\n rel,\n type: linkObj['@_type'] as string | undefined,\n title: linkObj['@_title'] as string | undefined,\n });\n }\n }\n }\n }\n\n for (const key of Object.keys(record)) {\n if (key !== linkKey) {\n extractLinks(record[key], links);\n }\n }\n }\n\n return links;\n}\n\n/**\n * Detect root element and namespace from parsed XML\n */\nfunction detectRootInfo(data: Record<string, unknown>): {\n namespace?: string;\n rootElement?: string;\n} {\n const keys = Object.keys(data).filter((k) => k !== '?xml');\n if (keys.length === 0) return {};\n\n const rootKey = keys[0];\n const colonIndex = rootKey.indexOf(':');\n\n if (colonIndex > 0) {\n return {\n namespace: rootKey.substring(0, colonIndex),\n rootElement: rootKey.substring(colonIndex + 1),\n };\n }\n\n return { rootElement: rootKey };\n}\n\n/**\n * Parse ADT XML response\n */\nexport function parseResponse(xml: string): ParsedResponse {\n const data = parser.parse(xml) as Record<string, unknown>;\n const links = extractLinks(data);\n const { namespace, rootElement } = detectRootInfo(data);\n\n return {\n raw: xml,\n data,\n links,\n namespace,\n rootElement,\n };\n}\n\n/**\n * Get action name from relation URL\n */\nexport function getActionName(rel: string): string {\n const lastSlash = rel.lastIndexOf('/');\n return lastSlash >= 0 ? rel.substring(lastSlash + 1) : rel;\n}\n\n/**\n * Categorize links by type\n */\nexport function categorizeLinks(links: HypermediaLink[]): {\n navigation: HypermediaLink[];\n actions: HypermediaLink[];\n self: HypermediaLink[];\n} {\n const navigation: HypermediaLink[] = [];\n const actions: HypermediaLink[] = [];\n const self: HypermediaLink[] = [];\n\n for (const link of links) {\n if (link.rel === 'self') {\n self.push(link);\n } else if (\n link.rel.includes('/relations/') ||\n link.rel.includes('adturi')\n ) {\n actions.push(link);\n } else {\n navigation.push(link);\n }\n }\n\n return { navigation, actions, self };\n}\n","/**\n * PageRenderer - Framework component that renders PageResult\n *\n * Handles:\n * - Header with title/icon\n * - Page content\n * - Menu navigation with keyboard\n * - Footer\n */\n\nimport { useState } from 'react';\nimport { Box, Text, useInput } from 'ink';\nimport type { PageResult, MenuItem } from './types';\n\ninterface PageRendererProps {\n result: PageResult;\n onNavigate: (url: string) => void;\n onBack: () => void;\n onAction?: (key: string) => void;\n}\n\n/**\n * Render a single menu item\n */\nfunction MenuItemRow({\n item,\n isSelected,\n}: {\n item: MenuItem;\n isSelected: boolean;\n}) {\n const prefix = isSelected ? '❯ ' : ' ';\n\n return (\n <Text color={isSelected ? 'cyan' : undefined}>\n {prefix}\n {item.icon && `${item.icon} `}\n {item.label}\n {item.status && (\n <Text color={item.status.color} dimColor={!isSelected}>\n {' '}\n [{item.status.text}]\n </Text>\n )}\n {item.info && <Text dimColor> {item.info}</Text>}\n </Text>\n );\n}\n\n/**\n * PageRenderer component\n */\nexport function PageRenderer({\n result,\n onNavigate,\n onBack,\n onAction,\n}: PageRendererProps): React.ReactNode {\n const [selectedIndex, setSelectedIndex] = useState(0);\n\n // Handle keyboard input\n useInput((input, key) => {\n if (key.upArrow) {\n setSelectedIndex((i) => Math.max(0, i - 1));\n } else if (key.downArrow) {\n setSelectedIndex((i) => Math.min(result.menu.length - 1, i + 1));\n } else if (key.return) {\n const item = result.menu[selectedIndex];\n if (item.isBack) {\n onBack();\n } else if (item.href) {\n onNavigate(item.href);\n } else if (onAction) {\n onAction(item.key);\n }\n } else if (input === 'o' || input === 'O') {\n // Open XML in VS Code\n onAction?.('openXml');\n } else if (input === 'e' || input === 'E') {\n // Open in Eclipse ADT\n onAction?.('openAdt');\n }\n });\n\n return (\n <Box flexDirection=\"column\" padding={1}>\n {/* Header */}\n <Box\n borderStyle=\"round\"\n borderColor={result.color || 'cyan'}\n paddingX={2}\n marginBottom={1}\n >\n <Text bold color={result.color || 'cyan'}>\n {result.icon && `${result.icon} `}\n {result.title}\n </Text>\n {result.subtitle && <Text dimColor> ({result.subtitle})</Text>}\n </Box>\n\n {/* Content */}\n <Box flexDirection=\"column\" marginBottom={1}>\n {result.content}\n </Box>\n\n {/* Menu */}\n <Box flexDirection=\"column\">\n {result.menu.map((item, i) => (\n <MenuItemRow\n key={item.key}\n item={item}\n isSelected={i === selectedIndex}\n />\n ))}\n </Box>\n\n {/* Footer */}\n <Box marginTop={1}>\n <Text dimColor>\n {result.footer || '[↑↓] navigate | [Enter] select'}\n </Text>\n </Box>\n </Box>\n );\n}\n","/**\n * Transport Request/Task Loader\n *\n * Uses adt-contracts for type-safe XML parsing.\n */\n\nimport {\n transportmanagmentSingle,\n type TransportResponse,\n} from '@abapify/adt-contracts';\n\n// Re-export the schema for type inference\nexport { transportmanagmentSingle as schema };\n\n// Use type from contracts package\ntype TransportRoot = TransportResponse;\n\n// Extract nested types - TransportResponse has root.request structure\nexport type Request = NonNullable<\n NonNullable<TransportRoot['root']>['request']\n>;\nexport type Task = NonNullable<Request['task']>[number];\nexport type AbapObject = NonNullable<Task['abap_object']>[number];\n\n/**\n * Parsed transport data with computed properties\n */\nexport interface TransportData {\n /** Whether this is a task (vs request) */\n isTask: boolean;\n /** Transport/Task number */\n number: string;\n /** Parent request number (for tasks) */\n parent?: string;\n /** Owner user ID */\n owner: string;\n /** Description */\n desc: string;\n /** Transport type */\n type: string;\n /** Status code (D=Modifiable, R=Released, O=Protected) */\n status: string;\n /** Status text */\n statusText: string;\n /** Target system */\n target: string;\n /** Target description */\n targetDesc: string;\n /** Child tasks (for requests) */\n tasks: TaskInfo[];\n /** Objects (for tasks) */\n objects: ObjectInfo[];\n /** Raw parsed data */\n raw: TransportRoot;\n}\n\nexport interface TaskInfo {\n number: string;\n owner: string;\n desc: string;\n status: string;\n statusText: string;\n uri: string;\n}\n\nexport interface ObjectInfo {\n pgmid: string;\n type: string;\n name: string;\n info: string;\n lockStatus: string;\n uri?: string;\n}\n\n/**\n * Load and parse transport request/task XML\n */\nexport function load(xml: string): TransportData {\n const parsed = transportmanagmentSingle.parse(xml);\n return transformToTransportData(parsed);\n}\n\n/**\n * Transform parsed XSD data to TransportData\n */\nfunction transformToTransportData(data: TransportRoot): TransportData {\n const request = data.root?.request;\n\n if (!request) {\n throw new Error('No request element found in transport data');\n }\n\n // Check if this is a task view (has parent) or request view\n const isTask = !!request.parent;\n\n // Extract tasks\n const tasks: TaskInfo[] = [];\n if (request.task && !isTask) {\n const taskArray = Array.isArray(request.task)\n ? request.task\n : [request.task];\n for (const task of taskArray) {\n tasks.push({\n number: task.number ?? '',\n owner: task.owner ?? '',\n desc: task.desc ?? '',\n status: task.status ?? '',\n statusText: task.status_text ?? '',\n uri: task.uri ?? '',\n });\n }\n }\n\n // Extract objects (from task's abap_object)\n const objects: ObjectInfo[] = [];\n if (request.task && isTask) {\n // When viewing a task, the task element contains objects\n const taskArray = Array.isArray(request.task)\n ? request.task\n : [request.task];\n for (const task of taskArray) {\n if (task.abap_object) {\n const objArray = Array.isArray(task.abap_object)\n ? task.abap_object\n : [task.abap_object];\n for (const obj of objArray) {\n objects.push({\n pgmid: obj.pgmid ?? '',\n type: obj.type ?? '',\n name: obj.name ?? '',\n info: obj.obj_info ?? obj.obj_desc ?? '',\n lockStatus: obj.lock_status ?? '',\n uri: obj.uri,\n });\n }\n }\n }\n }\n\n return {\n isTask,\n number: request.number ?? '',\n parent: request.parent,\n owner: request.owner ?? '',\n desc: request.desc ?? '',\n type: request.type ?? '',\n status: request.status ?? '',\n statusText: request.status_text ?? '',\n target: request.target ?? '',\n targetDesc: request.target_desc ?? '',\n tasks,\n objects,\n raw: data,\n };\n}\n","/**\n * Transport Request/Task Page\n *\n * Returns PageResult for framework-driven rendering.\n * Uses schema-driven parsing via [slug].loader.ts\n */\n\nimport { Box, Text } from 'ink';\nimport type {\n PageProps,\n PageResult,\n MenuItem,\n} from '../../../../../../lib/types';\nimport { load } from './[slug].loader';\n\n/**\n * Get status color\n */\nfunction getStatusColor(status: string): string {\n switch (status) {\n case 'D':\n return 'yellow'; // Modifiable\n case 'R':\n return 'green'; // Released\n case 'O':\n return 'cyan'; // Protected\n default:\n return 'white';\n }\n}\n\n/**\n * Transport Page - returns PageResult for framework rendering\n */\nexport function transportPage({\n url: _url,\n response,\n}: PageProps): PageResult | null {\n // Parse transport data\n let transport;\n try {\n transport = load(response.raw);\n } catch (_e) {\n return null;\n }\n\n // Build menu items\n const menu: MenuItem[] = [\n { key: 'back', label: 'Back', icon: '←', isBack: true },\n ];\n\n // Add parent link for tasks\n if (transport.isTask && transport.parent) {\n menu.push({\n key: 'parent',\n label: 'Go to Parent Request',\n icon: '⬆️',\n href: `/sap/bc/adt/cts/transportrequests/${transport.parent}`,\n });\n }\n\n // Add tasks (for requests)\n for (const task of transport.tasks) {\n menu.push({\n key: task.number,\n label: `${task.number} - ${task.desc || '(no desc)'}`,\n icon: '📝',\n href: task.uri,\n info: `@${task.owner}`,\n status: { text: task.statusText, color: getStatusColor(task.status) },\n });\n }\n\n // Add objects (for tasks)\n for (const obj of transport.objects) {\n const lockIcon = obj.lockStatus === 'X' ? '🔒' : '📦';\n menu.push({\n key: `${obj.pgmid}-${obj.name}`,\n label: `${obj.type} ${obj.name}`,\n icon: lockIcon,\n href: obj.uri,\n info: obj.uri ? undefined : '(no link)',\n });\n }\n\n // Add actions from links\n const actions = [\n { rel: 'consistencycheck', label: 'Consistency Check', icon: '🔍' },\n { rel: 'newreleasejobs', label: 'Release', icon: '🚀' },\n { rel: 'transportchecks', label: 'Transport Checks', icon: '✅' },\n ];\n\n for (const action of actions) {\n const link = response.links.find((l) => l.rel.includes(action.rel));\n if (link) {\n menu.push({\n key: action.rel,\n label: action.label,\n icon: action.icon,\n href: link.href,\n });\n }\n }\n\n // Build content\n const content = (\n <Box flexDirection=\"column\">\n <Text>\n <Text bold>Description:</Text> {transport.desc || '(no description)'}\n </Text>\n <Text>\n <Text bold>Owner:</Text> {transport.owner}\n </Text>\n <Text>\n <Text bold>Type:</Text> {transport.type}\n </Text>\n <Text>\n <Text bold>Status:</Text>{' '}\n <Text color={getStatusColor(transport.status)}>\n {transport.statusText} ({transport.status})\n </Text>\n </Text>\n {transport.target && (\n <Text>\n <Text bold>Target:</Text> {transport.target}{' '}\n {transport.targetDesc && `(${transport.targetDesc})`}\n </Text>\n )}\n {transport.tasks.length > 0 && (\n <Box marginTop={1}>\n <Text bold color=\"yellow\">\n 📋 Tasks ({transport.tasks.length}):\n </Text>\n </Box>\n )}\n {transport.objects.length > 0 && (\n <Box marginTop={1}>\n <Text bold color=\"cyan\">\n 📦 Objects ({transport.objects.length}):\n </Text>\n </Box>\n )}\n </Box>\n );\n\n const isTask = transport.isTask;\n return {\n title: `${isTask ? 'Task' : 'Transport Request'}: ${transport.number}`,\n icon: isTask ? '�' : '�',\n color: isTask ? 'yellow' : 'cyan',\n subtitle:\n isTask && transport.parent ? `Parent: ${transport.parent}` : undefined,\n content,\n menu,\n footer: '[o] XML in VS Code | [e] open in Eclipse ADT',\n };\n}\n\n// Default export for file-based routing\nexport default transportPage;\n","/**\n * Auto-generated route manifest\n * DO NOT EDIT - Generated by scripts/generate-routes.ts\n */\n\nimport { default as Page0 } from './sap/bc/adt/cts/transportrequests/[slug]';\nimport type { PageComponent } from '../lib/types';\n\nexport interface Route {\n pattern: string;\n regex: RegExp;\n page: PageComponent;\n}\n\nexport const routes: Route[] = [\n {\n pattern: '/sap/bc/adt/cts/transportrequests/:slug',\n regex: new RegExp('^/sap/bc/adt/cts/transportrequests/([^/]+)$'),\n page: Page0,\n },\n];\n\n/**\n * Match URL to route\n */\nexport function matchRoute(url: string): Route | null {\n for (const route of routes) {\n if (route.regex.test(url)) {\n return route;\n }\n }\n return null;\n}\n","/**\n * Generic Page Component\n *\n * Fallback page for unknown endpoints - shows payload + links.\n * Returns PageResult for framework-driven rendering.\n */\n\n// React import not needed with JSX transform\nimport { Box, Text } from 'ink';\nimport type { PageProps, PageResult, MenuItem } from '../lib/types';\nimport { getActionName } from '../lib/parser';\n\ninterface SummaryItem {\n key: string;\n value: string;\n}\n\n/**\n * Generic page - returns PageResult for framework rendering\n */\nexport function genericPage({ url, response }: PageProps): PageResult {\n // Extract summary from data\n const summary = extractSummary(response.data);\n\n // Build menu items from links\n const menu: MenuItem[] = [\n { key: 'back', label: 'Back', icon: '←', isBack: true },\n ];\n\n for (const link of response.links) {\n const title = link.title || getActionName(link.rel);\n\n // Make path relative/shorter for display\n let displayPath = link.href;\n const currentBase = url.split('/').slice(0, -1).join('/');\n if (link.href.startsWith(currentBase)) {\n displayPath = '.' + link.href.slice(currentBase.length);\n } else if (link.href.startsWith('/sap/bc/adt/')) {\n displayPath = link.href.replace('/sap/bc/adt/', '/');\n }\n if (displayPath.length > 40) {\n displayPath = '...' + displayPath.slice(-37);\n }\n\n menu.push({\n key: link.href,\n label: title,\n icon: '🔗',\n href: link.href,\n info: displayPath,\n });\n }\n\n // Build content\n const content = (\n <Box flexDirection=\"column\">\n <Text dimColor>URL: {url}</Text>\n <Box marginTop={1} flexDirection=\"column\">\n {summary.length > 0 ? (\n summary.map((item, i) => (\n <Text key={i}>\n <Text color=\"yellow\">{item.key}:</Text> {item.value}\n </Text>\n ))\n ) : (\n <Text dimColor>No summary available</Text>\n )}\n </Box>\n </Box>\n );\n\n return {\n title: `${response.namespace || 'unknown'}:${response.rootElement || 'response'}`,\n icon: '📄',\n color: 'cyan',\n content,\n menu,\n footer: `[o] XML | [e] Eclipse ADT | ${response.links.length} links`,\n };\n}\n\n/**\n * Extract key fields for summary display\n */\nfunction extractSummary(data: Record<string, unknown>): SummaryItem[] {\n const summary: SummaryItem[] = [];\n const seen = new Set<string>();\n\n function extract(obj: unknown, prefix = ''): void {\n if (!obj || typeof obj !== 'object') return;\n if (Array.isArray(obj)) {\n obj.forEach((item) => extract(item, prefix));\n return;\n }\n\n const record = obj as Record<string, unknown>;\n for (const [key, value] of Object.entries(record)) {\n // Skip links and xml declaration\n if (key.includes('link') || key === '?xml') continue;\n\n // Extract interesting attributes (those starting with @_)\n if (\n (key.startsWith('@_') && typeof value === 'string') ||\n typeof value === 'number'\n ) {\n const cleanKey = key.replace('@_', '').replace(/^\\w+:/, '');\n // Skip duplicates and uninteresting fields\n if (\n seen.has(cleanKey) ||\n cleanKey === 'version' ||\n cleanKey === 'encoding'\n )\n continue;\n seen.add(cleanKey);\n\n // Limit to important fields\n const important = [\n 'number',\n 'owner',\n 'desc',\n 'status',\n 'status_text',\n 'type',\n 'target',\n 'name',\n 'uri',\n ];\n if (important.some((f) => cleanKey.includes(f))) {\n summary.push({ key: cleanKey, value: String(value) });\n }\n }\n\n // Recurse into nested objects\n if (typeof value === 'object') {\n extract(value, key);\n }\n }\n }\n\n extract(data);\n return summary.slice(0, 8); // Limit to 8 items\n}\n\n// removeLinks function removed - was unused\n","/**\n * Navigator Component\n *\n * Main entry point - renders pages using the framework.\n * Pages return PageResult, Navigator handles rendering.\n */\n\nimport { useState, useMemo } from 'react';\nimport { Box, Text } from 'ink';\nimport Spinner from 'ink-spinner';\nimport TextInput from 'ink-text-input';\nimport { writeFileSync, existsSync } from 'node:fs';\nimport { tmpdir } from 'node:os';\nimport { join } from 'node:path';\nimport { exec, execFile } from 'node:child_process';\nimport { NavigationProvider, useNavigation } from './lib/context';\nimport { parseResponse } from './lib/parser';\nimport { PageRenderer } from './lib/PageRenderer';\nimport { matchRoute } from './pages/_routes';\nimport { genericPage } from './pages/GenericPage';\nimport type { FetchFn, PageComponent, PageResult } from './lib/types';\n\ninterface NavigatorProps {\n /** Optional starting URL - if not provided, shows prompt */\n startUrl?: string;\n /** Fetch function */\n fetch: FetchFn;\n /** SAP system name for ADT links (e.g., 'TRL', 'NPL') */\n systemName?: string;\n}\n\n/**\n * Open raw XML in IDE (VS Code)\n */\nfunction openXmlInIde(xml: string, url: string): void {\n const safeName = url.replaceAll(/[^a-zA-Z0-9]/g, '_').slice(-50);\n const filename = `adt_${safeName}.xml`;\n const filepath = join(tmpdir(), filename);\n writeFileSync(filepath, xml, 'utf-8');\n exec(`code \"${filepath}\"`, (error) => {\n if (error) {\n exec(`xdg-open \"${filepath}\"`);\n }\n });\n}\n\n/**\n * Resolve absolute path to a launcher binary from an allow-list of known\n * install locations. Prevents PATH-hijack (S4036) by avoiding bare-name\n * lookups when an absolute path is available. Falls back to the bare name\n * only if none of the known paths exist, so packaged distributions with\n * non-standard layouts still function.\n */\nfunction resolveLauncher(\n candidates: readonly string[],\n fallbackName: string,\n): string {\n for (const p of candidates) {\n if (existsSync(p)) return p;\n }\n return fallbackName;\n}\n\nconst XDG_OPEN = resolveLauncher(\n ['/usr/bin/xdg-open', '/usr/local/bin/xdg-open'],\n 'xdg-open',\n);\nconst OPEN_MAC = resolveLauncher(\n ['/usr/bin/open', '/usr/local/bin/open'],\n 'open',\n);\n\n/**\n * Open in Eclipse ADT\n * Format: adt://[system]/[path]\n * @see https://help.sap.com/docs/abap-cloud/abap-development-tools-user-guide/opening-adt-links\n */\nfunction openInAdt(systemName: string, url: string): void {\n const adtUrl = `adt://${systemName}${url}`;\n // Use execFile (arg array, no shell) to avoid shell-command injection\n // via systemName/url. Try xdg-open (Linux), fall back to open (macOS).\n // Paths resolved above via allow-list to prevent PATH-hijack (S4036).\n execFile(XDG_OPEN, [adtUrl], (error) => {\n if (error) {\n execFile(OPEN_MAC, [adtUrl], (err2) => {\n if (err2) {\n console.error('Failed to open ADT link:', err2.message);\n }\n });\n }\n });\n}\n\n/**\n * Inner component that uses navigation context\n */\nfunction NavigatorInner({ systemName }: { systemName?: string }) {\n const { current, loading, error, navigate, back } = useNavigation();\n\n // Get page result\n const pageResult = useMemo<PageResult | null>(() => {\n if (!current) return null;\n\n // Try file-based route matching first\n const fileRoute = matchRoute(current.url);\n const pageFn: PageComponent = fileRoute?.page ?? genericPage;\n\n return pageFn({ url: current.url, response: current.response });\n }, [current]);\n\n // Handle actions\n const handleAction = (key: string) => {\n if (key === 'openXml' && current) {\n openXmlInIde(current.response.raw, current.url);\n } else if (key === 'openAdt' && current && systemName) {\n openInAdt(systemName, current.url);\n }\n };\n\n if (loading) {\n return (\n <Box padding={1}>\n <Text color=\"cyan\">\n <Spinner type=\"dots\" />\n </Text>\n <Text> Loading...</Text>\n </Box>\n );\n }\n\n if (error) {\n return (\n <Box padding={1} flexDirection=\"column\">\n <Text color=\"red\">❌ Error: {error}</Text>\n <Text dimColor>Press Ctrl+C to exit</Text>\n </Box>\n );\n }\n\n if (!current) {\n return <UrlPrompt onSubmit={navigate} />;\n }\n\n if (!pageResult) {\n return (\n <Box padding={1}>\n <Text dimColor>No content loaded</Text>\n </Box>\n );\n }\n\n return (\n <PageRenderer\n result={pageResult}\n onNavigate={navigate}\n onBack={back}\n onAction={handleAction}\n />\n );\n}\n\n/**\n * Main Navigator component\n */\nexport function Navigator({\n startUrl,\n fetch,\n systemName,\n}: NavigatorProps): React.ReactNode {\n return (\n <NavigationProvider\n fetch={fetch}\n parseResponse={parseResponse}\n initialUrl={startUrl}\n >\n <NavigatorInner systemName={systemName} />\n </NavigationProvider>\n );\n}\n\n/**\n * URL input prompt component\n */\nfunction UrlPrompt({ onSubmit }: { onSubmit: (url: string) => void }) {\n const [url, setUrl] = useState('');\n\n const handleSubmit = () => {\n if (url.trim()) {\n onSubmit(url.trim());\n }\n };\n\n return (\n <Box flexDirection=\"column\" padding={1}>\n <Text color=\"cyan\" bold>\n ADT TUI - Hypermedia Navigator\n </Text>\n <Box marginTop={1}>\n <Text>Enter URL: </Text>\n <TextInput value={url} onChange={setUrl} onSubmit={handleSubmit} />\n </Box>\n <Box marginTop={1}>\n <Text dimColor>\n Example: /sap/bc/adt/cts/transportrequests/TRLK900001\n </Text>\n </Box>\n </Box>\n );\n}\n","/**\n * Main App Component\n *\n * Entry point for the TUI application.\n */\n\nimport { useState, useEffect } from 'react';\nimport { Navigator } from './Navigator';\nimport type { FetchFn } from './lib/types';\nimport type { Router } from './lib/router';\n\nexport interface AppProps {\n /** Optional starting URL - if not provided, shows prompt */\n startUrl?: string;\n /** Fetch function for making HTTP requests */\n fetch: FetchFn;\n /** Optional custom router */\n router?: Router;\n /** SAP system name for ADT links (e.g., 'TRL', 'NPL') */\n systemName?: string;\n}\n\n/**\n * Main TUI Application\n */\nexport function App({\n startUrl,\n fetch,\n router: _router,\n systemName,\n}: AppProps): React.ReactNode {\n const [, setResizeKey] = useState(0);\n\n // Handle terminal resize - just trigger re-render, don't clear\n useEffect(() => {\n const handleResize = () => {\n setResizeKey((k) => k + 1);\n };\n\n process.stdout.on('resize', handleResize);\n return () => {\n process.stdout.off('resize', handleResize);\n };\n }, []);\n\n return (\n <Navigator startUrl={startUrl} fetch={fetch} systemName={systemName} />\n );\n}\n","/**\n * Run TUI Application\n *\n * Helper to render the TUI and wait for exit.\n */\n\n// React import not needed with JSX transform\nimport { render } from 'ink';\nimport { App } from './App';\nimport type { FetchFn } from './lib/types';\nimport type { Router } from './lib/router';\n\nexport interface RunOptions {\n /** Optional starting URL - if not provided, shows prompt */\n startUrl?: string;\n /** Fetch function for making HTTP requests */\n fetch: FetchFn;\n /** Optional custom router */\n router?: Router;\n /** SAP system name for ADT links (e.g., 'TRL', 'NPL') */\n systemName?: string;\n /** Called when app exits */\n onExit?: () => void;\n}\n\n/**\n * Run the TUI application\n *\n * @returns Promise that resolves when the app exits\n */\nexport async function run(options: RunOptions): Promise<void> {\n const { onExit, ...appProps } = options;\n\n // Clear terminal on start\n console.clear();\n\n const { waitUntilExit, clear } = render(<App {...appProps} />);\n\n await waitUntilExit();\n\n // Cleanup\n clear();\n onExit?.();\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["Page0"],"sources":["../src/lib/context.tsx","../src/lib/parser.ts","../src/lib/PageRenderer.tsx","../src/pages/sap/bc/adt/cts/transportrequests/[slug].loader.ts","../src/pages/sap/bc/adt/cts/transportrequests/[slug].tsx","../src/pages/_routes.ts","../src/pages/GenericPage.tsx","../src/Navigator.tsx","../src/App.tsx","../src/run.tsx"],"sourcesContent":["/**\n * Navigation Context\n *\n * React context for sharing navigation state across components.\n */\n\nimport React, {\n createContext,\n useContext,\n useState,\n useCallback,\n useMemo,\n type ReactNode,\n} from 'react';\nimport type { NavigationEntry, ParsedResponse, FetchFn } from './types';\n\ninterface NavigationContextValue {\n /** Current navigation entry */\n current: NavigationEntry | null;\n /** Navigation history */\n history: NavigationEntry[];\n /** Navigate to URL */\n navigate: (url: string) => Promise<void>;\n /** Go back */\n back: () => void;\n /** Is loading */\n loading: boolean;\n /** Error message */\n error: string | null;\n /** Fetch function */\n fetch: FetchFn;\n}\n\nconst NavigationContext = createContext<NavigationContextValue | null>(null);\n\ninterface NavigationProviderProps {\n children: ReactNode;\n /** Fetch function for making requests */\n fetch: FetchFn;\n /** Parse response function */\n parseResponse: (xml: string) => ParsedResponse;\n /** Initial URL to load */\n initialUrl?: string;\n}\n\nexport function NavigationProvider({\n children,\n fetch,\n parseResponse,\n initialUrl,\n}: Readonly<NavigationProviderProps>): React.ReactNode {\n const [current, setCurrent] = useState<NavigationEntry | null>(null);\n const [history, setHistory] = useState<NavigationEntry[]>([]);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const navigate = useCallback(\n async (url: string) => {\n setLoading(true);\n setError(null);\n\n try {\n const response = await fetch(url, {\n method: 'GET',\n headers: { Accept: '*/*' },\n });\n\n const parsed = parseResponse(response);\n const entry: NavigationEntry = {\n url,\n response: parsed,\n timestamp: new Date(),\n };\n\n // Push current to history if exists\n if (current) {\n setHistory((prev: NavigationEntry[]) => [...prev, current]);\n }\n\n setCurrent(entry);\n } catch (err) {\n setError(err instanceof Error ? err.message : String(err));\n } finally {\n setLoading(false);\n }\n },\n [current, fetch, parseResponse],\n );\n\n const back = useCallback(() => {\n if (history.length === 0) return;\n\n const prev = history[history.length - 1];\n setHistory((h: NavigationEntry[]) => h.slice(0, -1));\n setCurrent(prev);\n }, [history]);\n\n // Load initial URL on mount\n React.useEffect(() => {\n if (initialUrl && !current) {\n navigate(initialUrl);\n }\n }, [initialUrl, current, navigate]);\n\n const value = useMemo(\n () => ({\n current,\n history,\n navigate,\n back,\n loading,\n error,\n fetch,\n }),\n [current, history, navigate, back, loading, error, fetch],\n );\n\n return (\n <NavigationContext.Provider value={value}>\n {children}\n </NavigationContext.Provider>\n );\n}\n\n/**\n * Hook to access navigation context\n */\nexport function useNavigation(): NavigationContextValue {\n const context = useContext(NavigationContext);\n if (!context) {\n throw new Error('useNavigation must be used within NavigationProvider');\n }\n return context;\n}\n","/**\n * XML Response Parser\n *\n * Parses ADT XML responses and extracts hypermedia links.\n */\n\nimport { XMLParser } from 'fast-xml-parser';\nimport type { ParsedResponse, HypermediaLink } from './types';\n\nconst parser = new XMLParser({\n ignoreAttributes: false,\n attributeNamePrefix: '@_',\n removeNSPrefix: false,\n parseAttributeValue: true,\n trimValues: true,\n});\n\n/**\n * Push all `atom:link` entries from a single link element (or array of them)\n * onto `links`.\n */\nfunction pushLinkEntries(linkData: unknown, links: HypermediaLink[]): void {\n const linkArray = Array.isArray(linkData) ? linkData : [linkData];\n\n for (const link of linkArray) {\n if (link && typeof link === 'object') {\n const linkObj = link as Record<string, unknown>;\n const href = linkObj['@_href'] as string;\n const rel = linkObj['@_rel'] as string;\n\n if (href && rel) {\n links.push({\n href,\n rel,\n type: linkObj['@_type'] as string | undefined,\n title: linkObj['@_title'] as string | undefined,\n });\n }\n }\n }\n}\n\n/**\n * Extract all atom:link elements from parsed XML object\n */\nfunction extractLinks(\n obj: unknown,\n links: HypermediaLink[] = [],\n): HypermediaLink[] {\n if (!obj || typeof obj !== 'object') return links;\n\n if (Array.isArray(obj)) {\n for (const item of obj) {\n extractLinks(item, links);\n }\n } else {\n const record = obj as Record<string, unknown>;\n\n const linkKey = Object.keys(record).find(\n (k) => k === 'atom:link' || k === 'link' || k.endsWith(':link'),\n );\n\n if (linkKey) {\n pushLinkEntries(record[linkKey], links);\n }\n\n for (const key of Object.keys(record)) {\n if (key !== linkKey) {\n extractLinks(record[key], links);\n }\n }\n }\n\n return links;\n}\n\n/**\n * Detect root element and namespace from parsed XML\n */\nfunction detectRootInfo(data: Record<string, unknown>): {\n namespace?: string;\n rootElement?: string;\n} {\n const keys = Object.keys(data).filter((k) => k !== '?xml');\n if (keys.length === 0) return {};\n\n const rootKey = keys[0];\n const colonIndex = rootKey.indexOf(':');\n\n if (colonIndex > 0) {\n return {\n namespace: rootKey.substring(0, colonIndex),\n rootElement: rootKey.substring(colonIndex + 1),\n };\n }\n\n return { rootElement: rootKey };\n}\n\n/**\n * Parse ADT XML response\n */\nexport function parseResponse(xml: string): ParsedResponse {\n const data = parser.parse(xml) as Record<string, unknown>;\n const links = extractLinks(data);\n const { namespace, rootElement } = detectRootInfo(data);\n\n return {\n raw: xml,\n data,\n links,\n namespace,\n rootElement,\n };\n}\n\n/**\n * Get action name from relation URL\n */\nexport function getActionName(rel: string): string {\n const lastSlash = rel.lastIndexOf('/');\n return lastSlash >= 0 ? rel.substring(lastSlash + 1) : rel;\n}\n\n/**\n * Categorize links by type\n */\nexport function categorizeLinks(links: HypermediaLink[]): {\n navigation: HypermediaLink[];\n actions: HypermediaLink[];\n self: HypermediaLink[];\n} {\n const navigation: HypermediaLink[] = [];\n const actions: HypermediaLink[] = [];\n const self: HypermediaLink[] = [];\n\n for (const link of links) {\n if (link.rel === 'self') {\n self.push(link);\n } else if (\n link.rel.includes('/relations/') ||\n link.rel.includes('adturi')\n ) {\n actions.push(link);\n } else {\n navigation.push(link);\n }\n }\n\n return { navigation, actions, self };\n}\n","/**\n * PageRenderer - Framework component that renders PageResult\n *\n * Handles:\n * - Header with title/icon\n * - Page content\n * - Menu navigation with keyboard\n * - Footer\n */\n\nimport { useState } from 'react';\nimport { Box, Text, useInput } from 'ink';\nimport type { PageResult, MenuItem } from './types';\n\ninterface PageRendererProps {\n result: PageResult;\n onNavigate: (url: string) => void;\n onBack: () => void;\n onAction?: (key: string) => void;\n}\n\n/**\n * Render a single menu item\n */\nfunction MenuItemRow({\n item,\n isSelected,\n}: Readonly<{\n item: MenuItem;\n isSelected: boolean;\n}>) {\n const prefix = isSelected ? '❯ ' : ' ';\n\n return (\n <Text color={isSelected ? 'cyan' : undefined}>\n {prefix}\n {item.icon && `${item.icon} `}\n {item.label}\n {item.status && (\n <Text\n color={item.status.color}\n dimColor={!isSelected}\n >\n {' '}\n [{item.status.text}]\n </Text>\n )}\n {item.info && <Text dimColor> {item.info}</Text>}\n </Text>\n );\n}\n\n/**\n * PageRenderer component\n */\nexport function PageRenderer({\n result,\n onNavigate,\n onBack,\n onAction,\n}: Readonly<PageRendererProps>): React.ReactNode {\n const [selectedIndex, setSelectedIndex] = useState(0);\n\n // Handle keyboard input\n useInput((input, key) => {\n if (key.upArrow) {\n setSelectedIndex((i) => Math.max(0, i - 1));\n } else if (key.downArrow) {\n setSelectedIndex((i) => Math.min(result.menu.length - 1, i + 1));\n } else if (key.return) {\n const item = result.menu[selectedIndex];\n if (item.isBack) {\n onBack();\n } else if (item.href) {\n onNavigate(item.href);\n } else if (onAction) {\n onAction(item.key);\n }\n } else if (input === 'o' || input === 'O') {\n // Open XML in VS Code\n onAction?.('openXml');\n } else if (input === 'e' || input === 'E') {\n // Open in Eclipse ADT\n onAction?.('openAdt');\n }\n });\n\n return (\n <Box\n flexDirection=\"column\"\n padding={1}\n >\n {/* Header */}\n <Box\n borderStyle=\"round\"\n borderColor={result.color || 'cyan'}\n paddingX={2}\n marginBottom={1}\n >\n <Text\n bold\n color={result.color || 'cyan'}\n >\n {result.icon && `${result.icon} `}\n {result.title}\n </Text>\n {result.subtitle && <Text dimColor> ({result.subtitle})</Text>}\n </Box>\n\n {/* Content */}\n <Box\n flexDirection=\"column\"\n marginBottom={1}\n >\n {result.content}\n </Box>\n\n {/* Menu */}\n <Box flexDirection=\"column\">\n {result.menu.map((item, i) => (\n <MenuItemRow\n key={item.key}\n item={item}\n isSelected={i === selectedIndex}\n />\n ))}\n </Box>\n\n {/* Footer */}\n <Box marginTop={1}>\n <Text dimColor>\n {result.footer || '[↑↓] navigate | [Enter] select'}\n </Text>\n </Box>\n </Box>\n );\n}\n","/**\n * Transport Request/Task Loader\n *\n * Uses adt-contracts for type-safe XML parsing.\n */\n\nimport {\n transportmanagmentSingle,\n type TransportResponse,\n} from '@abapify/adt-contracts';\n\n// Re-export the schema for type inference\nexport { transportmanagmentSingle as schema };\n\n// Use type from contracts package\ntype TransportRoot = TransportResponse;\n\n// Extract nested types - TransportResponse has root.request structure\nexport type Request = NonNullable<\n NonNullable<TransportRoot['root']>['request']\n>;\nexport type Task = NonNullable<Request['task']>[number];\nexport type AbapObject = NonNullable<Task['abap_object']>[number];\n\n/**\n * Parsed transport data with computed properties\n */\nexport interface TransportData {\n /** Whether this is a task (vs request) */\n isTask: boolean;\n /** Transport/Task number */\n number: string;\n /** Parent request number (for tasks) */\n parent?: string;\n /** Owner user ID */\n owner: string;\n /** Description */\n desc: string;\n /** Transport type */\n type: string;\n /** Status code (D=Modifiable, R=Released, O=Protected) */\n status: string;\n /** Status text */\n statusText: string;\n /** Target system */\n target: string;\n /** Target description */\n targetDesc: string;\n /** Child tasks (for requests) */\n tasks: TaskInfo[];\n /** Objects (for tasks) */\n objects: ObjectInfo[];\n /** Raw parsed data */\n raw: TransportRoot;\n}\n\nexport interface TaskInfo {\n number: string;\n owner: string;\n desc: string;\n status: string;\n statusText: string;\n uri: string;\n}\n\nexport interface ObjectInfo {\n pgmid: string;\n type: string;\n name: string;\n info: string;\n lockStatus: string;\n uri?: string;\n}\n\n/**\n * Load and parse transport request/task XML\n */\nexport function load(xml: string): TransportData {\n const parsed = transportmanagmentSingle.parse(xml);\n return transformToTransportData(parsed);\n}\n\n/**\n * Normalize an XSD element that may appear once or many times.\n */\nfunction asArray<T>(value: T | T[]): T[] {\n return Array.isArray(value) ? value : [value];\n}\n\n/** Child tasks for a request view (always empty for task views). */\nfunction extractTasks(request: Request, isTask: boolean): TaskInfo[] {\n const tasks: TaskInfo[] = [];\n if (request.task && !isTask) {\n for (const task of asArray<Task>(request.task)) {\n tasks.push({\n number: task.number ?? '',\n owner: task.owner ?? '',\n desc: task.desc ?? '',\n status: task.status ?? '',\n statusText: task.status_text ?? '',\n uri: task.uri ?? '',\n });\n }\n }\n return tasks;\n}\n\n/** Objects carried by the task element (task views only). */\nfunction extractObjects(request: Request, isTask: boolean): ObjectInfo[] {\n const objects: ObjectInfo[] = [];\n if (!request.task || !isTask) return objects;\n\n // When viewing a task, the task element contains objects\n for (const task of asArray<Task>(request.task)) {\n if (task.abap_object) {\n for (const obj of asArray<AbapObject>(task.abap_object)) {\n objects.push({\n pgmid: obj.pgmid ?? '',\n type: obj.type ?? '',\n name: obj.name ?? '',\n info: obj.obj_info ?? obj.obj_desc ?? '',\n lockStatus: obj.lock_status ?? '',\n uri: obj.uri,\n });\n }\n }\n }\n return objects;\n}\n\n/**\n * Transform parsed XSD data to TransportData\n */\nfunction transformToTransportData(data: TransportRoot): TransportData {\n const request = data.root?.request;\n\n if (!request) {\n throw new Error('No request element found in transport data');\n }\n\n // Check if this is a task view (has parent) or request view\n const isTask = !!request.parent;\n\n return {\n isTask,\n number: request.number ?? '',\n parent: request.parent,\n owner: request.owner ?? '',\n desc: request.desc ?? '',\n type: request.type ?? '',\n status: request.status ?? '',\n statusText: request.status_text ?? '',\n target: request.target ?? '',\n targetDesc: request.target_desc ?? '',\n tasks: extractTasks(request, isTask),\n objects: extractObjects(request, isTask),\n raw: data,\n };\n}\n","/**\n * Transport Request/Task Page\n *\n * Returns PageResult for framework-driven rendering.\n * Uses schema-driven parsing via [slug].loader.ts\n */\n\nimport { Box, Text } from 'ink';\nimport type {\n PageProps,\n PageResult,\n MenuItem,\n} from '../../../../../../lib/types';\nimport { load, type TransportData } from './[slug].loader';\n\n/**\n * Get status color\n */\nfunction getStatusColor(status: string): string {\n switch (status) {\n case 'D':\n return 'yellow'; // Modifiable\n case 'R':\n return 'green'; // Released\n case 'O':\n return 'cyan'; // Protected\n default:\n return 'white';\n }\n}\n\n/**\n * Build the menu: back, parent link, tasks, objects, and link-driven actions.\n */\nfunction buildTransportMenu(\n transport: TransportData,\n response: PageProps['response'],\n): MenuItem[] {\n const menu: MenuItem[] = [\n { key: 'back', label: 'Back', icon: '←', isBack: true },\n ];\n\n // Add parent link for tasks\n if (transport.isTask && transport.parent) {\n menu.push({\n key: 'parent',\n label: 'Go to Parent Request',\n icon: '⬆️',\n href: `/sap/bc/adt/cts/transportrequests/${transport.parent}`,\n });\n }\n\n // Add tasks (for requests)\n for (const task of transport.tasks) {\n menu.push({\n key: task.number,\n label: `${task.number} - ${task.desc || '(no desc)'}`,\n icon: '📝',\n href: task.uri,\n info: `@${task.owner}`,\n status: { text: task.statusText, color: getStatusColor(task.status) },\n });\n }\n\n // Add objects (for tasks)\n for (const obj of transport.objects) {\n const lockIcon = obj.lockStatus === 'X' ? '🔒' : '📦';\n menu.push({\n key: `${obj.pgmid}-${obj.name}`,\n label: `${obj.type} ${obj.name}`,\n icon: lockIcon,\n href: obj.uri,\n info: obj.uri ? undefined : '(no link)',\n });\n }\n\n // Add actions from links\n const actions = [\n { rel: 'consistencycheck', label: 'Consistency Check', icon: '🔍' },\n { rel: 'newreleasejobs', label: 'Release', icon: '🚀' },\n { rel: 'transportchecks', label: 'Transport Checks', icon: '✅' },\n ];\n\n for (const action of actions) {\n const link = response.links.find((l) => l.rel.includes(action.rel));\n if (link) {\n menu.push({\n key: action.rel,\n label: action.label,\n icon: action.icon,\n href: link.href,\n });\n }\n }\n\n return menu;\n}\n\n/**\n * Transport Page - returns PageResult for framework rendering\n */\nexport function transportPage({\n url: _url,\n response,\n}: PageProps): PageResult | null {\n // Parse transport data\n let transport;\n try {\n transport = load(response.raw);\n } catch (_e) {\n return null;\n }\n\n const menu = buildTransportMenu(transport, response);\n\n // Build content\n const content = (\n <Box flexDirection=\"column\">\n <Text>\n <Text bold>Description:</Text> {transport.desc || '(no description)'}\n </Text>\n <Text>\n <Text bold>Owner:</Text> {transport.owner}\n </Text>\n <Text>\n <Text bold>Type:</Text> {transport.type}\n </Text>\n <Text>\n <Text bold>Status:</Text>{' '}\n <Text color={getStatusColor(transport.status)}>\n {transport.statusText} ({transport.status})\n </Text>\n </Text>\n {transport.target && (\n <Text>\n <Text bold>Target:</Text> {transport.target}{' '}\n {transport.targetDesc && `(${transport.targetDesc})`}\n </Text>\n )}\n {transport.tasks.length > 0 && (\n <Box marginTop={1}>\n <Text\n bold\n color=\"yellow\"\n >\n 📋 Tasks ({transport.tasks.length}):\n </Text>\n </Box>\n )}\n {transport.objects.length > 0 && (\n <Box marginTop={1}>\n <Text\n bold\n color=\"cyan\"\n >\n 📦 Objects ({transport.objects.length}):\n </Text>\n </Box>\n )}\n </Box>\n );\n\n const isTask = transport.isTask;\n return {\n title: `${isTask ? 'Task' : 'Transport Request'}: ${transport.number}`,\n icon: isTask ? '📋' : '📦',\n color: isTask ? 'yellow' : 'cyan',\n subtitle:\n isTask && transport.parent ? `Parent: ${transport.parent}` : undefined,\n content,\n menu,\n footer: '[o] XML in VS Code | [e] open in Eclipse ADT',\n };\n}\n\n// Default export for file-based routing\nexport default transportPage;\n","/**\n * Auto-generated route manifest\n * DO NOT EDIT - Generated by scripts/generate-routes.ts\n */\n\nimport Page0 from './sap/bc/adt/cts/transportrequests/[slug]';\nimport type { PageComponent } from '../lib/types';\n\nexport interface Route {\n pattern: string;\n regex: RegExp;\n page: PageComponent;\n}\n\nexport const routes: Route[] = [\n {\n pattern: '/sap/bc/adt/cts/transportrequests/:slug',\n regex: /^\\/sap\\/bc\\/adt\\/cts\\/transportrequests\\/([^/]+)$/,\n page: Page0,\n },\n];\n\n/**\n * Match URL to route\n */\nexport function matchRoute(url: string): Route | null {\n for (const route of routes) {\n if (route.regex.test(url)) {\n return route;\n }\n }\n return null;\n}\n","/**\n * Generic Page Component\n *\n * Fallback page for unknown endpoints - shows payload + links.\n * Returns PageResult for framework-driven rendering.\n */\n\n// React import not needed with JSX transform\nimport { Box, Text } from 'ink';\nimport type { PageProps, PageResult, MenuItem } from '../lib/types';\nimport { getActionName } from '../lib/parser';\n\ninterface SummaryItem {\n key: string;\n value: string;\n}\n\n/**\n * Generic page - returns PageResult for framework rendering\n */\nexport function genericPage({ url, response }: PageProps): PageResult {\n // Extract summary from data\n const summary = extractSummary(response.data);\n\n // Build menu items from links\n const menu: MenuItem[] = [\n { key: 'back', label: 'Back', icon: '←', isBack: true },\n ];\n\n for (const link of response.links) {\n const title = link.title || getActionName(link.rel);\n\n // Make path relative/shorter for display\n let displayPath = link.href;\n const currentBase = url.split('/').slice(0, -1).join('/');\n if (link.href.startsWith(currentBase)) {\n displayPath = '.' + link.href.slice(currentBase.length);\n } else if (link.href.startsWith('/sap/bc/adt/')) {\n displayPath = link.href.replace('/sap/bc/adt/', '/');\n }\n if (displayPath.length > 40) {\n displayPath = '...' + displayPath.slice(-37);\n }\n\n menu.push({\n key: link.href,\n label: title,\n icon: '🔗',\n href: link.href,\n info: displayPath,\n });\n }\n\n // Build content\n const content = (\n <Box flexDirection=\"column\">\n <Text dimColor>URL: {url}</Text>\n <Box\n marginTop={1}\n flexDirection=\"column\"\n >\n {summary.length > 0 ? (\n summary.map((item) => (\n <Text key={item.key}>\n <Text color=\"yellow\">{item.key}:</Text> {item.value}\n </Text>\n ))\n ) : (\n <Text dimColor>No summary available</Text>\n )}\n </Box>\n </Box>\n );\n\n return {\n title: `${response.namespace || 'unknown'}:${response.rootElement || 'response'}`,\n icon: '📄',\n color: 'cyan',\n content,\n menu,\n footer: `[o] XML | [e] Eclipse ADT | ${response.links.length} links`,\n };\n}\n\n/**\n * Collect one interesting `@_` attribute into the summary (dedup + allow-list).\n */\nfunction collectSummaryAttr(\n summary: SummaryItem[],\n seen: Set<string>,\n key: string,\n value: unknown,\n): void {\n const isStringAttr = key.startsWith('@_') && typeof value === 'string';\n if (!isStringAttr && typeof value !== 'number') return;\n\n const cleanKey = key.replace('@_', '').replace(/^\\w+:/, '');\n // Skip duplicates and uninteresting fields\n if (seen.has(cleanKey) || cleanKey === 'version' || cleanKey === 'encoding')\n return;\n seen.add(cleanKey);\n\n // Limit to important fields\n const important = [\n 'number',\n 'owner',\n 'desc',\n 'status',\n 'status_text',\n 'type',\n 'target',\n 'name',\n 'uri',\n ];\n if (important.some((f) => cleanKey.includes(f))) {\n summary.push({ key: cleanKey, value: String(value) });\n }\n}\n\n/**\n * Extract key fields for summary display\n */\nfunction extractSummary(data: Record<string, unknown>): SummaryItem[] {\n const summary: SummaryItem[] = [];\n const seen = new Set<string>();\n\n function extract(obj: unknown, prefix = ''): void {\n if (!obj || typeof obj !== 'object') return;\n if (Array.isArray(obj)) {\n obj.forEach((item) => extract(item, prefix));\n return;\n }\n\n const record = obj as Record<string, unknown>;\n for (const [key, value] of Object.entries(record)) {\n // Skip links and xml declaration\n if (key.includes('link') || key === '?xml') continue;\n\n // Extract interesting attributes (those starting with @_)\n collectSummaryAttr(summary, seen, key, value);\n\n // Recurse into nested objects\n if (typeof value === 'object') {\n extract(value, key);\n }\n }\n }\n\n extract(data);\n return summary.slice(0, 8); // Limit to 8 items\n}\n\n// removeLinks function removed - was unused\n","/**\n * Navigator Component\n *\n * Main entry point - renders pages using the framework.\n * Pages return PageResult, Navigator handles rendering.\n */\n\nimport { useState, useMemo } from 'react';\nimport { Box, Text } from 'ink';\nimport Spinner from 'ink-spinner';\nimport TextInput from 'ink-text-input';\nimport { writeFileSync, existsSync } from 'node:fs';\nimport { tmpdir } from 'node:os';\nimport { join } from 'node:path';\nimport { exec, execFile } from 'node:child_process';\nimport { NavigationProvider, useNavigation } from './lib/context';\nimport { parseResponse } from './lib/parser';\nimport { PageRenderer } from './lib/PageRenderer';\nimport { matchRoute } from './pages/_routes';\nimport { genericPage } from './pages/GenericPage';\nimport type { FetchFn, PageComponent, PageResult } from './lib/types';\n\ninterface NavigatorProps {\n /** Optional starting URL - if not provided, shows prompt */\n startUrl?: string;\n /** Fetch function */\n fetch: FetchFn;\n /** SAP system name for ADT links (e.g., 'TRL', 'NPL') */\n systemName?: string;\n}\n\n/**\n * Open raw XML in IDE (VS Code)\n */\nfunction openXmlInIde(xml: string, url: string): void {\n const safeName = url.replaceAll(/[^a-zA-Z0-9]/g, '_').slice(-50);\n const filename = `adt_${safeName}.xml`;\n const filepath = join(tmpdir(), filename);\n writeFileSync(filepath, xml, 'utf-8');\n exec(`code \"${filepath}\"`, (error) => {\n if (error) {\n exec(`xdg-open \"${filepath}\"`);\n }\n });\n}\n\n/**\n * Resolve absolute path to a launcher binary from an allow-list of known\n * install locations. Prevents PATH-hijack (S4036) by avoiding bare-name\n * lookups when an absolute path is available. Falls back to the bare name\n * only if none of the known paths exist, so packaged distributions with\n * non-standard layouts still function.\n */\nfunction resolveLauncher(\n candidates: readonly string[],\n fallbackName: string,\n): string {\n for (const p of candidates) {\n if (existsSync(p)) return p;\n }\n return fallbackName;\n}\n\nconst XDG_OPEN = resolveLauncher(\n ['/usr/bin/xdg-open', '/usr/local/bin/xdg-open'],\n 'xdg-open',\n);\nconst OPEN_MAC = resolveLauncher(\n ['/usr/bin/open', '/usr/local/bin/open'],\n 'open',\n);\n\n/**\n * Open in Eclipse ADT\n * Format: adt://[system]/[path]\n * @see https://help.sap.com/docs/abap-cloud/abap-development-tools-user-guide/opening-adt-links\n */\nfunction openInAdt(systemName: string, url: string): void {\n const adtUrl = `adt://${systemName}${url}`;\n // Use execFile (arg array, no shell) to avoid shell-command injection\n // via systemName/url. Try xdg-open (Linux), fall back to open (macOS).\n // Paths resolved above via allow-list to prevent PATH-hijack (S4036).\n execFile(XDG_OPEN, [adtUrl], (error) => {\n if (error) {\n execFile(OPEN_MAC, [adtUrl], (err2) => {\n if (err2) {\n console.error('Failed to open ADT link:', err2.message);\n }\n });\n }\n });\n}\n\n/**\n * Inner component that uses navigation context\n */\nfunction NavigatorInner({ systemName }: Readonly<{ systemName?: string }>) {\n const { current, loading, error, navigate, back } = useNavigation();\n\n // Get page result\n const pageResult = useMemo<PageResult | null>(() => {\n if (!current) return null;\n\n // Try file-based route matching first\n const fileRoute = matchRoute(current.url);\n const pageFn: PageComponent = fileRoute?.page ?? genericPage;\n\n return pageFn({ url: current.url, response: current.response });\n }, [current]);\n\n // Handle actions\n const handleAction = (key: string) => {\n if (key === 'openXml' && current) {\n openXmlInIde(current.response.raw, current.url);\n } else if (key === 'openAdt' && current && systemName) {\n openInAdt(systemName, current.url);\n }\n };\n\n if (loading) {\n return (\n <Box padding={1}>\n <Text color=\"cyan\">\n <Spinner type=\"dots\" />\n </Text>\n <Text> Loading...</Text>\n </Box>\n );\n }\n\n if (error) {\n return (\n <Box\n padding={1}\n flexDirection=\"column\"\n >\n <Text color=\"red\">❌ Error: {error}</Text>\n <Text dimColor>Press Ctrl+C to exit</Text>\n </Box>\n );\n }\n\n if (!current) {\n return <UrlPrompt onSubmit={navigate} />;\n }\n\n if (!pageResult) {\n return (\n <Box padding={1}>\n <Text dimColor>No content loaded</Text>\n </Box>\n );\n }\n\n return (\n <PageRenderer\n result={pageResult}\n onNavigate={navigate}\n onBack={back}\n onAction={handleAction}\n />\n );\n}\n\n/**\n * Main Navigator component\n */\nexport function Navigator({\n startUrl,\n fetch,\n systemName,\n}: Readonly<NavigatorProps>): React.ReactNode {\n return (\n <NavigationProvider\n fetch={fetch}\n parseResponse={parseResponse}\n initialUrl={startUrl}\n >\n <NavigatorInner systemName={systemName} />\n </NavigationProvider>\n );\n}\n\n/**\n * URL input prompt component\n */\nfunction UrlPrompt({\n onSubmit,\n}: Readonly<{ onSubmit: (url: string) => void }>) {\n const [url, setUrl] = useState('');\n\n const handleSubmit = () => {\n if (url.trim()) {\n onSubmit(url.trim());\n }\n };\n\n return (\n <Box\n flexDirection=\"column\"\n padding={1}\n >\n <Text\n color=\"cyan\"\n bold\n >\n ADT TUI - Hypermedia Navigator\n </Text>\n <Box marginTop={1}>\n <Text>Enter URL: </Text>\n <TextInput\n value={url}\n onChange={setUrl}\n onSubmit={handleSubmit}\n />\n </Box>\n <Box marginTop={1}>\n <Text dimColor>\n Example: /sap/bc/adt/cts/transportrequests/TRLK900001\n </Text>\n </Box>\n </Box>\n );\n}\n","/**\n * Main App Component\n *\n * Entry point for the TUI application.\n */\n\nimport { useState, useEffect, useDebugValue } from 'react';\nimport { Navigator } from './Navigator';\nimport type { FetchFn } from './lib/types';\nimport type { Router } from './lib/router';\n\nexport interface AppProps {\n /** Optional starting URL - if not provided, shows prompt */\n startUrl?: string;\n /** Fetch function for making HTTP requests */\n fetch: FetchFn;\n /** Optional custom router */\n router?: Router;\n /** SAP system name for ADT links (e.g., 'TRL', 'NPL') */\n systemName?: string;\n}\n\n/**\n * Main TUI Application\n */\nexport function App({\n startUrl,\n fetch,\n router: _router,\n systemName,\n}: Readonly<AppProps>): React.ReactNode {\n // The counter value only exists to force a re-render on resize; reading it\n // via useDebugValue keeps the state consumed without touching the render\n // tree (useDebugValue is a no-op outside React DevTools).\n const [resizeKey, setResizeKey] = useState(0);\n useDebugValue(resizeKey);\n\n // Handle terminal resize - just trigger re-render, don't clear\n useEffect(() => {\n const handleResize = () => {\n setResizeKey((k) => k + 1);\n };\n\n process.stdout.on('resize', handleResize);\n return () => {\n process.stdout.off('resize', handleResize);\n };\n }, []);\n\n return (\n <Navigator\n startUrl={startUrl}\n fetch={fetch}\n systemName={systemName}\n />\n );\n}\n","/**\n * Run TUI Application\n *\n * Helper to render the TUI and wait for exit.\n */\n\n// React import not needed with JSX transform\nimport { render } from 'ink';\nimport { App } from './App';\nimport type { FetchFn } from './lib/types';\nimport type { Router } from './lib/router';\n\nexport interface RunOptions {\n /** Optional starting URL - if not provided, shows prompt */\n startUrl?: string;\n /** Fetch function for making HTTP requests */\n fetch: FetchFn;\n /** Optional custom router */\n router?: Router;\n /** SAP system name for ADT links (e.g., 'TRL', 'NPL') */\n systemName?: string;\n /** Called when app exits */\n onExit?: () => void;\n}\n\n/**\n * Run the TUI application\n *\n * @returns Promise that resolves when the app exits\n */\nexport async function run(options: RunOptions): Promise<void> {\n const { onExit, ...appProps } = options;\n\n // Clear terminal on start\n console.clear();\n\n const { waitUntilExit, clear } = render(<App {...appProps} />);\n\n await waitUntilExit();\n\n // Cleanup\n clear();\n onExit?.();\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@abapify/adt-tui",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.36",
|
|
4
4
|
"description": "Terminal UI components for ADT - page-based navigation with Ink",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"publishConfig": {
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"README.md"
|
|
18
18
|
],
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@abapify/adt-contracts": "0.4.
|
|
20
|
+
"@abapify/adt-contracts": "0.4.36",
|
|
21
21
|
"fast-xml-parser": "^5.3.1",
|
|
22
22
|
"ink": "5.1.0",
|
|
23
23
|
"ink-select-input": "^6.0.0",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"@types/react": "18.3.0"
|
|
30
30
|
},
|
|
31
31
|
"peerDependencies": {
|
|
32
|
-
"@abapify/adt-client": "0.4.
|
|
32
|
+
"@abapify/adt-client": "0.4.36"
|
|
33
33
|
},
|
|
34
34
|
"repository": {
|
|
35
35
|
"type": "git",
|