@abapify/adt-config 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.
- package/dist/index.d.mts +208 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +220 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +1 -1
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
//#region src/types.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* ADT Configuration Types
|
|
4
|
+
*
|
|
5
|
+
* Configuration types for adt.config.ts/json files.
|
|
6
|
+
* Includes auth plugin definition types.
|
|
7
|
+
*/
|
|
8
|
+
interface Destination<TOptions = unknown> {
|
|
9
|
+
/** Auth method type identifier (e.g., 'basic', 'slc', 'puppeteer') */
|
|
10
|
+
type: string;
|
|
11
|
+
/** Auth method-specific options (url, etc.) */
|
|
12
|
+
options: TOptions;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Destination input can be:
|
|
16
|
+
* - A full Destination object with type and options
|
|
17
|
+
* - A string URL (to be transformed by a plugin wrapper)
|
|
18
|
+
*/
|
|
19
|
+
type DestinationInput = Destination | string;
|
|
20
|
+
interface AdtConfig {
|
|
21
|
+
/** Named destinations (SID -> destination config or URL string) */
|
|
22
|
+
destinations?: Record<string, DestinationInput>;
|
|
23
|
+
/** CLI command plugins to load dynamically */
|
|
24
|
+
commands?: string[];
|
|
25
|
+
/** Codegen framework configuration */
|
|
26
|
+
codegen?: Record<string, unknown>;
|
|
27
|
+
/** Contract generation configuration */
|
|
28
|
+
contracts?: ContractsConfig;
|
|
29
|
+
/** Incremental transport tree materialization. */
|
|
30
|
+
flow?: FlowConfig;
|
|
31
|
+
/** Allow arbitrary plugin-specific config sections */
|
|
32
|
+
[key: string]: unknown;
|
|
33
|
+
}
|
|
34
|
+
interface FlowFormatConfig {
|
|
35
|
+
id: string;
|
|
36
|
+
options?: Record<string, string | number | boolean>;
|
|
37
|
+
}
|
|
38
|
+
interface FlowSelectorConfig {
|
|
39
|
+
objectTypes?: string[];
|
|
40
|
+
packages?: string[];
|
|
41
|
+
applicationComponents?: string[];
|
|
42
|
+
}
|
|
43
|
+
interface FlowConcurrencyConfig {
|
|
44
|
+
/** Positive integer in [1, 32] */
|
|
45
|
+
metadata?: number;
|
|
46
|
+
/** Positive integer in [1, 32] */
|
|
47
|
+
sources?: number;
|
|
48
|
+
}
|
|
49
|
+
interface FlowConfig {
|
|
50
|
+
format: FlowFormatConfig;
|
|
51
|
+
include?: FlowSelectorConfig;
|
|
52
|
+
concurrency?: FlowConcurrencyConfig;
|
|
53
|
+
/** Positive integer in [1, 64 MiB] */
|
|
54
|
+
maxSourceBytes?: number;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Contract generation configuration
|
|
58
|
+
*/
|
|
59
|
+
interface ContractsConfig {
|
|
60
|
+
/**
|
|
61
|
+
* Discovery source configuration
|
|
62
|
+
* - If file exists: use cached discovery data
|
|
63
|
+
* - If file doesn't exist: fetch from SAP and cache to this path
|
|
64
|
+
*
|
|
65
|
+
* @example 'tmp/discovery/discovery.xml'
|
|
66
|
+
*/
|
|
67
|
+
discovery?: string;
|
|
68
|
+
/** Content-type to schema mapping */
|
|
69
|
+
contentTypeMapping?: ContentTypeMapping | string;
|
|
70
|
+
/** Enabled endpoints whitelist */
|
|
71
|
+
enabledEndpoints?: EnabledEndpoints | string;
|
|
72
|
+
/** Output directory for generated contracts */
|
|
73
|
+
output?: string;
|
|
74
|
+
/** Output directory for documentation */
|
|
75
|
+
docs?: string;
|
|
76
|
+
/** Custom import resolver */
|
|
77
|
+
resolveImports?: () => {
|
|
78
|
+
base: string;
|
|
79
|
+
schemas: string;
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* Clean output directory before generating.
|
|
83
|
+
* When true, removes all files in outputDir before generating new contracts.
|
|
84
|
+
* @default false
|
|
85
|
+
*/
|
|
86
|
+
clean?: boolean;
|
|
87
|
+
}
|
|
88
|
+
interface ContentTypeMapping {
|
|
89
|
+
mapping: Record<string, string>;
|
|
90
|
+
fallbacks: Record<string, string>;
|
|
91
|
+
}
|
|
92
|
+
interface EnabledEndpoints {
|
|
93
|
+
enabled: string[];
|
|
94
|
+
notes?: Record<string, string>;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Result of testing credentials validity
|
|
98
|
+
*/
|
|
99
|
+
interface AuthTestResult {
|
|
100
|
+
success: boolean;
|
|
101
|
+
error?: string;
|
|
102
|
+
statusCode?: number;
|
|
103
|
+
responseTime?: number;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Auth plugin interface
|
|
107
|
+
*
|
|
108
|
+
* Plugins implement this to provide authentication methods.
|
|
109
|
+
* Use `defineAuthPlugin` helper for type safety.
|
|
110
|
+
*/
|
|
111
|
+
interface AuthPlugin<TOptions = unknown, TCredentials = unknown> {
|
|
112
|
+
/** Plugin name (e.g., 'puppeteer', 'basic') */
|
|
113
|
+
readonly name: string;
|
|
114
|
+
/** Human-readable display name */
|
|
115
|
+
readonly displayName?: string;
|
|
116
|
+
/**
|
|
117
|
+
* Authenticate and return credentials
|
|
118
|
+
* @param options - Plugin-specific options from destination config
|
|
119
|
+
*/
|
|
120
|
+
authenticate(options: TOptions): Promise<TCredentials>;
|
|
121
|
+
/**
|
|
122
|
+
* Test if credentials are still valid
|
|
123
|
+
* @param credentials - Previously obtained credentials
|
|
124
|
+
*/
|
|
125
|
+
test(credentials: TCredentials): Promise<AuthTestResult>;
|
|
126
|
+
/**
|
|
127
|
+
* Refresh credentials if supported
|
|
128
|
+
* @param credentials - Current credentials
|
|
129
|
+
* @returns Updated credentials or null if not supported
|
|
130
|
+
*/
|
|
131
|
+
refresh?(credentials: TCredentials): Promise<TCredentials | null>;
|
|
132
|
+
}
|
|
133
|
+
//#endregion
|
|
134
|
+
//#region src/config-loader.d.ts
|
|
135
|
+
interface LoadedConfig {
|
|
136
|
+
/** Raw config data */
|
|
137
|
+
readonly raw: AdtConfig;
|
|
138
|
+
/** Get a destination by name (SID) */
|
|
139
|
+
getDestination(name: string): Destination | undefined;
|
|
140
|
+
/** List all destination names */
|
|
141
|
+
listDestinations(): string[];
|
|
142
|
+
/** Check if destination exists */
|
|
143
|
+
hasDestination(name: string): boolean;
|
|
144
|
+
}
|
|
145
|
+
interface LoadConfigOptions {
|
|
146
|
+
/** Explicit config file path (overrides auto-discovery) */
|
|
147
|
+
configPath?: string;
|
|
148
|
+
/** Current working directory for auto-discovery (defaults to process.cwd()) */
|
|
149
|
+
cwd?: string;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Load configuration with precedence:
|
|
153
|
+
* 1. Explicit configPath (if provided via --config flag)
|
|
154
|
+
* 2. adt.config.ts in cwd
|
|
155
|
+
* 3. adt.config.json in cwd
|
|
156
|
+
* 4. ~/.adt/config.json (global)
|
|
157
|
+
*
|
|
158
|
+
* @param options Load config options or cwd string for backwards compatibility
|
|
159
|
+
*/
|
|
160
|
+
declare function loadConfig(options?: LoadConfigOptions | string): Promise<LoadedConfig>;
|
|
161
|
+
/**
|
|
162
|
+
* Helper function for TypeScript config files.
|
|
163
|
+
* Provides type checking and autocomplete.
|
|
164
|
+
*
|
|
165
|
+
* Usage in adt.config.ts:
|
|
166
|
+
* ```ts
|
|
167
|
+
* import { defineConfig } from '@abapify/adt-config';
|
|
168
|
+
*
|
|
169
|
+
* export default defineConfig({
|
|
170
|
+
* destinations: {
|
|
171
|
+
* DEV: { type: 'basic', options: { url: '...' } },
|
|
172
|
+
* }
|
|
173
|
+
* });
|
|
174
|
+
* ```
|
|
175
|
+
*/
|
|
176
|
+
declare function defineConfig(config: AdtConfig): AdtConfig;
|
|
177
|
+
//#endregion
|
|
178
|
+
//#region src/plugin.d.ts
|
|
179
|
+
/**
|
|
180
|
+
* Define an auth plugin with full type inference.
|
|
181
|
+
*
|
|
182
|
+
* Usage:
|
|
183
|
+
* ```ts
|
|
184
|
+
* import { defineAuthPlugin } from '@abapify/adt-config';
|
|
185
|
+
*
|
|
186
|
+
* interface MyOptions { url: string; }
|
|
187
|
+
* interface MyCredentials { token: string; }
|
|
188
|
+
*
|
|
189
|
+
* export const myPlugin = defineAuthPlugin<MyOptions, MyCredentials>({
|
|
190
|
+
* name: 'my-auth',
|
|
191
|
+
* displayName: 'My Auth Method',
|
|
192
|
+
*
|
|
193
|
+
* async authenticate(options) {
|
|
194
|
+
* // options is typed as MyOptions
|
|
195
|
+
* return { token: '...' };
|
|
196
|
+
* },
|
|
197
|
+
*
|
|
198
|
+
* async test(credentials) {
|
|
199
|
+
* // credentials is typed as MyCredentials
|
|
200
|
+
* return { success: true };
|
|
201
|
+
* },
|
|
202
|
+
* });
|
|
203
|
+
* ```
|
|
204
|
+
*/
|
|
205
|
+
declare function defineAuthPlugin<TOptions = unknown, TCredentials = unknown>(plugin: AuthPlugin<TOptions, TCredentials>): AuthPlugin<TOptions, TCredentials>;
|
|
206
|
+
//#endregion
|
|
207
|
+
export { type AdtConfig, type AuthPlugin, type AuthTestResult, type ContentTypeMapping, type ContractsConfig, type Destination, type DestinationInput, type EnabledEndpoints, type FlowConcurrencyConfig, type FlowConfig, type FlowFormatConfig, type FlowSelectorConfig, type LoadConfigOptions, type LoadedConfig, defineAuthPlugin, defineConfig, loadConfig };
|
|
208
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/config-loader.ts","../src/plugin.ts"],"mappings":";;;;;;;UAWiB,YAAY;;EAE3B;;EAEA,SAAS;;;;;;;KAYC,mBAAmB;UAEd;;EAEf,eAAe,eAAe;;EAG9B;;EAGA,UAAU;;EAGV,YAAY;;EAGZ,OAAO;;GAGN;;UAGc;EACf;EACA,UAAU;;UAGK;EACf;EACA;EACA;;UAGe;;EAEf;;EAEA;;UAGe;EACf,QAAQ;EACR,UAAU;EACV,cAAc;;EAEd;;;;;UAMe;;;;;;;;EAQf;;EAGA,qBAAqB;;EAErB,mBAAmB;;EAEnB;;EAEA;;EAEA;IAAyB;IAAc;;;;;;;EAMvC;;UAGe;EACf,SAAS;EACT,WAAW;;UAGI;EACf;EACA,QAAQ;;;;;UAUO;EACf;EACA;EACA;EACA;;;;;;;;UASe,WAAW,oBAAoB;;WAErC;;WAGA;;;;;EAMT,aAAa,SAAS,WAAW,QAAQ;;;;;EAMzC,KAAK,aAAa,eAAe,QAAQ;;;;;;EAOzC,SAAS,aAAa,eAAe,QAAQ;;;;UCrI9B;;WAEN,KAAK;;EAGd,eAAe,eAAe;;EAG9B;;EAGA,eAAe;;UAuLA;;EAEf;;EAEA;;;;;;;;;;;iBAyCoB,WACpB,UAAS,6BACR,QAAQ;;;;;;;;;;;;;;;;iBAwEK,aAAa,QAAQ,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBClTjC,iBAAiB,oBAAoB,wBACnD,QAAQ,WAAW,UAAU,gBAC5B,WAAW,UAAU"}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
2
|
+
import { join, resolve } from "node:path";
|
|
3
|
+
|
|
4
|
+
//#region src/config-loader.ts
|
|
5
|
+
/**
|
|
6
|
+
* Config Loader
|
|
7
|
+
*
|
|
8
|
+
* Loads ADT configuration from:
|
|
9
|
+
* 1. adt.config.ts (TypeScript config - takes precedence)
|
|
10
|
+
* 2. adt.config.json (JSON config)
|
|
11
|
+
* 3. ~/.adt/config.json (global defaults)
|
|
12
|
+
*/
|
|
13
|
+
const GLOBAL_CONFIG_PATH = resolve(process.env.HOME || process.env.USERPROFILE || ".", ".adt", "config.json");
|
|
14
|
+
/**
|
|
15
|
+
* Load JSON config from a file path
|
|
16
|
+
*/
|
|
17
|
+
function loadJsonConfig(filePath) {
|
|
18
|
+
if (!existsSync(filePath)) return null;
|
|
19
|
+
try {
|
|
20
|
+
return JSON.parse(readFileSync(filePath, "utf8"));
|
|
21
|
+
} catch {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function normalizeDestinationEntry(raw) {
|
|
26
|
+
if (!raw || typeof raw !== "object") return null;
|
|
27
|
+
const candidate = raw;
|
|
28
|
+
if (typeof candidate.type === "string" && "options" in candidate && candidate.options != null && typeof candidate.options === "object") return candidate;
|
|
29
|
+
if (typeof candidate.url === "string" && candidate.uaa != null && typeof candidate.uaa === "object") return {
|
|
30
|
+
type: "@abapify/adt-auth/plugins/service-key",
|
|
31
|
+
options: {
|
|
32
|
+
url: candidate.url,
|
|
33
|
+
serviceKey: candidate
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
if (typeof candidate.url === "string") return candidate.url;
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
function loadDestinationsDirectory(cwd) {
|
|
40
|
+
const destinationsDir = join(cwd, ".adt", "destinations");
|
|
41
|
+
if (!existsSync(destinationsDir)) return null;
|
|
42
|
+
const destinations = {};
|
|
43
|
+
for (const fileName of readdirSync(destinationsDir)) {
|
|
44
|
+
if (!fileName.toLowerCase().endsWith(".json")) continue;
|
|
45
|
+
const sid = fileName.slice(0, -5).toUpperCase();
|
|
46
|
+
const filePath = join(destinationsDir, fileName);
|
|
47
|
+
try {
|
|
48
|
+
const normalized = normalizeDestinationEntry(JSON.parse(readFileSync(filePath, "utf8")));
|
|
49
|
+
if (normalized) destinations[sid] = normalized;
|
|
50
|
+
} catch {}
|
|
51
|
+
}
|
|
52
|
+
if (Object.keys(destinations).length === 0) return null;
|
|
53
|
+
return { destinations };
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Load TypeScript config (requires dynamic import)
|
|
57
|
+
* The TS config is expected to export a default config object
|
|
58
|
+
*/
|
|
59
|
+
async function loadTsConfig(filePath) {
|
|
60
|
+
if (!existsSync(filePath)) return null;
|
|
61
|
+
try {
|
|
62
|
+
const module = await import(filePath);
|
|
63
|
+
return module.default || module;
|
|
64
|
+
} catch {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Merge local config with global config
|
|
70
|
+
* Local destinations override global ones with the same name
|
|
71
|
+
*/
|
|
72
|
+
function mergeWithGlobal(localConfig) {
|
|
73
|
+
const globalConfig = loadJsonConfig(GLOBAL_CONFIG_PATH);
|
|
74
|
+
if (!globalConfig) return localConfig;
|
|
75
|
+
return {
|
|
76
|
+
...globalConfig,
|
|
77
|
+
...localConfig,
|
|
78
|
+
destinations: {
|
|
79
|
+
...globalConfig.destinations,
|
|
80
|
+
...localConfig.destinations
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Merge two local configs where overlay values win, and destinations are merged by key.
|
|
86
|
+
*/
|
|
87
|
+
function mergeLocalConfig(baseConfig, overlayConfig) {
|
|
88
|
+
return {
|
|
89
|
+
...baseConfig,
|
|
90
|
+
...overlayConfig,
|
|
91
|
+
destinations: {
|
|
92
|
+
...baseConfig.destinations,
|
|
93
|
+
...overlayConfig.destinations
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Create LoadedConfig wrapper
|
|
99
|
+
*/
|
|
100
|
+
function createLoadedConfig(config) {
|
|
101
|
+
return {
|
|
102
|
+
raw: config,
|
|
103
|
+
getDestination(name) {
|
|
104
|
+
const dest = config.destinations?.[name];
|
|
105
|
+
if (!dest) return void 0;
|
|
106
|
+
if (typeof dest === "string") return {
|
|
107
|
+
type: "@abapify/adt-auth/plugins/basic",
|
|
108
|
+
options: { url: dest }
|
|
109
|
+
};
|
|
110
|
+
return dest;
|
|
111
|
+
},
|
|
112
|
+
listDestinations() {
|
|
113
|
+
return Object.keys(config.destinations || {});
|
|
114
|
+
},
|
|
115
|
+
hasDestination(name) {
|
|
116
|
+
return name in (config.destinations || {});
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Try loading a config file from an explicit path (JSON or TS/JS).
|
|
122
|
+
*/
|
|
123
|
+
async function loadExplicitConfig(configPath) {
|
|
124
|
+
if (configPath.endsWith(".json")) return loadJsonConfig(configPath);
|
|
125
|
+
return loadTsConfig(configPath);
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Discover a config file by trying multiple extensions in priority order:
|
|
129
|
+
* .ts → .js → .json
|
|
130
|
+
*/
|
|
131
|
+
async function discoverConfig(basePath, baseName) {
|
|
132
|
+
const tsConfig = await loadTsConfig(join(basePath, `${baseName}.ts`));
|
|
133
|
+
if (tsConfig) return tsConfig;
|
|
134
|
+
const jsConfig = await loadTsConfig(join(basePath, `${baseName}.js`));
|
|
135
|
+
if (jsConfig) return jsConfig;
|
|
136
|
+
return loadJsonConfig(join(basePath, `${baseName}.json`));
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Load configuration with precedence:
|
|
140
|
+
* 1. Explicit configPath (if provided via --config flag)
|
|
141
|
+
* 2. adt.config.ts in cwd
|
|
142
|
+
* 3. adt.config.json in cwd
|
|
143
|
+
* 4. ~/.adt/config.json (global)
|
|
144
|
+
*
|
|
145
|
+
* @param options Load config options or cwd string for backwards compatibility
|
|
146
|
+
*/
|
|
147
|
+
async function loadConfig(options = {}) {
|
|
148
|
+
const opts = typeof options === "string" ? { cwd: options } : options;
|
|
149
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
150
|
+
if (opts.configPath) {
|
|
151
|
+
const configPath = resolve(cwd, opts.configPath);
|
|
152
|
+
const config = await loadExplicitConfig(configPath);
|
|
153
|
+
if (config) return createLoadedConfig(mergeWithGlobal(config));
|
|
154
|
+
console.warn(`Warning: Config file not found at ${configPath}, using auto-discovery`);
|
|
155
|
+
}
|
|
156
|
+
const baseLocalConfig = await discoverConfig(cwd, "adt.config");
|
|
157
|
+
const localOverrideConfig = await discoverConfig(join(cwd, ".adt"), "config");
|
|
158
|
+
const destinationsDirConfig = loadDestinationsDirectory(cwd);
|
|
159
|
+
if (baseLocalConfig || localOverrideConfig || destinationsDirConfig) {
|
|
160
|
+
let mergedLocalConfig = baseLocalConfig || {};
|
|
161
|
+
if (destinationsDirConfig) mergedLocalConfig = mergeLocalConfig(mergedLocalConfig, destinationsDirConfig);
|
|
162
|
+
if (localOverrideConfig) mergedLocalConfig = mergeLocalConfig(mergedLocalConfig, localOverrideConfig);
|
|
163
|
+
return createLoadedConfig(mergeWithGlobal(mergedLocalConfig));
|
|
164
|
+
}
|
|
165
|
+
return createLoadedConfig(loadJsonConfig(GLOBAL_CONFIG_PATH) || { destinations: {} });
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Helper function for TypeScript config files.
|
|
169
|
+
* Provides type checking and autocomplete.
|
|
170
|
+
*
|
|
171
|
+
* Usage in adt.config.ts:
|
|
172
|
+
* ```ts
|
|
173
|
+
* import { defineConfig } from '@abapify/adt-config';
|
|
174
|
+
*
|
|
175
|
+
* export default defineConfig({
|
|
176
|
+
* destinations: {
|
|
177
|
+
* DEV: { type: 'basic', options: { url: '...' } },
|
|
178
|
+
* }
|
|
179
|
+
* });
|
|
180
|
+
* ```
|
|
181
|
+
*/
|
|
182
|
+
function defineConfig(config) {
|
|
183
|
+
return config;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
//#endregion
|
|
187
|
+
//#region src/plugin.ts
|
|
188
|
+
/**
|
|
189
|
+
* Define an auth plugin with full type inference.
|
|
190
|
+
*
|
|
191
|
+
* Usage:
|
|
192
|
+
* ```ts
|
|
193
|
+
* import { defineAuthPlugin } from '@abapify/adt-config';
|
|
194
|
+
*
|
|
195
|
+
* interface MyOptions { url: string; }
|
|
196
|
+
* interface MyCredentials { token: string; }
|
|
197
|
+
*
|
|
198
|
+
* export const myPlugin = defineAuthPlugin<MyOptions, MyCredentials>({
|
|
199
|
+
* name: 'my-auth',
|
|
200
|
+
* displayName: 'My Auth Method',
|
|
201
|
+
*
|
|
202
|
+
* async authenticate(options) {
|
|
203
|
+
* // options is typed as MyOptions
|
|
204
|
+
* return { token: '...' };
|
|
205
|
+
* },
|
|
206
|
+
*
|
|
207
|
+
* async test(credentials) {
|
|
208
|
+
* // credentials is typed as MyCredentials
|
|
209
|
+
* return { success: true };
|
|
210
|
+
* },
|
|
211
|
+
* });
|
|
212
|
+
* ```
|
|
213
|
+
*/
|
|
214
|
+
function defineAuthPlugin(plugin) {
|
|
215
|
+
return plugin;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
//#endregion
|
|
219
|
+
export { defineAuthPlugin, defineConfig, loadConfig };
|
|
220
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/config-loader.ts","../src/plugin.ts"],"sourcesContent":["/**\n * Config Loader\n *\n * Loads ADT configuration from:\n * 1. adt.config.ts (TypeScript config - takes precedence)\n * 2. adt.config.json (JSON config)\n * 3. ~/.adt/config.json (global defaults)\n */\n\nimport { existsSync, readFileSync, readdirSync } from 'node:fs';\nimport { resolve, join } from 'node:path';\nimport type { AdtConfig, Destination } from './types';\n\n// =============================================================================\n// Configuration Paths\n// =============================================================================\n\nconst GLOBAL_CONFIG_PATH = resolve(\n process.env.HOME || process.env.USERPROFILE || '.',\n '.adt',\n 'config.json',\n);\n\n// =============================================================================\n// Loaded Config Interface\n// =============================================================================\n\nexport interface LoadedConfig {\n /** Raw config data */\n readonly raw: AdtConfig;\n\n /** Get a destination by name (SID) */\n getDestination(name: string): Destination | undefined;\n\n /** List all destination names */\n listDestinations(): string[];\n\n /** Check if destination exists */\n hasDestination(name: string): boolean;\n}\n\n// =============================================================================\n// Config Loading\n// =============================================================================\n\n/**\n * Load JSON config from a file path\n */\nfunction loadJsonConfig(filePath: string): AdtConfig | null {\n if (!existsSync(filePath)) {\n return null;\n }\n\n try {\n return JSON.parse(readFileSync(filePath, 'utf8'));\n } catch {\n return null;\n }\n}\n\nfunction normalizeDestinationEntry(raw: unknown): Destination | string | null {\n if (!raw || typeof raw !== 'object') {\n return null;\n }\n\n const candidate = raw as Record<string, unknown>;\n\n // Already in destination format\n if (\n typeof candidate.type === 'string' &&\n 'options' in candidate &&\n candidate.options != null &&\n typeof candidate.options === 'object'\n ) {\n return candidate as unknown as Destination;\n }\n\n // Service key JSON format\n if (\n typeof candidate.url === 'string' &&\n candidate.uaa != null &&\n typeof candidate.uaa === 'object'\n ) {\n return {\n type: '@abapify/adt-auth/plugins/service-key',\n options: {\n url: candidate.url,\n serviceKey: candidate,\n },\n };\n }\n\n // URL-only shorthand\n if (typeof candidate.url === 'string') {\n return candidate.url;\n }\n\n return null;\n}\n\nfunction loadDestinationsDirectory(cwd: string): AdtConfig | null {\n const destinationsDir = join(cwd, '.adt', 'destinations');\n if (!existsSync(destinationsDir)) {\n return null;\n }\n\n const destinations: Record<string, Destination | string> = {};\n\n for (const fileName of readdirSync(destinationsDir)) {\n if (!fileName.toLowerCase().endsWith('.json')) {\n continue;\n }\n\n const sid = fileName.slice(0, -5).toUpperCase();\n const filePath = join(destinationsDir, fileName);\n\n try {\n const raw = JSON.parse(readFileSync(filePath, 'utf8')) as unknown;\n const normalized = normalizeDestinationEntry(raw);\n if (normalized) {\n destinations[sid] = normalized;\n }\n } catch {\n // Ignore invalid destination files and continue loading others.\n }\n }\n\n if (Object.keys(destinations).length === 0) {\n return null;\n }\n\n return { destinations };\n}\n\n/**\n * Load TypeScript config (requires dynamic import)\n * The TS config is expected to export a default config object\n */\nasync function loadTsConfig(filePath: string): Promise<AdtConfig | null> {\n if (!existsSync(filePath)) {\n return null;\n }\n\n try {\n // Dynamic import for TS/JS config files\n const module = await import(filePath);\n return module.default || module;\n } catch {\n return null;\n }\n}\n\n/**\n * Merge local config with global config\n * Local destinations override global ones with the same name\n */\nfunction mergeWithGlobal(localConfig: AdtConfig): AdtConfig {\n const globalConfig = loadJsonConfig(GLOBAL_CONFIG_PATH);\n\n if (!globalConfig) {\n return localConfig;\n }\n\n return {\n ...globalConfig,\n ...localConfig,\n destinations: {\n ...globalConfig.destinations,\n ...localConfig.destinations,\n },\n };\n}\n\n/**\n * Merge two local configs where overlay values win, and destinations are merged by key.\n */\nfunction mergeLocalConfig(\n baseConfig: AdtConfig,\n overlayConfig: AdtConfig,\n): AdtConfig {\n return {\n ...baseConfig,\n ...overlayConfig,\n destinations: {\n ...baseConfig.destinations,\n ...overlayConfig.destinations,\n },\n };\n}\n\n/**\n * Create LoadedConfig wrapper\n */\nfunction createLoadedConfig(config: AdtConfig): LoadedConfig {\n return {\n raw: config,\n\n getDestination(name: string): Destination | undefined {\n const dest = config.destinations?.[name];\n if (!dest) return undefined;\n // Handle string shorthand (URL) by converting to Destination object\n // Uses built-in basic auth plugin as the default\n if (typeof dest === 'string') {\n return {\n type: '@abapify/adt-auth/plugins/basic',\n options: { url: dest },\n };\n }\n return dest;\n },\n\n listDestinations(): string[] {\n return Object.keys(config.destinations || {});\n },\n\n hasDestination(name: string): boolean {\n return name in (config.destinations || {});\n },\n };\n}\n\nexport interface LoadConfigOptions {\n /** Explicit config file path (overrides auto-discovery) */\n configPath?: string;\n /** Current working directory for auto-discovery (defaults to process.cwd()) */\n cwd?: string;\n}\n\n/**\n * Try loading a config file from an explicit path (JSON or TS/JS).\n */\nasync function loadExplicitConfig(\n configPath: string,\n): Promise<AdtConfig | null> {\n if (configPath.endsWith('.json')) {\n return loadJsonConfig(configPath);\n }\n return loadTsConfig(configPath);\n}\n\n/**\n * Discover a config file by trying multiple extensions in priority order:\n * .ts → .js → .json\n */\nasync function discoverConfig(\n basePath: string,\n baseName: string,\n): Promise<AdtConfig | null> {\n const tsConfig = await loadTsConfig(join(basePath, `${baseName}.ts`));\n if (tsConfig) return tsConfig;\n\n const jsConfig = await loadTsConfig(join(basePath, `${baseName}.js`));\n if (jsConfig) return jsConfig;\n\n return loadJsonConfig(join(basePath, `${baseName}.json`));\n}\n\n/**\n * Load configuration with precedence:\n * 1. Explicit configPath (if provided via --config flag)\n * 2. adt.config.ts in cwd\n * 3. adt.config.json in cwd\n * 4. ~/.adt/config.json (global)\n *\n * @param options Load config options or cwd string for backwards compatibility\n */\nexport async function loadConfig(\n options: LoadConfigOptions | string = {},\n): Promise<LoadedConfig> {\n // Support legacy string parameter (cwd)\n const opts: LoadConfigOptions =\n typeof options === 'string' ? { cwd: options } : options;\n const cwd = opts.cwd ?? process.cwd();\n\n // If explicit config path provided, use it directly\n if (opts.configPath) {\n const configPath = resolve(cwd, opts.configPath);\n const config = await loadExplicitConfig(configPath);\n if (config) {\n return createLoadedConfig(mergeWithGlobal(config));\n }\n // Config path specified but file not found/loadable - continue with auto-discovery\n console.warn(\n `Warning: Config file not found at ${configPath}, using auto-discovery`,\n );\n }\n\n // Discover base local config (adt.config.*)\n const baseLocalConfig = await discoverConfig(cwd, 'adt.config');\n\n // Discover optional local override (.adt/config.*)\n const localOverrideConfig = await discoverConfig(join(cwd, '.adt'), 'config');\n\n // Discover optional destinations directory (.adt/destinations/*.json)\n const destinationsDirConfig = loadDestinationsDirectory(cwd);\n\n if (baseLocalConfig || localOverrideConfig || destinationsDirConfig) {\n let mergedLocalConfig = baseLocalConfig || {};\n\n if (destinationsDirConfig) {\n mergedLocalConfig = mergeLocalConfig(\n mergedLocalConfig,\n destinationsDirConfig,\n );\n }\n\n if (localOverrideConfig) {\n mergedLocalConfig = mergeLocalConfig(\n mergedLocalConfig,\n localOverrideConfig,\n );\n }\n\n return createLoadedConfig(mergeWithGlobal(mergedLocalConfig));\n }\n\n // Fall back to global config\n const globalConfig = loadJsonConfig(GLOBAL_CONFIG_PATH);\n return createLoadedConfig(globalConfig || { destinations: {} });\n}\n\n// =============================================================================\n// Config Helper - defineConfig\n// =============================================================================\n\n/**\n * Helper function for TypeScript config files.\n * Provides type checking and autocomplete.\n *\n * Usage in adt.config.ts:\n * ```ts\n * import { defineConfig } from '@abapify/adt-config';\n *\n * export default defineConfig({\n * destinations: {\n * DEV: { type: 'basic', options: { url: '...' } },\n * }\n * });\n * ```\n */\nexport function defineConfig(config: AdtConfig): AdtConfig {\n return config;\n}\n","/**\n * Auth Plugin Helpers\n *\n * Provides type-safe helpers for defining auth plugins.\n */\n\nimport type { AuthPlugin } from './types';\n\n/**\n * Define an auth plugin with full type inference.\n *\n * Usage:\n * ```ts\n * import { defineAuthPlugin } from '@abapify/adt-config';\n *\n * interface MyOptions { url: string; }\n * interface MyCredentials { token: string; }\n *\n * export const myPlugin = defineAuthPlugin<MyOptions, MyCredentials>({\n * name: 'my-auth',\n * displayName: 'My Auth Method',\n *\n * async authenticate(options) {\n * // options is typed as MyOptions\n * return { token: '...' };\n * },\n *\n * async test(credentials) {\n * // credentials is typed as MyCredentials\n * return { success: true };\n * },\n * });\n * ```\n */\nexport function defineAuthPlugin<TOptions = unknown, TCredentials = unknown>(\n plugin: AuthPlugin<TOptions, TCredentials>,\n): AuthPlugin<TOptions, TCredentials> {\n return plugin;\n}\n"],"mappings":"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"}
|