UNPKG

@sonatel-os/juf

Version:

The community SDK for Orange Money, SMS, Email & Sonatel APIs on the Orange Developer Platform.

1 lines 25.9 kB
{"version":3,"sources":["/Users/JOHNSON028877/Documents/new-juf.js/dist/chunk-W5WEVJMX.cjs","../src/core/logger.js","../src/core/constants.js","../src/core/cache.js","../config/index.js","../config/src/apigee.js","../config/src/apm.js","../config/src/environment.js","../src/core/apiUrl.js","../src/core/requester.js","../src/core/errors.js"],"names":[],"mappings":"AAAA;ACOA,IAAM,WAAA,EAAa;AAAA,EACjB,KAAA,EAAO,CAAA;AAAA,EACP,IAAA,EAAM,CAAA;AAAA,EACN,IAAA,EAAM,CAAA;AAAA,EACN,KAAA,EAAO;AACT,CAAA;AAaA,IAAM,OAAA,EAAN,MAAM,QAAO;AAAA;AAAA,EAEX,CAAA,MAAA;AAAA;AAAA,EAGA,CAAA,KAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,WAAA,CAAY,EAAE,OAAA,EAAS,KAAA,EAAO,MAAA,EAAQ,OAAO,EAAA,EAAI,CAAC,CAAA,EAAG;AACnD,IAAA,IAAA,CAAK,CAAA,OAAA,EAAU,MAAA;AACf,IAAA,IAAA,CAAK,CAAA,MAAA,mBAAS,UAAA,CAAW,KAAK,CAAA,UAAK,UAAA,CAAW,MAAA;AAAA,EAChD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,KAAA,CAAM,KAAA,EAAO;AACX,IAAA,OAAO,IAAI,OAAA,CAAO;AAAA,MAChB,MAAA,EAAQ,CAAA,EAAA;AACD,MAAA;AACR,IAAA;AACH,EAAA;AAAA;AAAA;AAAA;AAAA;AAMM,EAAA;AACM,IAAA;AACZ,EAAA;AAAA;AAAA;AAAA;AAAA;AAMK,EAAA;AACO,IAAA;AACZ,EAAA;AAAA;AAAA;AAAA;AAAA;AAMK,EAAA;AACO,IAAA;AACZ,EAAA;AAAA;AAAA;AAAA;AAAA;AAMM,EAAA;AACM,IAAA;AACZ,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQY,EAAA;AACN,IAAA;AAEE,IAAA;AACJ,MAAA;AACA,MAAA;AACQ,MAAA;AACR,MAAA;AACI,MAAA;AACN,IAAA;AAEM,IAAA;AACC,IAAA;AACT,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQU,EAAA;AACF,IAAA;AACJ,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACD,IAAA;AAEK,IAAA;AAEK,IAAA;AACL,MAAA;AACF,QAAA;AACF,MAAA;AACE,QAAA;AACK,MAAA;AACL,QAAA;AACF,MAAA;AACF,IAAA;AAEO,IAAA;AACT,EAAA;AACF;AAGsB;AACb,EAAA;AACR;ADjCc;AACA;AExGF;AAGA;AAGA;AAGA;AAMA;AAGA;AAGA;AAGA;AAGA;AAGA;AAGA;AAGA;AAGA;AF+EE;AACA;AG1HR;AAIQ;AAYf;AAAY;AAEV,EAAA;AAAA;AAAA;AAAA;AAAA;AAMY,EAAA;AACL,IAAA;AACP,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AASW,EAAA;AACL,IAAA;AACK,MAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACT,IAAA;AACF,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQS,EAAA;AACH,IAAA;AACO,MAAA;AACA,QAAA;AACT,MAAA;AACO,IAAA;AACA,MAAA;AACT,IAAA;AACO,IAAA;AACT,EAAA;AACF;AAMa;AACN;AHsGQ;AACA;AI9KR;AJgLQ;AACA;AK7KF;AACH,EAAA;AACD,IAAA;AACG,IAAA;AACC,IAAA;AACJ,IAAA;AACP,EAAA;AACS,EAAA;AACF,IAAA;AACG,IAAA;AACC,IAAA;AACJ,IAAA;AACP,EAAA;AACK,EAAA;AACH,IAAA;AACO,MAAA;AACG,MAAA;AACC,MAAA;AACJ,MAAA;AACP,IAAA;AACS,IAAA;AACF,MAAA;AACG,MAAA;AACC,MAAA;AACJ,MAAA;AACP,IAAA;AACS,IAAA;AACF,MAAA;AACG,MAAA;AACC,MAAA;AACJ,MAAA;AACP,IAAA;AACF,EAAA;AACW,EAAA;AACJ,IAAA;AACG,IAAA;AACC,IAAA;AACJ,IAAA;AACP,EAAA;AACA,EAAA;AACO,IAAA;AACG,IAAA;AACC,IAAA;AACJ,IAAA;AACP,EAAA;AACA,EAAA;AACO,IAAA;AACG,IAAA;AACC,IAAA;AACJ,IAAA;AACP,EAAA;AACF;AL+Ke;AACA;AMnOF;AACX,EAAA;AACO,IAAA;AACG,IAAA;AACC,IAAA;AACJ,IAAA;AACP,EAAA;AACU,EAAA;AACH,IAAA;AACG,IAAA;AACC,IAAA;AACJ,IAAA;AACP,EAAA;AACa,EAAA;AACN,IAAA;AACG,IAAA;AACC,IAAA;AACJ,IAAA;AACP,EAAA;AACa,EAAA;AACN,IAAA;AACG,IAAA;AACC,IAAA;AACJ,IAAA;AACP,EAAA;AACa,EAAA;AACN,IAAA;AACG,IAAA;AACC,IAAA;AACJ,IAAA;AACP,EAAA;AACW,EAAA;AACJ,IAAA;AACG,IAAA;AACC,IAAA;AACJ,IAAA;AACP,EAAA;AACF;ANqOe;AACA;AO3QF;AACN,EAAA;AACE,IAAA;AACI,IAAA;AACA,IAAA;AACJ,IAAA;AACP,EAAA;AACM,EAAA;AACC,IAAA;AACG,IAAA;AACC,IAAA;AACJ,IAAA;AACP,EAAA;AACF;AP6Qe;AACA;AIzRT;AAMA;AACD,EAAA;AACK,EAAA;AACH,EAAA;AACP;AAGI;AAOE;AACC,EAAA;AACI,IAAA;AACP,IAAA;AACF,EAAA;AACF;AASM;AACE,EAAA;AAEC,EAAA;AACM,IAAA;AAEL,MAAA;AACI,QAAA;AAGF,QAAA;AAGA,QAAA;AACF,UAAA;AACF,QAAA;AAEO,QAAA;AACF,MAAA;AAEE,QAAA;AACT,MAAA;AACK,IAAA;AAEE,MAAA;AACT,IAAA;AACD,EAAA;AAEM,EAAA;AACT;AASM;AACE,EAAA;AACG,IAAA;AACA,IAAA;AACT,EAAA;AAEa,EAAA;AACL,IAAA;AACD,IAAA;AACG,MAAA;AACJ,QAAA;AAEF,MAAA;AACF,IAAA;AACF,EAAA;AACF;AAOM;AAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAOV,EAAA;AACJ,IAAA;AACM,IAAA;AAEF,IAAA;AACF,MAAA;AACF,IAAA;AAEO,IAAA;AACT,EAAA;AACF;AJ0Oe;AACA;AQzVF;AACL,EAAA;AACG,IAAA;AACP,IAAA;AACA,IAAA;AACE,EAAA;AACQ,EAAA;AACC,EAAA;AACN,EAAA;AACT;AAEa;AACH,EAAA;AACD,EAAA;AACG,IAAA;AACC,IAAA;AACX,EAAA;AACF;AR0Ve;AACA;AS/WR;AACA;AACA;AAQD;AAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAeP,EAAA;AACG,IAAA;AAEF,IAAA;AACJ,MAAA;AACG,MAAA;AACJ,IAAA;AAEG,IAAA;AACF,MAAA;AACE,QAAA;AACA,QAAA;AACA,QAAA;AACD,MAAA;AACH,IAAA;AAEO,IAAA;AACT,EAAA;AACF;AAEO;ATsWQ;AACA;AUvYR;AAA6B;AAElC,EAAA;AAAA;AAGA,EAAA;AAAA;AAGA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQY,EAAA;AACJ,IAAA;AACD,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACP,EAAA;AAAA;AAAA;AAAA;AAAA;AAMS,EAAA;AACA,IAAA;AACI,MAAA;AACF,MAAA;AACC,QAAA;AACN,QAAA;AACI,QAAA;AACN,MAAA;AACF,IAAA;AACF,EAAA;AACF;AAOa;AAAiC;AAAA;AAAA;AAAA;AAKhC,EAAA;AACJ,IAAA;AACR,EAAA;AACF;AAOa;AAAqC;AAAA;AAAA;AAAA;AAKpC,EAAA;AACJ,IAAA;AACR,EAAA;AACF;AAOa;AAAsC;AAAA;AAAA;AAAA;AAAA;AAMrC,EAAA;AACJ,IAAA;AACR,EAAA;AACF;AAQa;AACL,EAAA;AACA,EAAA;AAEA,EAAA;AACA,EAAA;AAEK,EAAA;AACb;AV0We;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"/Users/JOHNSON028877/Documents/new-juf.js/dist/chunk-W5WEVJMX.cjs","sourcesContent":[null,"/**\n * @namespace Logger\n * @description Lightweight internal logger for JUF library.\n * Provides structured, level-aware logging without Express/HTTP dependencies.\n * Uses JSON format in production and readable format in development.\n */\n\nconst LOG_LEVELS = {\n error: 0,\n warn: 1,\n info: 2,\n debug: 3,\n};\n\n/**\n * @class Logger\n * @classdesc Minimal structured logger for library-internal use.\n * Not intended for application-level logging — consumers should use their own logger.\n *\n * @example\n * import { logger } from './utils/logger.js';\n *\n * logger.info('Payment checkout prepared', { reference: 'INV001' });\n * logger.error('QR code generation failed', { reference: 'INV001', status: 500 });\n */\nclass Logger {\n /** @type {string} */\n #prefix;\n\n /** @type {number} */\n #level;\n\n /**\n * @param {object} options - Logger configuration.\n * @param {string} [options.prefix='juf'] - Log line prefix / service identifier.\n * @param {string} [options.level='info'] - Minimum log level to output.\n */\n constructor({ prefix = 'juf', level = 'info' } = {}) {\n this.#prefix = prefix;\n this.#level = LOG_LEVELS[level] ?? LOG_LEVELS.info;\n }\n\n /**\n * Creates a child logger with a scoped prefix.\n * @param {string} scope - The scope name (e.g. 'payment', 'auth').\n * @returns {Logger} A new Logger instance with the scoped prefix.\n */\n child(scope) {\n return new Logger({\n prefix: `${this.#prefix}:${scope}`,\n level: Object.keys(LOG_LEVELS).find((k) => LOG_LEVELS[k] === this.#level),\n });\n }\n\n /**\n * @param {string} message - Log message.\n * @param {object} [context] - Structured context metadata.\n */\n error(message, context) {\n this.#log('error', message, context);\n }\n\n /**\n * @param {string} message - Log message.\n * @param {object} [context] - Structured context metadata.\n */\n warn(message, context) {\n this.#log('warn', message, context);\n }\n\n /**\n * @param {string} message - Log message.\n * @param {object} [context] - Structured context metadata.\n */\n info(message, context) {\n this.#log('info', message, context);\n }\n\n /**\n * @param {string} message - Log message.\n * @param {object} [context] - Structured context metadata.\n */\n debug(message, context) {\n this.#log('debug', message, context);\n }\n\n /**\n * @private\n * @param {string} level - Log level.\n * @param {string} message - Log message.\n * @param {object} [context] - Structured context metadata.\n */\n #log(level, message, context) {\n if (LOG_LEVELS[level] > this.#level) return;\n\n const entry = {\n timestamp: new Date().toISOString(),\n level,\n prefix: this.#prefix,\n message,\n ...(context && { context: this.#sanitize(context) }),\n };\n\n const output = level === 'error' || level === 'warn' ? console.error : console.log;\n output(JSON.stringify(entry));\n }\n\n /**\n * Strips sensitive fields from context before logging.\n * @private\n * @param {object} context - The context object to sanitize.\n * @returns {object} Sanitized copy of the context.\n */\n #sanitize(context) {\n const SENSITIVE_KEYS = new Set([\n 'authorization',\n 'password',\n 'client_secret',\n 'access_token',\n 'token',\n 'secret',\n 'cookie',\n ]);\n\n const sanitized = {};\n\n for (const [key, value] of Object.entries(context)) {\n if (SENSITIVE_KEYS.has(key.toLowerCase())) {\n sanitized[key] = '[REDACTED]';\n } else if (typeof value === 'object' && value !== null && !Array.isArray(value)) {\n sanitized[key] = this.#sanitize(value);\n } else {\n sanitized[key] = value;\n }\n }\n\n return sanitized;\n }\n}\n\n/** @type {Logger} Singleton logger instance for internal JUF use. */\nexport const logger = new Logger({\n level: process.env.JUF_LOG_LEVEL || 'info',\n});\n\nexport default Logger;\n","/**\n * @namespace Constants\n * @description Centralized constants for the JUF library.\n * No magic values — every threshold, timeout, and config lives here.\n */\n\n/** @type {number} Cache time-to-live for auth tokens in seconds. */\nexport const AUTH_CACHE_TTL_SECONDS = 240;\n\n/** @type {number} Maximum number of retry attempts for failed HTTP requests. */\nexport const MAX_RETRY_ATTEMPTS = 2;\n\n/** @type {number} Base delay between retries in milliseconds. */\nexport const RETRY_BASE_DELAY_MS = 2000;\n\n/** @type {number} HTTP status code that triggers automatic retry. */\nexport const RETRYABLE_STATUS_CODE = 503;\n\n/** @type {number} Default QR code validity in seconds (1 day). */\nexport const DEFAULT_QR_VALIDITY_SECONDS = 86400;\n\n/** @type {string} Currency unit for payment amounts. */\nexport const CURRENCY_UNIT = 'XOF';\n\n/** @type {string} Default channel for SMS delivery. */\nexport const SMS_CHANNEL = 'SMS';\n\n/** @type {string} Content type for OAuth token requests. */\nexport const OAUTH_CONTENT_TYPE = 'application/x-www-form-urlencoded';\n\n/** @type {string} OAuth grant type used for machine-to-machine auth. */\nexport const OAUTH_GRANT_TYPE = 'client_credentials';\n\n/** @type {string} OAuth token endpoint path. */\nexport const OAUTH_TOKEN_PATH = '/oauth/v1/token';\n\n/** @type {number} HTTP status for bad request / validation errors. */\nexport const HTTP_BAD_REQUEST = 400;\n\n/** @type {number} HTTP status for authentication failures. */\nexport const HTTP_UNAUTHORIZED = 401;\n\n/** @type {number} HTTP status for internal server errors. */\nexport const HTTP_INTERNAL_SERVER_ERROR = 500;\n\n/** @type {number} HTTP status for bad gateway / upstream failures. */\nexport const HTTP_BAD_GATEWAY = 502;\n","/**\n * @namespace CacheUtil\n */\n\nimport NodeCache from 'node-cache';\nimport { logger as rootLogger } from './logger.js';\nimport { AUTH_CACHE_TTL_SECONDS } from './constants.js';\n\nconst logger = rootLogger.child('cache');\n\n/**\n * @class Cache\n * @classdesc Handles caching of data with expiration using NodeCache.\n * Provides methods to store and retrieve cached data, with error handling for reliable cache management.\n *\n * @example\n * const myCache = new Cache(300);\n * myCache.store('myKey', { data: 'value' });\n * const cachedData = myCache.retrieve('myKey');\n */\nclass Cache {\n /** @private @type {NodeCache} */\n #cache;\n\n /**\n * Creates an instance of Cache.\n * @param {number} ttlSeconds - Time-to-live (TTL) in seconds for cached items.\n */\n constructor(ttlSeconds) {\n this.#cache = new NodeCache({ stdTTL: ttlSeconds });\n }\n\n /**\n * Stores a value in the cache with a specified key.\n *\n * @param {string} key - The key under which the value is stored.\n * @param {*} value - The value to be stored. Must be serializable to JSON.\n * @returns {boolean} Returns true if stored successfully, otherwise false.\n */\n store(key, value) {\n try {\n return this.#cache.set(key, JSON.stringify(value));\n } catch (error) {\n logger.error('Failed to store cache entry', { key, error: error.message });\n return false;\n }\n }\n\n /**\n * Retrieves a value from the cache by its key.\n *\n * @param {string} key - The key to retrieve the value.\n * @returns {object|null} Returns the parsed cached value if found, otherwise null.\n */\n retrieve(key) {\n try {\n if (this.#cache.has(key)) {\n return JSON.parse(this.#cache.get(key));\n }\n } catch (error) {\n logger.error('Failed to retrieve cache entry', { key, error: error.message });\n }\n return null;\n }\n}\n\n/**\n * Singleton instance of Cache with the standard auth token TTL.\n * @type {Cache}\n */\nexport const CachingSystem = new Cache(AUTH_CACHE_TTL_SECONDS);\nexport default Cache;\n","import dotenv from 'dotenv';\nimport { apigeeConfig } from './src/apigee.js';\nimport { apmConfig } from './src/apm.js';\nimport { environmentConfig } from './src/environment.js';\n\n/** Placeholder values that indicate credentials were never set. */\nconst PLACEHOLDER_PATTERN = /^<.+>$/;\n\n/**\n * @constant configSchema\n * @description Consolidated configuration schema combining all atomic configurations.\n */\nconst configSchema = {\n ...environmentConfig,\n apigee: apigeeConfig,\n apm: apmConfig,\n};\n\n/** @type {boolean} Tracks whether dotenv has been loaded. */\nlet envLoaded = false;\n\n/**\n * Ensures dotenv is loaded exactly once, on first config access.\n * This avoids side-effects at import time, making the library\n * compatible with serverless/edge environments.\n */\nconst ensureEnvLoaded = () => {\n if (!envLoaded) {\n dotenv.config();\n envLoaded = true;\n }\n};\n\n/**\n * @function loadConfigFromEnv\n * @description Recursively loads configuration values from environment variables based on a given schema.\n * If the environment variable is not set, it falls back to the default value in the schema.\n * @param {Object} schema - The configuration schema object.\n * @returns {Object} - An object with the loaded configuration values.\n */\nconst loadConfigFromEnv = (schema) => {\n const config = {};\n\n Object.entries(schema).forEach(([key, value]) => {\n if (typeof value === 'object' && value !== null && !Array.isArray(value)) {\n // If the value is an object, check if it's a nested schema or a schema property\n if ('env' in value && 'default' in value) {\n const envValue = process.env[value.env];\n\n // Load value from environment variable or use default if not set\n let finalValue = envValue !== undefined ? envValue : value.default;\n\n // Apply the format function if it exists and is a function\n if (typeof value.format === 'function') {\n finalValue = value.format(finalValue);\n }\n\n config[key] = finalValue;\n } else {\n // Recursively process nested objects\n config[key] = loadConfigFromEnv(value);\n }\n } else {\n // Handle unexpected schema structures\n config[key] = value;\n }\n });\n\n return config;\n};\n\n/**\n * Validates that required Apigee credentials are set and not placeholders.\n * Throws immediately with an actionable message if credentials are missing.\n *\n * @param {object} config - The loaded apigee configuration.\n * @throws {Error} When required credentials are missing or still placeholder values.\n */\nconst validateApigeeConfig = (config) => {\n const required = [\n { key: 'client_id', env: 'JUF_APIGEE_CLIENT_ID' },\n { key: 'client_secret', env: 'JUF_APIGEE_CLIENT_SECRET' },\n ];\n\n for (const { key, env } of required) {\n const value = config[key];\n if (!value || PLACEHOLDER_PATTERN.test(value)) {\n throw new Error(\n `Missing ${env} — set it in your .env file or environment variables. ` +\n 'Get your credentials at https://developer.orange-sonatel.com',\n );\n }\n }\n};\n\n/**\n * @object envConfig\n * @description Provides methods to retrieve configuration based on the environment schema.\n * Dotenv is loaded lazily on first access — no side-effects at import time.\n */\nconst envConfig = {\n /**\n * @function get\n * @description Retrieves the configuration for a given schema name.\n * @param {string} name - The name of the schema to retrieve.\n * @returns {Object} - The configuration object for the specified schema.\n */\n get: (name) => {\n ensureEnvLoaded();\n const config = loadConfigFromEnv(configSchema[name]);\n\n if (name === 'apigee') {\n validateApigeeConfig(config);\n }\n\n return config;\n },\n};\n\nexport { envConfig };\n","/**\n * @constant apigeeConfig\n * @description Configuration schema for Apigee settings including URLs, client credentials, and environment flags.\n */\nexport const apigeeConfig = {\n onProd: {\n doc: 'Flag to indicate production environment for Apigee',\n format: 'Boolean',\n default: false,\n env: 'JUF_APIGEE_ON_PROD',\n },\n onPProd: {\n doc: 'Flag to indicate pre-production environment for Apigee',\n format: 'Boolean',\n default: false,\n env: 'JUF_APIGEE_SP_PPROD',\n },\n url: {\n production: {\n doc: 'Production URL for Apigee',\n format: String,\n default: 'https://api.orange-sonatel.com',\n env: 'JUF_APIGEE_PROD_URL',\n },\n sandbox: {\n doc: 'Sandbox URL for Apigee',\n format: String,\n default: 'https://api.sandbox.orange-sonatel.com',\n env: 'JUF_APIGEE_SANDBOX_URL',\n },\n preprod: {\n doc: 'Pre-production URL for Apigee',\n format: String,\n default: 'https://api.preprod.orange-sonatel.com',\n env: 'JUF_APIGEE_PREPROD_URL',\n },\n },\n client_id: {\n doc: 'The Apigee client ID',\n format: String,\n default: '<CLIENT_ID>',\n env: 'JUF_APIGEE_CLIENT_ID',\n },\n client_secret: {\n doc: 'The Apigee client secret',\n format: String,\n default: '<CLIENT_SECRET>',\n env: 'JUF_APIGEE_CLIENT_SECRET',\n },\n decode_qr_sp_authorization: {\n doc: 'The Apigee SP authorization',\n format: String,\n default: '<SP_AUTHORIZATION>',\n env: 'JUF_APIGEE_DECODE_QR_SP_AUTHORIZATION',\n },\n};\n","/**\n * @constant apmConfig\n * @description Configuration schema for APM (Application Performance Monitoring) settings.\n */\nexport const apmConfig = {\n verifyServerCert: {\n doc: 'Verify APM server certificate',\n format: 'Boolean',\n default: false,\n env: 'JUF_ELK_APM_VERIF_CERT',\n },\n logLevel: {\n doc: 'Log level for APM',\n format: String,\n default: 'info',\n env: 'JUF_ELK_APM_LOG_LEVEL',\n },\n environment: {\n doc: 'Environment name for APM',\n format: String,\n default: '<JUF_JS>',\n env: 'JUF_ELK_APM_ENV_NAME',\n },\n serviceName: {\n doc: 'Service name for APM',\n format: String,\n default: '<JUF_JS>',\n env: 'JUF_ELK_APM_SERVICE_NAME',\n },\n secretToken: {\n doc: 'Secret token for APM',\n format: String,\n default: '<PASSWORD>',\n env: 'JUF_ELK_APM_SECRET_TOKEN',\n },\n serverUrl: {\n doc: 'Server URL for APM',\n format: String,\n default: 'http://127.0.0.1:8200',\n env: 'JUF_ELK_APM_SERVER',\n },\n};","/**\n * @constant environmentConfig\n * @description Configuration schema for general environment and app settings.\n */\nexport const environmentConfig = {\n env: {\n doc: 'The application environment',\n format: ['test', 'dev', 'development', 'pprod', 'prod', 'production'],\n default: 'dev',\n env: 'NODE_ENV',\n },\n port: {\n doc: 'The port to bind',\n format: 'port',\n default: 3000,\n env: 'PORT',\n },\n};","import { envConfig } from '../../config/index.js';\n\n/**\n * @function getApiUrl\n * @description Determines the correct API URL based on the environment configuration.\n * @returns {string} The API URL to be used.\n */\nexport const getApiUrl = () => {\n const {\n url: { production, sandbox, preprod },\n onProd,\n onPProd,\n } = envConfig.get('apigee');\n if (onProd) return production;\n if (onPProd) return preprod;\n return sandbox;\n};\n\nexport const getApiEnv = () => {\n const { onProd, onPProd } = envConfig.get('apigee');\n return {\n onProd: Boolean(onProd),\n onPProd: Boolean(onPProd),\n };\n};\n","/**\n * @namespace Shared\n */\n\nimport axios from 'axios';\nimport axiosRetry from 'axios-retry';\nimport https from 'https';\nimport { MAX_RETRY_ATTEMPTS, RETRY_BASE_DELAY_MS, RETRYABLE_STATUS_CODE } from './constants.js';\n\n/**\n * @class Requester\n * @classdesc Utility class to create and configure Axios instances dynamically.\n * Each instance gets its own retry configuration instead of polluting the global axios.\n */\nclass Requester {\n /**\n * Creates a new Axios instance with custom configuration and per-instance retry.\n *\n * @static\n * @method bootstrap\n * @memberof Shared\n * @param {object} config - Custom configuration for Axios.\n * @param {string} config.baseURL - Base URL for requests.\n * @param {object} [config.agentParams] - HTTPS agent parameters.\n * @param {object|false} [config.retry] - Retry configuration, or false to disable.\n * @param {number} [config.retry.retries] - Number of retries (default: 2).\n * @param {number} [config.retry.baseDelay] - Base delay in ms for exponential backoff (default: 2000).\n * @returns {import('axios').AxiosInstance} A configured Axios instance.\n */\n static bootstrap(config) {\n const { agentParams, retry, ...axiosConfig } = config;\n\n const instance = axios.create({\n httpsAgent: new https.Agent(agentParams || {}),\n ...axiosConfig,\n });\n\n if (retry !== false) {\n axiosRetry(instance, {\n retries: retry?.retries ?? MAX_RETRY_ATTEMPTS,\n retryDelay: (retryCount) => retryCount * (retry?.baseDelay ?? RETRY_BASE_DELAY_MS),\n retryCondition: (error) => error.response?.status === RETRYABLE_STATUS_CODE,\n });\n }\n\n return instance;\n }\n}\n\nexport default Requester;\n","/**\n * @namespace Errors\n * @description Custom error hierarchy for JUF library.\n * All errors follow a consistent shape: { success, error: { code, message, details } }\n */\n\nimport { HTTP_BAD_REQUEST, HTTP_UNAUTHORIZED, HTTP_INTERNAL_SERVER_ERROR, HTTP_BAD_GATEWAY } from './constants.js';\n\n/**\n * @class JufError\n * @extends Error\n * @classdesc Base error class for all JUF errors.\n * Provides a consistent error response shape across the library.\n *\n * @example\n * throw new JufError('Something went wrong', 500, 'JUF_INTERNAL_ERROR');\n */\nexport class JufError extends Error {\n /** @type {number} */\n status;\n\n /** @type {string} */\n code;\n\n /** @type {*} */\n details;\n\n /**\n * @param {string} message - Human-readable error message.\n * @param {number} [status=500] - HTTP-equivalent status code.\n * @param {string} [code='JUF_ERROR'] - Machine-readable error code.\n * @param {*} [details=null] - Additional error context.\n */\n constructor(message, status = HTTP_INTERNAL_SERVER_ERROR, code = 'JUF_ERROR', details = null) {\n super(message);\n this.name = this.constructor.name;\n this.status = status;\n this.code = code;\n this.details = details;\n }\n\n /**\n * Returns a consistent JSON-serializable error response.\n * @returns {{ success: false, error: { code: string, message: string, details: * } }}\n */\n toJSON() {\n return {\n success: false,\n error: {\n code: this.code,\n message: this.message,\n ...(this.details && { details: this.details }),\n },\n };\n }\n}\n\n/**\n * @class ValidationError\n * @extends JufError\n * @classdesc Thrown when input validation fails (Superstruct assertions, URL checks, etc.).\n */\nexport class ValidationError extends JufError {\n /**\n * @param {string} message - Description of the validation failure.\n * @param {*} [details=null] - Field-level details about what failed.\n */\n constructor(message, details = null) {\n super(message, HTTP_BAD_REQUEST, 'JUF_VALIDATION_ERROR', details);\n }\n}\n\n/**\n * @class AuthenticationError\n * @extends JufError\n * @classdesc Thrown when authentication with the Apigee platform fails.\n */\nexport class AuthenticationError extends JufError {\n /**\n * @param {string} [message='Authentication failed. Check your credentials and try again.'] - Error message.\n * @param {*} [details=null] - API response details.\n */\n constructor(message = 'Authentication failed. Check your credentials and try again.', details = null) {\n super(message, HTTP_UNAUTHORIZED, 'JUF_AUTH_ERROR', details);\n }\n}\n\n/**\n * @class ExternalServiceError\n * @extends JufError\n * @classdesc Thrown when an external API call (Apigee, payment gateway, etc.) fails.\n */\nexport class ExternalServiceError extends JufError {\n /**\n * @param {string} message - Description of the service failure.\n * @param {number} [status=502] - HTTP status from the upstream service.\n * @param {*} [details=null] - Upstream response body or error details.\n */\n constructor(message, status = HTTP_BAD_GATEWAY, details = null) {\n super(message, status, 'JUF_EXTERNAL_SERVICE_ERROR', details);\n }\n}\n\n/**\n * Extracts a structured JufError from an Axios error response.\n * @param {object} error - Axios error object.\n * @param {string} fallbackMessage - Default message when the upstream provides none.\n * @returns {ExternalServiceError} A structured error ready to throw.\n */\nexport const fromAxiosError = (error, fallbackMessage) => {\n const status = error.response?.status || HTTP_INTERNAL_SERVER_ERROR;\n const upstream = error.response?.data;\n\n const message = upstream?.message || upstream?.error_description || fallbackMessage;\n const details = upstream?.detail || upstream?.error || null;\n\n return new ExternalServiceError(message, status, details);\n};\n"]}