@abapify/adt-config 0.4.17 → 0.4.19

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 CHANGED
@@ -1,4 +1,3 @@
1
- //#region src/types.d.ts
2
1
  /**
3
2
  * ADT Configuration Types
4
3
  *
@@ -130,8 +129,6 @@ interface AuthPlugin<TOptions = unknown, TCredentials = unknown> {
130
129
  */
131
130
  refresh?(credentials: TCredentials): Promise<TCredentials | null>;
132
131
  }
133
- //#endregion
134
- //#region src/config-loader.d.ts
135
132
  interface LoadedConfig {
136
133
  /** Raw config data */
137
134
  readonly raw: AdtConfig;
@@ -174,8 +171,6 @@ declare function loadConfig(options?: LoadConfigOptions | string): Promise<Loade
174
171
  * ```
175
172
  */
176
173
  declare function defineConfig(config: AdtConfig): AdtConfig;
177
- //#endregion
178
- //#region src/plugin.d.ts
179
174
  /**
180
175
  * Define an auth plugin with full type inference.
181
176
  *
@@ -203,6 +198,5 @@ declare function defineConfig(config: AdtConfig): AdtConfig;
203
198
  * ```
204
199
  */
205
200
  declare function defineAuthPlugin<TOptions = unknown, TCredentials = unknown>(plugin: AuthPlugin<TOptions, TCredentials>): AuthPlugin<TOptions, TCredentials>;
206
- //#endregion
207
201
  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
202
  //# sourceMappingURL=index.d.mts.map
@@ -1 +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"}
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 CHANGED
@@ -1,7 +1,7 @@
1
1
  import { existsSync, readFileSync, readdirSync } from "node:fs";
2
2
  import { join, resolve } from "node:path";
3
3
 
4
- //#region src/config-loader.ts
4
+ // src/config-loader.ts
5
5
  /**
6
6
  * Config Loader
7
7
  *
@@ -183,8 +183,7 @@ function defineConfig(config) {
183
183
  return config;
184
184
  }
185
185
 
186
- //#endregion
187
- //#region src/plugin.ts
186
+ // src/plugin.ts
188
187
  /**
189
188
  * Define an auth plugin with full type inference.
190
189
  *
@@ -215,6 +214,5 @@ function defineAuthPlugin(plugin) {
215
214
  return plugin;
216
215
  }
217
216
 
218
- //#endregion
219
217
  export { defineAuthPlugin, defineConfig, loadConfig };
220
218
  //# sourceMappingURL=index.mjs.map
@@ -1 +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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"}
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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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abapify/adt-config",
3
- "version": "0.4.17",
3
+ "version": "0.4.19",
4
4
  "description": "Configuration loader for ADT CLI",
5
5
  "license": "MIT",
6
6
  "publishConfig": {