@abapify/adt-tui 0.4.0 → 0.4.3

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.
@@ -0,0 +1,257 @@
1
+ import React$1, { ReactNode } from "react";
2
+ //#region src/lib/types.d.ts
3
+ /**
4
+ * Core types for ADT TUI
5
+ */
6
+ /**
7
+ * Hypermedia link extracted from ADT responses
8
+ */
9
+ interface HypermediaLink {
10
+ href: string;
11
+ rel: string;
12
+ type?: string;
13
+ title?: string;
14
+ }
15
+ /**
16
+ * Parsed ADT response
17
+ */
18
+ interface ParsedResponse {
19
+ raw: string;
20
+ data: Record<string, unknown>;
21
+ links: HypermediaLink[];
22
+ namespace?: string;
23
+ rootElement?: string;
24
+ }
25
+ /**
26
+ * Navigation entry in history
27
+ */
28
+ interface NavigationEntry {
29
+ url: string;
30
+ response: ParsedResponse;
31
+ timestamp: Date;
32
+ }
33
+ /**
34
+ * Fetch function type - abstracted from client
35
+ */
36
+ type FetchFn = (url: string, options?: {
37
+ method?: string;
38
+ headers?: Record<string, string>;
39
+ body?: string;
40
+ }) => Promise<string>;
41
+ /**
42
+ * Menu item for navigation
43
+ */
44
+ interface MenuItem {
45
+ /** Unique key for React */
46
+ key: string;
47
+ /** Display label */
48
+ label: string;
49
+ /** Icon (emoji) */
50
+ icon?: string;
51
+ /** URL to navigate to (if navigable) */
52
+ href?: string;
53
+ /** Whether this is a back action */
54
+ isBack?: boolean;
55
+ /** Additional info shown dimmed */
56
+ info?: string;
57
+ /** Status indicator */
58
+ status?: {
59
+ text: string;
60
+ color: string;
61
+ };
62
+ }
63
+ /**
64
+ * Page result returned by page components
65
+ */
66
+ interface PageResult {
67
+ /** Page title */
68
+ title: string;
69
+ /** Title icon */
70
+ icon?: string;
71
+ /** Title color */
72
+ color?: string;
73
+ /** Subtitle (shown dimmed) */
74
+ subtitle?: string;
75
+ /** Content to render (React element) */
76
+ content: React.ReactNode;
77
+ /** Menu items for navigation */
78
+ menu: MenuItem[];
79
+ /** Footer text */
80
+ footer?: string;
81
+ }
82
+ /**
83
+ * Props passed to every page component
84
+ */
85
+ interface PageProps {
86
+ /** Current URL */
87
+ url: string;
88
+ /** Parsed response data */
89
+ response: ParsedResponse;
90
+ }
91
+ /**
92
+ * Page component type - returns PageResult, not JSX
93
+ */
94
+ type PageComponent = (props: PageProps) => PageResult | null;
95
+ /**
96
+ * Route definition
97
+ */
98
+ interface Route {
99
+ /** URL pattern (string for exact, RegExp for pattern) */
100
+ pattern: RegExp | string;
101
+ /** Page function that returns PageResult */
102
+ page: PageComponent;
103
+ /** Optional name for debugging */
104
+ name?: string;
105
+ }
106
+ //#endregion
107
+ //#region src/lib/router.d.ts
108
+ /**
109
+ * Router class for URL pattern matching
110
+ */
111
+ declare class Router {
112
+ private readonly routes;
113
+ private fallback;
114
+ /**
115
+ * Register a route
116
+ */
117
+ register(route: Route): this;
118
+ /**
119
+ * Set fallback page for unmatched routes
120
+ */
121
+ setFallback(page: PageComponent): this;
122
+ /**
123
+ * Match URL to a route
124
+ */
125
+ match(url: string): {
126
+ page: PageComponent;
127
+ name?: string;
128
+ } | null;
129
+ /**
130
+ * List all registered routes
131
+ */
132
+ listRoutes(): Array<{
133
+ pattern: string;
134
+ name?: string;
135
+ }>;
136
+ }
137
+ //#endregion
138
+ //#region src/run.d.ts
139
+ interface RunOptions {
140
+ /** Optional starting URL - if not provided, shows prompt */
141
+ startUrl?: string;
142
+ /** Fetch function for making HTTP requests */
143
+ fetch: FetchFn;
144
+ /** Optional custom router */
145
+ router?: Router;
146
+ /** SAP system name for ADT links (e.g., 'TRL', 'NPL') */
147
+ systemName?: string;
148
+ /** Called when app exits */
149
+ onExit?: () => void;
150
+ }
151
+ /**
152
+ * Run the TUI application
153
+ *
154
+ * @returns Promise that resolves when the app exits
155
+ */
156
+ declare function run(options: RunOptions): Promise<void>;
157
+ //#endregion
158
+ //#region src/App.d.ts
159
+ interface AppProps {
160
+ /** Optional starting URL - if not provided, shows prompt */
161
+ startUrl?: string;
162
+ /** Fetch function for making HTTP requests */
163
+ fetch: FetchFn;
164
+ /** Optional custom router */
165
+ router?: Router;
166
+ /** SAP system name for ADT links (e.g., 'TRL', 'NPL') */
167
+ systemName?: string;
168
+ }
169
+ /**
170
+ * Main TUI Application
171
+ */
172
+ declare function App({ startUrl, fetch, router: _router, systemName }: AppProps): React.ReactNode;
173
+ //#endregion
174
+ //#region src/Navigator.d.ts
175
+ interface NavigatorProps {
176
+ /** Optional starting URL - if not provided, shows prompt */
177
+ startUrl?: string;
178
+ /** Fetch function */
179
+ fetch: FetchFn;
180
+ /** SAP system name for ADT links (e.g., 'TRL', 'NPL') */
181
+ systemName?: string;
182
+ }
183
+ /**
184
+ * Main Navigator component
185
+ */
186
+ declare function Navigator({ startUrl, fetch, systemName }: NavigatorProps): React.ReactNode;
187
+ //#endregion
188
+ //#region src/lib/PageRenderer.d.ts
189
+ interface PageRendererProps {
190
+ result: PageResult;
191
+ onNavigate: (url: string) => void;
192
+ onBack: () => void;
193
+ onAction?: (key: string) => void;
194
+ }
195
+ /**
196
+ * PageRenderer component
197
+ */
198
+ declare function PageRenderer({ result, onNavigate, onBack, onAction }: PageRendererProps): React.ReactNode;
199
+ //#endregion
200
+ //#region src/lib/context.d.ts
201
+ interface NavigationContextValue {
202
+ /** Current navigation entry */
203
+ current: NavigationEntry | null;
204
+ /** Navigation history */
205
+ history: NavigationEntry[];
206
+ /** Navigate to URL */
207
+ navigate: (url: string) => Promise<void>;
208
+ /** Go back */
209
+ back: () => void;
210
+ /** Is loading */
211
+ loading: boolean;
212
+ /** Error message */
213
+ error: string | null;
214
+ /** Fetch function */
215
+ fetch: FetchFn;
216
+ }
217
+ interface NavigationProviderProps {
218
+ children: ReactNode;
219
+ /** Fetch function for making requests */
220
+ fetch: FetchFn;
221
+ /** Parse response function */
222
+ parseResponse: (xml: string) => ParsedResponse;
223
+ /** Initial URL to load */
224
+ initialUrl?: string;
225
+ }
226
+ declare function NavigationProvider({ children, fetch, parseResponse, initialUrl }: NavigationProviderProps): React$1.ReactNode;
227
+ /**
228
+ * Hook to access navigation context
229
+ */
230
+ declare function useNavigation(): NavigationContextValue;
231
+ //#endregion
232
+ //#region src/lib/parser.d.ts
233
+ /**
234
+ * Parse ADT XML response
235
+ */
236
+ declare function parseResponse$1(xml: string): ParsedResponse;
237
+ /**
238
+ * Get action name from relation URL
239
+ */
240
+ declare function getActionName(rel: string): string;
241
+ /**
242
+ * Categorize links by type
243
+ */
244
+ declare function categorizeLinks(links: HypermediaLink[]): {
245
+ navigation: HypermediaLink[];
246
+ actions: HypermediaLink[];
247
+ self: HypermediaLink[];
248
+ };
249
+ //#endregion
250
+ //#region src/pages/GenericPage.d.ts
251
+ /**
252
+ * Generic page - returns PageResult for framework rendering
253
+ */
254
+ declare function genericPage({ url, response }: PageProps): PageResult;
255
+ //#endregion
256
+ export { App, type AppProps, type FetchFn, type HypermediaLink, type MenuItem, type NavigationEntry, NavigationProvider, Navigator, type PageComponent, type PageProps, PageRenderer, type PageResult, type ParsedResponse, type Route, type RunOptions, categorizeLinks, genericPage, getActionName, parseResponse$1 as parseResponse, run, useNavigation };
257
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"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,WAAW,MAAM;;;UCRV;;EAER;;EAEA,OAAO;;EAEP;;;;;iBAwIc,YACd,UACA,OACA,cACC,iBAAiB,MAAM;;;UC1JhB;EACR,QAAQ;EACR,aAAa;EACb;EACA,YAAY;;;;;iBAkCE,eACd,QACA,YACA,QACA,YACC,oBAAoB,MAAM;;;UC1CnB;;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,0BAA0B,QAAM;;;;iBA0EnB,iBAAiB;;;;;;iBC5BjB,gBAAc,cAAc;;;;iBAiB5B,cAAc;;;;iBAQd,gBAAgB,OAAO;EACrC,YAAY;EACZ,SAAS;EACT,MAAM;;;;;;;iBCvGQ,cAAc,KAAK,YAAY,YAAY"}
package/dist/index.mjs ADDED
@@ -0,0 +1,836 @@
1
+ import { Box, Text, render, useInput } from "ink";
2
+ import React, { createContext, useCallback, useContext, useEffect, useMemo, useState } from "react";
3
+ import Spinner from "ink-spinner";
4
+ import TextInput from "ink-text-input";
5
+ import { existsSync, writeFileSync } from "node:fs";
6
+ import { tmpdir } from "node:os";
7
+ import { join } from "node:path";
8
+ import { exec, execFile } from "node:child_process";
9
+ import { jsx, jsxs } from "react/jsx-runtime";
10
+ import { XMLParser } from "fast-xml-parser";
11
+ import { transportmanagmentSingle } from "@abapify/adt-contracts";
12
+
13
+ //#region src/lib/context.tsx
14
+ /**
15
+ * Navigation Context
16
+ *
17
+ * React context for sharing navigation state across components.
18
+ */
19
+ const NavigationContext = createContext(null);
20
+ function NavigationProvider({ children, fetch, parseResponse, initialUrl }) {
21
+ const [current, setCurrent] = useState(null);
22
+ const [history, setHistory] = useState([]);
23
+ const [loading, setLoading] = useState(false);
24
+ const [error, setError] = useState(null);
25
+ const navigate = useCallback(async (url) => {
26
+ setLoading(true);
27
+ setError(null);
28
+ try {
29
+ const entry = {
30
+ url,
31
+ response: parseResponse(await fetch(url, {
32
+ method: "GET",
33
+ headers: { Accept: "*/*" }
34
+ })),
35
+ timestamp: /* @__PURE__ */ new Date()
36
+ };
37
+ if (current) setHistory((prev) => [...prev, current]);
38
+ setCurrent(entry);
39
+ } catch (err) {
40
+ setError(err instanceof Error ? err.message : String(err));
41
+ } finally {
42
+ setLoading(false);
43
+ }
44
+ }, [
45
+ current,
46
+ fetch,
47
+ parseResponse
48
+ ]);
49
+ const back = useCallback(() => {
50
+ if (history.length === 0) return;
51
+ const prev = history[history.length - 1];
52
+ setHistory((h) => h.slice(0, -1));
53
+ setCurrent(prev);
54
+ }, [history]);
55
+ React.useEffect(() => {
56
+ if (initialUrl && !current) navigate(initialUrl);
57
+ }, [
58
+ initialUrl,
59
+ current,
60
+ navigate
61
+ ]);
62
+ return /* @__PURE__ */ jsx(NavigationContext.Provider, {
63
+ value: {
64
+ current,
65
+ history,
66
+ navigate,
67
+ back,
68
+ loading,
69
+ error,
70
+ fetch
71
+ },
72
+ children
73
+ });
74
+ }
75
+ /**
76
+ * Hook to access navigation context
77
+ */
78
+ function useNavigation() {
79
+ const context = useContext(NavigationContext);
80
+ if (!context) throw new Error("useNavigation must be used within NavigationProvider");
81
+ return context;
82
+ }
83
+
84
+ //#endregion
85
+ //#region src/lib/parser.ts
86
+ /**
87
+ * XML Response Parser
88
+ *
89
+ * Parses ADT XML responses and extracts hypermedia links.
90
+ */
91
+ const parser = new XMLParser({
92
+ ignoreAttributes: false,
93
+ attributeNamePrefix: "@_",
94
+ removeNSPrefix: false,
95
+ parseAttributeValue: true,
96
+ trimValues: true
97
+ });
98
+ /**
99
+ * Extract all atom:link elements from parsed XML object
100
+ */
101
+ function extractLinks(obj, links = []) {
102
+ if (!obj || typeof obj !== "object") return links;
103
+ if (Array.isArray(obj)) for (const item of obj) extractLinks(item, links);
104
+ else {
105
+ const record = obj;
106
+ const linkKey = Object.keys(record).find((k) => k === "atom:link" || k === "link" || k.endsWith(":link"));
107
+ if (linkKey) {
108
+ const linkData = record[linkKey];
109
+ const linkArray = Array.isArray(linkData) ? linkData : [linkData];
110
+ for (const link of linkArray) if (link && typeof link === "object") {
111
+ const linkObj = link;
112
+ const href = linkObj["@_href"];
113
+ const rel = linkObj["@_rel"];
114
+ if (href && rel) links.push({
115
+ href,
116
+ rel,
117
+ type: linkObj["@_type"],
118
+ title: linkObj["@_title"]
119
+ });
120
+ }
121
+ }
122
+ for (const key of Object.keys(record)) if (key !== linkKey) extractLinks(record[key], links);
123
+ }
124
+ return links;
125
+ }
126
+ /**
127
+ * Detect root element and namespace from parsed XML
128
+ */
129
+ function detectRootInfo(data) {
130
+ const keys = Object.keys(data).filter((k) => k !== "?xml");
131
+ if (keys.length === 0) return {};
132
+ const rootKey = keys[0];
133
+ const colonIndex = rootKey.indexOf(":");
134
+ if (colonIndex > 0) return {
135
+ namespace: rootKey.substring(0, colonIndex),
136
+ rootElement: rootKey.substring(colonIndex + 1)
137
+ };
138
+ return { rootElement: rootKey };
139
+ }
140
+ /**
141
+ * Parse ADT XML response
142
+ */
143
+ function parseResponse(xml) {
144
+ const data = parser.parse(xml);
145
+ const links = extractLinks(data);
146
+ const { namespace, rootElement } = detectRootInfo(data);
147
+ return {
148
+ raw: xml,
149
+ data,
150
+ links,
151
+ namespace,
152
+ rootElement
153
+ };
154
+ }
155
+ /**
156
+ * Get action name from relation URL
157
+ */
158
+ function getActionName(rel) {
159
+ const lastSlash = rel.lastIndexOf("/");
160
+ return lastSlash >= 0 ? rel.substring(lastSlash + 1) : rel;
161
+ }
162
+ /**
163
+ * Categorize links by type
164
+ */
165
+ function categorizeLinks(links) {
166
+ const navigation = [];
167
+ const actions = [];
168
+ const self = [];
169
+ for (const link of links) if (link.rel === "self") self.push(link);
170
+ else if (link.rel.includes("/relations/") || link.rel.includes("adturi")) actions.push(link);
171
+ else navigation.push(link);
172
+ return {
173
+ navigation,
174
+ actions,
175
+ self
176
+ };
177
+ }
178
+
179
+ //#endregion
180
+ //#region src/lib/PageRenderer.tsx
181
+ /**
182
+ * PageRenderer - Framework component that renders PageResult
183
+ *
184
+ * Handles:
185
+ * - Header with title/icon
186
+ * - Page content
187
+ * - Menu navigation with keyboard
188
+ * - Footer
189
+ */
190
+ /**
191
+ * Render a single menu item
192
+ */
193
+ function MenuItemRow({ item, isSelected }) {
194
+ return /* @__PURE__ */ jsxs(Text, {
195
+ color: isSelected ? "cyan" : void 0,
196
+ children: [
197
+ isSelected ? "❯ " : " ",
198
+ item.icon && `${item.icon} `,
199
+ item.label,
200
+ item.status && /* @__PURE__ */ jsxs(Text, {
201
+ color: item.status.color,
202
+ dimColor: !isSelected,
203
+ children: [
204
+ " ",
205
+ "[",
206
+ item.status.text,
207
+ "]"
208
+ ]
209
+ }),
210
+ item.info && /* @__PURE__ */ jsxs(Text, {
211
+ dimColor: true,
212
+ children: [" ", item.info]
213
+ })
214
+ ]
215
+ });
216
+ }
217
+ /**
218
+ * PageRenderer component
219
+ */
220
+ function PageRenderer({ result, onNavigate, onBack, onAction }) {
221
+ const [selectedIndex, setSelectedIndex] = useState(0);
222
+ useInput((input, key) => {
223
+ if (key.upArrow) setSelectedIndex((i) => Math.max(0, i - 1));
224
+ else if (key.downArrow) setSelectedIndex((i) => Math.min(result.menu.length - 1, i + 1));
225
+ else if (key.return) {
226
+ const item = result.menu[selectedIndex];
227
+ if (item.isBack) onBack();
228
+ else if (item.href) onNavigate(item.href);
229
+ else if (onAction) onAction(item.key);
230
+ } else if (input === "o" || input === "O") onAction?.("openXml");
231
+ else if (input === "e" || input === "E") onAction?.("openAdt");
232
+ });
233
+ return /* @__PURE__ */ jsxs(Box, {
234
+ flexDirection: "column",
235
+ padding: 1,
236
+ children: [
237
+ /* @__PURE__ */ jsxs(Box, {
238
+ borderStyle: "round",
239
+ borderColor: result.color || "cyan",
240
+ paddingX: 2,
241
+ marginBottom: 1,
242
+ children: [/* @__PURE__ */ jsxs(Text, {
243
+ bold: true,
244
+ color: result.color || "cyan",
245
+ children: [result.icon && `${result.icon} `, result.title]
246
+ }), result.subtitle && /* @__PURE__ */ jsxs(Text, {
247
+ dimColor: true,
248
+ children: [
249
+ " (",
250
+ result.subtitle,
251
+ ")"
252
+ ]
253
+ })]
254
+ }),
255
+ /* @__PURE__ */ jsx(Box, {
256
+ flexDirection: "column",
257
+ marginBottom: 1,
258
+ children: result.content
259
+ }),
260
+ /* @__PURE__ */ jsx(Box, {
261
+ flexDirection: "column",
262
+ children: result.menu.map((item, i) => /* @__PURE__ */ jsx(MenuItemRow, {
263
+ item,
264
+ isSelected: i === selectedIndex
265
+ }, item.key))
266
+ }),
267
+ /* @__PURE__ */ jsx(Box, {
268
+ marginTop: 1,
269
+ children: /* @__PURE__ */ jsx(Text, {
270
+ dimColor: true,
271
+ children: result.footer || "[↑↓] navigate | [Enter] select"
272
+ })
273
+ })
274
+ ]
275
+ });
276
+ }
277
+
278
+ //#endregion
279
+ //#region src/pages/sap/bc/adt/cts/transportrequests/[slug].loader.ts
280
+ /**
281
+ * Transport Request/Task Loader
282
+ *
283
+ * Uses adt-contracts for type-safe XML parsing.
284
+ */
285
+ /**
286
+ * Load and parse transport request/task XML
287
+ */
288
+ function load(xml) {
289
+ return transformToTransportData(transportmanagmentSingle.parse(xml));
290
+ }
291
+ /**
292
+ * Transform parsed XSD data to TransportData
293
+ */
294
+ function transformToTransportData(data) {
295
+ const request = data.root?.request;
296
+ if (!request) throw new Error("No request element found in transport data");
297
+ const isTask = !!request.parent;
298
+ const tasks = [];
299
+ if (request.task && !isTask) {
300
+ const taskArray = Array.isArray(request.task) ? request.task : [request.task];
301
+ for (const task of taskArray) tasks.push({
302
+ number: task.number ?? "",
303
+ owner: task.owner ?? "",
304
+ desc: task.desc ?? "",
305
+ status: task.status ?? "",
306
+ statusText: task.status_text ?? "",
307
+ uri: task.uri ?? ""
308
+ });
309
+ }
310
+ const objects = [];
311
+ if (request.task && isTask) {
312
+ const taskArray = Array.isArray(request.task) ? request.task : [request.task];
313
+ for (const task of taskArray) if (task.abap_object) {
314
+ const objArray = Array.isArray(task.abap_object) ? task.abap_object : [task.abap_object];
315
+ for (const obj of objArray) objects.push({
316
+ pgmid: obj.pgmid ?? "",
317
+ type: obj.type ?? "",
318
+ name: obj.name ?? "",
319
+ info: obj.obj_info ?? obj.obj_desc ?? "",
320
+ lockStatus: obj.lock_status ?? "",
321
+ uri: obj.uri
322
+ });
323
+ }
324
+ }
325
+ return {
326
+ isTask,
327
+ number: request.number ?? "",
328
+ parent: request.parent,
329
+ owner: request.owner ?? "",
330
+ desc: request.desc ?? "",
331
+ type: request.type ?? "",
332
+ status: request.status ?? "",
333
+ statusText: request.status_text ?? "",
334
+ target: request.target ?? "",
335
+ targetDesc: request.target_desc ?? "",
336
+ tasks,
337
+ objects,
338
+ raw: data
339
+ };
340
+ }
341
+
342
+ //#endregion
343
+ //#region src/pages/sap/bc/adt/cts/transportrequests/[slug].tsx
344
+ /**
345
+ * Transport Request/Task Page
346
+ *
347
+ * Returns PageResult for framework-driven rendering.
348
+ * Uses schema-driven parsing via [slug].loader.ts
349
+ */
350
+ /**
351
+ * Get status color
352
+ */
353
+ function getStatusColor(status) {
354
+ switch (status) {
355
+ case "D": return "yellow";
356
+ case "R": return "green";
357
+ case "O": return "cyan";
358
+ default: return "white";
359
+ }
360
+ }
361
+ /**
362
+ * Transport Page - returns PageResult for framework rendering
363
+ */
364
+ function transportPage({ url: _url, response }) {
365
+ let transport;
366
+ try {
367
+ transport = load(response.raw);
368
+ } catch (_e) {
369
+ return null;
370
+ }
371
+ const menu = [{
372
+ key: "back",
373
+ label: "Back",
374
+ icon: "←",
375
+ isBack: true
376
+ }];
377
+ if (transport.isTask && transport.parent) menu.push({
378
+ key: "parent",
379
+ label: "Go to Parent Request",
380
+ icon: "⬆️",
381
+ href: `/sap/bc/adt/cts/transportrequests/${transport.parent}`
382
+ });
383
+ for (const task of transport.tasks) menu.push({
384
+ key: task.number,
385
+ label: `${task.number} - ${task.desc || "(no desc)"}`,
386
+ icon: "📝",
387
+ href: task.uri,
388
+ info: `@${task.owner}`,
389
+ status: {
390
+ text: task.statusText,
391
+ color: getStatusColor(task.status)
392
+ }
393
+ });
394
+ for (const obj of transport.objects) {
395
+ const lockIcon = obj.lockStatus === "X" ? "🔒" : "📦";
396
+ menu.push({
397
+ key: `${obj.pgmid}-${obj.name}`,
398
+ label: `${obj.type} ${obj.name}`,
399
+ icon: lockIcon,
400
+ href: obj.uri,
401
+ info: obj.uri ? void 0 : "(no link)"
402
+ });
403
+ }
404
+ for (const action of [
405
+ {
406
+ rel: "consistencycheck",
407
+ label: "Consistency Check",
408
+ icon: "🔍"
409
+ },
410
+ {
411
+ rel: "newreleasejobs",
412
+ label: "Release",
413
+ icon: "🚀"
414
+ },
415
+ {
416
+ rel: "transportchecks",
417
+ label: "Transport Checks",
418
+ icon: "✅"
419
+ }
420
+ ]) {
421
+ const link = response.links.find((l) => l.rel.includes(action.rel));
422
+ if (link) menu.push({
423
+ key: action.rel,
424
+ label: action.label,
425
+ icon: action.icon,
426
+ href: link.href
427
+ });
428
+ }
429
+ const content = /* @__PURE__ */ jsxs(Box, {
430
+ flexDirection: "column",
431
+ children: [
432
+ /* @__PURE__ */ jsxs(Text, { children: [
433
+ /* @__PURE__ */ jsx(Text, {
434
+ bold: true,
435
+ children: "Description:"
436
+ }),
437
+ " ",
438
+ transport.desc || "(no description)"
439
+ ] }),
440
+ /* @__PURE__ */ jsxs(Text, { children: [
441
+ /* @__PURE__ */ jsx(Text, {
442
+ bold: true,
443
+ children: "Owner:"
444
+ }),
445
+ " ",
446
+ transport.owner
447
+ ] }),
448
+ /* @__PURE__ */ jsxs(Text, { children: [
449
+ /* @__PURE__ */ jsx(Text, {
450
+ bold: true,
451
+ children: "Type:"
452
+ }),
453
+ " ",
454
+ transport.type
455
+ ] }),
456
+ /* @__PURE__ */ jsxs(Text, { children: [
457
+ /* @__PURE__ */ jsx(Text, {
458
+ bold: true,
459
+ children: "Status:"
460
+ }),
461
+ " ",
462
+ /* @__PURE__ */ jsxs(Text, {
463
+ color: getStatusColor(transport.status),
464
+ children: [
465
+ transport.statusText,
466
+ " (",
467
+ transport.status,
468
+ ")"
469
+ ]
470
+ })
471
+ ] }),
472
+ transport.target && /* @__PURE__ */ jsxs(Text, { children: [
473
+ /* @__PURE__ */ jsx(Text, {
474
+ bold: true,
475
+ children: "Target:"
476
+ }),
477
+ " ",
478
+ transport.target,
479
+ " ",
480
+ transport.targetDesc && `(${transport.targetDesc})`
481
+ ] }),
482
+ transport.tasks.length > 0 && /* @__PURE__ */ jsx(Box, {
483
+ marginTop: 1,
484
+ children: /* @__PURE__ */ jsxs(Text, {
485
+ bold: true,
486
+ color: "yellow",
487
+ children: [
488
+ "📋 Tasks (",
489
+ transport.tasks.length,
490
+ "):"
491
+ ]
492
+ })
493
+ }),
494
+ transport.objects.length > 0 && /* @__PURE__ */ jsx(Box, {
495
+ marginTop: 1,
496
+ children: /* @__PURE__ */ jsxs(Text, {
497
+ bold: true,
498
+ color: "cyan",
499
+ children: [
500
+ "📦 Objects (",
501
+ transport.objects.length,
502
+ "):"
503
+ ]
504
+ })
505
+ })
506
+ ]
507
+ });
508
+ const isTask = transport.isTask;
509
+ return {
510
+ title: `${isTask ? "Task" : "Transport Request"}: ${transport.number}`,
511
+ icon: isTask ? "�" : "�",
512
+ color: isTask ? "yellow" : "cyan",
513
+ subtitle: isTask && transport.parent ? `Parent: ${transport.parent}` : void 0,
514
+ content,
515
+ menu,
516
+ footer: "[o] XML in VS Code | [e] open in Eclipse ADT"
517
+ };
518
+ }
519
+
520
+ //#endregion
521
+ //#region src/pages/_routes.ts
522
+ /**
523
+ * Auto-generated route manifest
524
+ * DO NOT EDIT - Generated by scripts/generate-routes.ts
525
+ */
526
+ const routes = [{
527
+ pattern: "/sap/bc/adt/cts/transportrequests/:slug",
528
+ regex: /* @__PURE__ */ new RegExp("^/sap/bc/adt/cts/transportrequests/([^/]+)$"),
529
+ page: transportPage
530
+ }];
531
+ /**
532
+ * Match URL to route
533
+ */
534
+ function matchRoute(url) {
535
+ for (const route of routes) if (route.regex.test(url)) return route;
536
+ return null;
537
+ }
538
+
539
+ //#endregion
540
+ //#region src/pages/GenericPage.tsx
541
+ /**
542
+ * Generic Page Component
543
+ *
544
+ * Fallback page for unknown endpoints - shows payload + links.
545
+ * Returns PageResult for framework-driven rendering.
546
+ */
547
+ /**
548
+ * Generic page - returns PageResult for framework rendering
549
+ */
550
+ function genericPage({ url, response }) {
551
+ const summary = extractSummary(response.data);
552
+ const menu = [{
553
+ key: "back",
554
+ label: "Back",
555
+ icon: "←",
556
+ isBack: true
557
+ }];
558
+ for (const link of response.links) {
559
+ const title = link.title || getActionName(link.rel);
560
+ let displayPath = link.href;
561
+ const currentBase = url.split("/").slice(0, -1).join("/");
562
+ if (link.href.startsWith(currentBase)) displayPath = "." + link.href.slice(currentBase.length);
563
+ else if (link.href.startsWith("/sap/bc/adt/")) displayPath = link.href.replace("/sap/bc/adt/", "/");
564
+ if (displayPath.length > 40) displayPath = "..." + displayPath.slice(-37);
565
+ menu.push({
566
+ key: link.href,
567
+ label: title,
568
+ icon: "🔗",
569
+ href: link.href,
570
+ info: displayPath
571
+ });
572
+ }
573
+ const content = /* @__PURE__ */ jsxs(Box, {
574
+ flexDirection: "column",
575
+ children: [/* @__PURE__ */ jsxs(Text, {
576
+ dimColor: true,
577
+ children: ["URL: ", url]
578
+ }), /* @__PURE__ */ jsx(Box, {
579
+ marginTop: 1,
580
+ flexDirection: "column",
581
+ children: summary.length > 0 ? summary.map((item, i) => /* @__PURE__ */ jsxs(Text, { children: [
582
+ /* @__PURE__ */ jsxs(Text, {
583
+ color: "yellow",
584
+ children: [item.key, ":"]
585
+ }),
586
+ " ",
587
+ item.value
588
+ ] }, i)) : /* @__PURE__ */ jsx(Text, {
589
+ dimColor: true,
590
+ children: "No summary available"
591
+ })
592
+ })]
593
+ });
594
+ return {
595
+ title: `${response.namespace || "unknown"}:${response.rootElement || "response"}`,
596
+ icon: "📄",
597
+ color: "cyan",
598
+ content,
599
+ menu,
600
+ footer: `[o] XML | [e] Eclipse ADT | ${response.links.length} links`
601
+ };
602
+ }
603
+ /**
604
+ * Extract key fields for summary display
605
+ */
606
+ function extractSummary(data) {
607
+ const summary = [];
608
+ const seen = /* @__PURE__ */ new Set();
609
+ function extract(obj, prefix = "") {
610
+ if (!obj || typeof obj !== "object") return;
611
+ if (Array.isArray(obj)) {
612
+ obj.forEach((item) => extract(item, prefix));
613
+ return;
614
+ }
615
+ const record = obj;
616
+ for (const [key, value] of Object.entries(record)) {
617
+ if (key.includes("link") || key === "?xml") continue;
618
+ if (key.startsWith("@_") && typeof value === "string" || typeof value === "number") {
619
+ const cleanKey = key.replace("@_", "").replace(/^\w+:/, "");
620
+ if (seen.has(cleanKey) || cleanKey === "version" || cleanKey === "encoding") continue;
621
+ seen.add(cleanKey);
622
+ if ([
623
+ "number",
624
+ "owner",
625
+ "desc",
626
+ "status",
627
+ "status_text",
628
+ "type",
629
+ "target",
630
+ "name",
631
+ "uri"
632
+ ].some((f) => cleanKey.includes(f))) summary.push({
633
+ key: cleanKey,
634
+ value: String(value)
635
+ });
636
+ }
637
+ if (typeof value === "object") extract(value, key);
638
+ }
639
+ }
640
+ extract(data);
641
+ return summary.slice(0, 8);
642
+ }
643
+
644
+ //#endregion
645
+ //#region src/Navigator.tsx
646
+ /**
647
+ * Navigator Component
648
+ *
649
+ * Main entry point - renders pages using the framework.
650
+ * Pages return PageResult, Navigator handles rendering.
651
+ */
652
+ /**
653
+ * Open raw XML in IDE (VS Code)
654
+ */
655
+ function openXmlInIde(xml, url) {
656
+ const filename = `adt_${url.replaceAll(/[^a-zA-Z0-9]/g, "_").slice(-50)}.xml`;
657
+ const filepath = join(tmpdir(), filename);
658
+ writeFileSync(filepath, xml, "utf-8");
659
+ exec(`code "${filepath}"`, (error) => {
660
+ if (error) exec(`xdg-open "${filepath}"`);
661
+ });
662
+ }
663
+ /**
664
+ * Resolve absolute path to a launcher binary from an allow-list of known
665
+ * install locations. Prevents PATH-hijack (S4036) by avoiding bare-name
666
+ * lookups when an absolute path is available. Falls back to the bare name
667
+ * only if none of the known paths exist, so packaged distributions with
668
+ * non-standard layouts still function.
669
+ */
670
+ function resolveLauncher(candidates, fallbackName) {
671
+ for (const p of candidates) if (existsSync(p)) return p;
672
+ return fallbackName;
673
+ }
674
+ const XDG_OPEN = resolveLauncher(["/usr/bin/xdg-open", "/usr/local/bin/xdg-open"], "xdg-open");
675
+ const OPEN_MAC = resolveLauncher(["/usr/bin/open", "/usr/local/bin/open"], "open");
676
+ /**
677
+ * Open in Eclipse ADT
678
+ * Format: adt://[system]/[path]
679
+ * @see https://help.sap.com/docs/abap-cloud/abap-development-tools-user-guide/opening-adt-links
680
+ */
681
+ function openInAdt(systemName, url) {
682
+ const adtUrl = `adt://${systemName}${url}`;
683
+ execFile(XDG_OPEN, [adtUrl], (error) => {
684
+ if (error) execFile(OPEN_MAC, [adtUrl], (err2) => {
685
+ if (err2) console.error("Failed to open ADT link:", err2.message);
686
+ });
687
+ });
688
+ }
689
+ /**
690
+ * Inner component that uses navigation context
691
+ */
692
+ function NavigatorInner({ systemName }) {
693
+ const { current, loading, error, navigate, back } = useNavigation();
694
+ const pageResult = useMemo(() => {
695
+ if (!current) return null;
696
+ return (matchRoute(current.url)?.page ?? genericPage)({
697
+ url: current.url,
698
+ response: current.response
699
+ });
700
+ }, [current]);
701
+ const handleAction = (key) => {
702
+ if (key === "openXml" && current) openXmlInIde(current.response.raw, current.url);
703
+ else if (key === "openAdt" && current && systemName) openInAdt(systemName, current.url);
704
+ };
705
+ if (loading) return /* @__PURE__ */ jsxs(Box, {
706
+ padding: 1,
707
+ children: [/* @__PURE__ */ jsx(Text, {
708
+ color: "cyan",
709
+ children: /* @__PURE__ */ jsx(Spinner, { type: "dots" })
710
+ }), /* @__PURE__ */ jsx(Text, { children: " Loading..." })]
711
+ });
712
+ if (error) return /* @__PURE__ */ jsxs(Box, {
713
+ padding: 1,
714
+ flexDirection: "column",
715
+ children: [/* @__PURE__ */ jsxs(Text, {
716
+ color: "red",
717
+ children: ["❌ Error: ", error]
718
+ }), /* @__PURE__ */ jsx(Text, {
719
+ dimColor: true,
720
+ children: "Press Ctrl+C to exit"
721
+ })]
722
+ });
723
+ if (!current) return /* @__PURE__ */ jsx(UrlPrompt, { onSubmit: navigate });
724
+ if (!pageResult) return /* @__PURE__ */ jsx(Box, {
725
+ padding: 1,
726
+ children: /* @__PURE__ */ jsx(Text, {
727
+ dimColor: true,
728
+ children: "No content loaded"
729
+ })
730
+ });
731
+ return /* @__PURE__ */ jsx(PageRenderer, {
732
+ result: pageResult,
733
+ onNavigate: navigate,
734
+ onBack: back,
735
+ onAction: handleAction
736
+ });
737
+ }
738
+ /**
739
+ * Main Navigator component
740
+ */
741
+ function Navigator({ startUrl, fetch, systemName }) {
742
+ return /* @__PURE__ */ jsx(NavigationProvider, {
743
+ fetch,
744
+ parseResponse,
745
+ initialUrl: startUrl,
746
+ children: /* @__PURE__ */ jsx(NavigatorInner, { systemName })
747
+ });
748
+ }
749
+ /**
750
+ * URL input prompt component
751
+ */
752
+ function UrlPrompt({ onSubmit }) {
753
+ const [url, setUrl] = useState("");
754
+ const handleSubmit = () => {
755
+ if (url.trim()) onSubmit(url.trim());
756
+ };
757
+ return /* @__PURE__ */ jsxs(Box, {
758
+ flexDirection: "column",
759
+ padding: 1,
760
+ children: [
761
+ /* @__PURE__ */ jsx(Text, {
762
+ color: "cyan",
763
+ bold: true,
764
+ children: "ADT TUI - Hypermedia Navigator"
765
+ }),
766
+ /* @__PURE__ */ jsxs(Box, {
767
+ marginTop: 1,
768
+ children: [/* @__PURE__ */ jsx(Text, { children: "Enter URL: " }), /* @__PURE__ */ jsx(TextInput, {
769
+ value: url,
770
+ onChange: setUrl,
771
+ onSubmit: handleSubmit
772
+ })]
773
+ }),
774
+ /* @__PURE__ */ jsx(Box, {
775
+ marginTop: 1,
776
+ children: /* @__PURE__ */ jsx(Text, {
777
+ dimColor: true,
778
+ children: "Example: /sap/bc/adt/cts/transportrequests/TRLK900001"
779
+ })
780
+ })
781
+ ]
782
+ });
783
+ }
784
+
785
+ //#endregion
786
+ //#region src/App.tsx
787
+ /**
788
+ * Main App Component
789
+ *
790
+ * Entry point for the TUI application.
791
+ */
792
+ /**
793
+ * Main TUI Application
794
+ */
795
+ function App({ startUrl, fetch, router: _router, systemName }) {
796
+ const [, setResizeKey] = useState(0);
797
+ useEffect(() => {
798
+ const handleResize = () => {
799
+ setResizeKey((k) => k + 1);
800
+ };
801
+ process.stdout.on("resize", handleResize);
802
+ return () => {
803
+ process.stdout.off("resize", handleResize);
804
+ };
805
+ }, []);
806
+ return /* @__PURE__ */ jsx(Navigator, {
807
+ startUrl,
808
+ fetch,
809
+ systemName
810
+ });
811
+ }
812
+
813
+ //#endregion
814
+ //#region src/run.tsx
815
+ /**
816
+ * Run TUI Application
817
+ *
818
+ * Helper to render the TUI and wait for exit.
819
+ */
820
+ /**
821
+ * Run the TUI application
822
+ *
823
+ * @returns Promise that resolves when the app exits
824
+ */
825
+ async function run(options) {
826
+ const { onExit, ...appProps } = options;
827
+ console.clear();
828
+ const { waitUntilExit, clear } = render(/* @__PURE__ */ jsx(App, { ...appProps }));
829
+ await waitUntilExit();
830
+ clear();
831
+ onExit?.();
832
+ }
833
+
834
+ //#endregion
835
+ export { App, NavigationProvider, Navigator, PageRenderer, categorizeLinks, genericPage, getActionName, parseResponse, run, useNavigation };
836
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +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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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abapify/adt-tui",
3
- "version": "0.4.0",
3
+ "version": "0.4.3",
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.0",
20
+ "@abapify/adt-contracts": "0.4.3",
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.0"
32
+ "@abapify/adt-client": "0.4.3"
33
33
  },
34
34
  "repository": {
35
35
  "type": "git",