@spfn/core 0.3.0-beta.3 → 0.3.0-beta.30

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.
Files changed (104) hide show
  1. package/README.md +301 -10
  2. package/dist/app-config/index.d.ts +39 -1
  3. package/dist/app-config/index.js.map +1 -1
  4. package/dist/authz/index.js +47 -391
  5. package/dist/authz/index.js.map +1 -1
  6. package/dist/{boss-D16fO2oG.d.ts → boss-BiDCbbAQ.d.ts} +60 -7
  7. package/dist/codegen/index.d.ts +157 -16
  8. package/dist/codegen/index.js +772 -166
  9. package/dist/codegen/index.js.map +1 -1
  10. package/dist/config/index.js +4 -4
  11. package/dist/config/index.js.map +1 -1
  12. package/dist/contract/index.d.ts +39 -16
  13. package/dist/contract/index.js +147 -23
  14. package/dist/contract/index.js.map +1 -1
  15. package/dist/db/index.d.ts +230 -35
  16. package/dist/db/index.js +316 -160
  17. package/dist/db/index.js.map +1 -1
  18. package/dist/env/index.d.ts +6 -442
  19. package/dist/env/index.js +13 -16
  20. package/dist/env/index.js.map +1 -1
  21. package/dist/env/loader.js +24 -1
  22. package/dist/env/loader.js.map +1 -1
  23. package/dist/error-CIZlYJlL.d.ts +157 -0
  24. package/dist/errors/index.d.ts +18 -1
  25. package/dist/errors/index.js +47 -391
  26. package/dist/errors/index.js.map +1 -1
  27. package/dist/event/index.d.ts +5 -5
  28. package/dist/event/index.js +202 -3
  29. package/dist/event/index.js.map +1 -1
  30. package/dist/event/sse/client.d.ts +18 -4
  31. package/dist/event/sse/client.js +94 -18
  32. package/dist/event/sse/client.js.map +1 -1
  33. package/dist/event/sse/index.d.ts +18 -5
  34. package/dist/event/sse/index.js +452 -114
  35. package/dist/event/sse/index.js.map +1 -1
  36. package/dist/event/ws/client.d.ts +3 -2
  37. package/dist/event/ws/index.d.ts +4 -3
  38. package/dist/job/index.d.ts +48 -3
  39. package/dist/job/index.js +131 -52
  40. package/dist/job/index.js.map +1 -1
  41. package/dist/logger/index.js +0 -12
  42. package/dist/logger/index.js.map +1 -1
  43. package/dist/middleware/index.js +52 -397
  44. package/dist/middleware/index.js.map +1 -1
  45. package/dist/nextjs/index.d.ts +75 -14
  46. package/dist/nextjs/index.js +200 -84
  47. package/dist/nextjs/index.js.map +1 -1
  48. package/dist/nextjs/server.d.ts +46 -8
  49. package/dist/nextjs/server.js +15 -1
  50. package/dist/nextjs/server.js.map +1 -1
  51. package/dist/ops/index.d.ts +61 -42
  52. package/dist/ops/index.js +648 -97
  53. package/dist/ops/index.js.map +1 -1
  54. package/dist/registry-C2KQCh0m.d.ts +473 -0
  55. package/dist/route/index.d.ts +5 -2
  56. package/dist/route/index.js +107 -28
  57. package/dist/route/index.js.map +1 -1
  58. package/dist/route/types.d.ts +7 -1
  59. package/dist/{router-Cy7rAfmj.d.ts → route-builder-DRW-MSCJ.d.ts} +51 -126
  60. package/dist/router-C57y-vz1.d.ts +184 -0
  61. package/dist/router-CX5uVjmU.d.ts +406 -0
  62. package/dist/server/index.d.ts +130 -7
  63. package/dist/server/index.js +1174 -164
  64. package/dist/server/index.js.map +1 -1
  65. package/dist/state/client.d.ts +206 -0
  66. package/dist/state/client.js +678 -0
  67. package/dist/state/client.js.map +1 -0
  68. package/dist/state/index.d.ts +443 -0
  69. package/dist/state/index.js +644 -0
  70. package/dist/state/index.js.map +1 -0
  71. package/dist/state/react.d.ts +49 -0
  72. package/dist/state/react.js +50 -0
  73. package/dist/state/react.js.map +1 -0
  74. package/dist/surface/index.d.ts +115 -0
  75. package/dist/surface/index.js +1013 -0
  76. package/dist/surface/index.js.map +1 -0
  77. package/dist/token-manager-Ckjt2-qq.d.ts +122 -0
  78. package/dist/{types-CfYVhIQ9.d.ts → types-5Rl11_Nb.d.ts} +43 -4
  79. package/dist/types-CKH0D3Dy.d.ts +441 -0
  80. package/dist/{types-ZQODsBft.d.ts → types-CurMab2Q.d.ts} +52 -2
  81. package/dist/{types-2AbaW4Ie.d.ts → types-DncqXfWN.d.ts} +4 -3
  82. package/docs/file-upload.md +195 -333
  83. package/package.json +39 -9
  84. package/src/cache/README.md +330 -0
  85. package/src/codegen/README.md +721 -0
  86. package/src/config/README.md +333 -0
  87. package/src/contract/README.md +442 -0
  88. package/src/db/README.md +589 -0
  89. package/src/db/manager/README.md +500 -0
  90. package/src/db/schema/README.md +354 -0
  91. package/src/db/transaction/README.md +845 -0
  92. package/src/env/README.md +787 -0
  93. package/src/errors/README.md +445 -0
  94. package/src/event/README.md +994 -0
  95. package/src/job/README.md +613 -0
  96. package/src/logger/README.md +321 -0
  97. package/src/middleware/README.md +634 -0
  98. package/src/nextjs/README.md +756 -0
  99. package/src/route/README.md +763 -0
  100. package/src/security/README.md +100 -0
  101. package/src/server/README.md +704 -0
  102. package/src/state/README.md +806 -0
  103. package/dist/token-manager-BT5EnUAR.d.ts +0 -278
  104. package/dist/types-9oszaJqp.d.ts +0 -250
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/env/validator.ts","../../src/env/schema.ts","../../src/env/registry.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Environment Variable Management - Parsers\n *\n * Parser functions that transform and validate environment variable strings.\n * All parsers follow the pattern: (value: string) => T or throw Error\n */\n\n// ============================================================================\n// Core Types\n// ============================================================================\n\n/**\n * Parser function that transforms and validates a string value\n * @throws Error if validation fails\n */\nexport type Parser<T> = (value: string) => T;\n\n// ============================================================================\n// String Parsers\n// ============================================================================\n\n/**\n * Parse a non-empty string\n *\n * @param value - Value to parse\n * @returns Trimmed string\n * @throws Error if string is empty after trimming\n *\n * @example\n * ```typescript\n * const name = getEnvVar('APP_NAME', {\n * validator: parseString,\n * });\n * ```\n */\nexport function parseString(value: string): string\n{\n const trimmed = value.trim();\n\n if (trimmed.length === 0)\n {\n throw new Error('Value cannot be empty');\n }\n\n return trimmed;\n}\n\n/**\n * Create a string parser with validation rules\n *\n * @param options - Validation options\n * @returns Parser function\n *\n * @example\n * ```typescript\n * const apiKey = getEnvVar('API_KEY', {\n * validator: createStringParser({\n * minLength: 32,\n * pattern: /^[A-Za-z0-9_-]+$/,\n * }),\n * });\n * ```\n */\nexport function createStringParser(\n options: {\n minLength?: number;\n maxLength?: number;\n pattern?: RegExp;\n trim?: boolean;\n } = {},\n): Parser<string>\n{\n return (value: string): string =>\n {\n const { minLength, maxLength, pattern, trim = true } = options;\n\n let result = trim ? value.trim() : value;\n\n // Empty check\n if (result.length === 0)\n {\n throw new Error('Value cannot be empty');\n }\n\n // Length validation\n if (minLength !== undefined && result.length < minLength)\n {\n throw new Error(`Must be at least ${minLength} characters long (current: ${result.length})`);\n }\n\n if (maxLength !== undefined && result.length > maxLength)\n {\n throw new Error(`Must be at most ${maxLength} characters long (current: ${result.length})`);\n }\n\n // Pattern validation\n if (pattern && !pattern.test(result))\n {\n throw new Error(`Must match pattern ${pattern}`);\n }\n\n return result;\n };\n}\n\n// ============================================================================\n// Boolean Parser\n// ============================================================================\n\n/**\n * Parse a boolean environment variable\n *\n * Accepts: 'true', '1', 'yes' (case-insensitive) → true\n * 'false', '0', 'no' (case-insensitive) → false\n *\n * @param value - Value to parse\n * @returns Boolean value\n * @throws Error if value is not a valid boolean string\n *\n * @example\n * ```typescript\n * const debug = getEnvVar('DEBUG', {\n * default: 'false',\n * validator: parseBoolean,\n * });\n * ```\n */\nexport function parseBoolean(value: string): boolean\n{\n const normalized = value.toLowerCase().trim();\n\n if (['true', '1', 'yes'].includes(normalized))\n {\n return true;\n }\n\n if (['false', '0', 'no'].includes(normalized))\n {\n return false;\n }\n\n throw new Error(\n `Must be a boolean value (true/false, 1/0, yes/no), got: ${value}`,\n );\n}\n\n// ============================================================================\n// Number Parsers\n// ============================================================================\n\n/**\n * Parse and validate number\n *\n * @param value - Value to parse\n * @param options - Validation options\n * @returns Parsed number\n * @throws Error if invalid number or constraint violation\n *\n * @example\n * ```typescript\n * const port = getEnvVar('PORT', {\n * default: '3000',\n * validator: (val) => parseNumber(val, { min: 1, max: 65535, integer: true }),\n * });\n * ```\n */\nexport function parseNumber(\n value: string,\n options: { min?: number; max?: number; integer?: boolean } = {},\n): number\n{\n const { min, max, integer = false } = options;\n\n // Reject empty strings\n if (value.trim() === '')\n {\n throw new Error('Value cannot be empty');\n }\n\n const num = Number(value);\n\n if (isNaN(num))\n {\n throw new Error(`Must be a valid number, got: ${value}`);\n }\n\n if (integer && !Number.isInteger(num))\n {\n throw new Error(`Must be an integer, got: ${value}`);\n }\n\n if (min !== undefined && num < min)\n {\n throw new Error(`Must be at least ${min}, got: ${num}`);\n }\n\n if (max !== undefined && num > max)\n {\n throw new Error(`Must be at most ${max}, got: ${num}`);\n }\n\n return num;\n}\n\n/**\n * Create a number parser with specific constraints\n *\n * @param options - Validation constraints\n * @returns Parser function\n *\n * @example\n * ```typescript\n * const port = getEnvVar('PORT', {\n * default: '3000',\n * validator: createNumberParser({ min: 1, max: 65535, integer: true }),\n * });\n * ```\n */\nexport function createNumberParser(\n options: { min?: number; max?: number; integer?: boolean } = {},\n): Parser<number>\n{\n return (value: string) => parseNumber(value, options);\n}\n\n/**\n * Parse integer with optional constraints\n *\n * @param value - Value to parse\n * @param options - Min/max constraints\n * @returns Parsed integer\n * @throws Error if invalid or out of range\n *\n * @example\n * ```typescript\n * const retries = getEnvVar('MAX_RETRIES', {\n * default: '3',\n * validator: (val) => parseInteger(val, { min: 1, max: 10 }),\n * });\n * ```\n */\nexport function parseInteger(\n value: string,\n options: { min?: number; max?: number } = {},\n): number\n{\n return parseNumber(value, { ...options, integer: true });\n}\n\n/**\n * Parse float/decimal number with optional constraints\n *\n * @param value - Value to parse\n * @param options - Min/max constraints\n * @returns Parsed decimal number\n * @throws Error if invalid or out of range\n *\n * @example\n * ```typescript\n * const ratio = getEnvVar('CACHE_RATIO', {\n * default: '0.75',\n * validator: (val) => parseDecimal(val, { min: 0, max: 1 }),\n * });\n * ```\n */\nexport function parseDecimal(\n value: string,\n options: { min?: number; max?: number } = {},\n): number\n{\n return parseNumber(value, { ...options, integer: false });\n}\n\n// ============================================================================\n// URL Parsers\n// ============================================================================\n\n/**\n * Parse and validate URL\n *\n * @param value - Value to parse\n * @param options - Validation options\n * @returns Validated URL string\n * @throws Error if invalid URL or protocol mismatch\n *\n * @example\n * ```typescript\n * const apiUrl = getEnvVar('API_URL', {\n * validator: (val) => parseUrl(val, { protocol: 'https' }),\n * });\n * ```\n */\nexport function parseUrl(\n value: string,\n options: { protocol?: 'http' | 'https' | 'any' } = {},\n): string\n{\n const { protocol = 'any' } = options;\n\n // Parse URL (may throw TypeError)\n let url: URL;\n try\n {\n url = new URL(value);\n }\n catch (error)\n {\n if (error instanceof TypeError)\n {\n throw new Error(`Invalid URL: ${value}`);\n }\n throw error;\n }\n\n // Validate protocol\n if (protocol === 'http' && url.protocol !== 'http:')\n {\n throw new Error(`URL must use HTTP protocol, got ${url.protocol}`);\n }\n\n if (protocol === 'https' && url.protocol !== 'https:')\n {\n throw new Error(`URL must use HTTPS protocol, got ${url.protocol}`);\n }\n\n return value;\n}\n\n/**\n * Create a URL parser with specific protocol requirement\n *\n * @param protocol - Required protocol ('http', 'https', or 'any')\n * @returns Parser function\n *\n * @example\n * ```typescript\n * const apiUrl = getEnvVar('API_URL', {\n * validator: createUrlParser('https'),\n * });\n * ```\n */\nexport function createUrlParser(protocol: 'http' | 'https' | 'any' = 'any'): Parser<string>\n{\n return (value: string) => parseUrl(value, { protocol });\n}\n\n/**\n * Parse PostgreSQL connection string\n *\n * @param value - Value to parse\n * @returns Validated PostgreSQL URL string\n * @throws Error if invalid PostgreSQL URL\n *\n * @example\n * ```typescript\n * const dbUrl = getEnvVar('DATABASE_URL', {\n * required: true,\n * validator: parsePostgresUrl,\n * });\n * ```\n */\nexport function parsePostgresUrl(value: string): string\n{\n // Parse URL (may throw TypeError)\n let url: URL;\n try\n {\n url = new URL(value);\n }\n catch (error)\n {\n if (error instanceof TypeError)\n {\n // Never echo the value — these URLs carry embedded credentials\n throw new Error('Invalid PostgreSQL URL');\n }\n throw error;\n }\n\n // Validate protocol\n if (url.protocol !== 'postgres:' && url.protocol !== 'postgresql:')\n {\n throw new Error(\n `Must be a PostgreSQL URL (postgres:// or postgresql://), got ${url.protocol}`,\n );\n }\n\n return value;\n}\n\n/**\n * Parse Redis connection string\n *\n * @param value - Value to parse\n * @returns Validated Redis URL string\n * @throws Error if invalid Redis URL\n *\n * @example\n * ```typescript\n * const redisUrl = getEnvVar('REDIS_URL', {\n * required: true,\n * validator: parseRedisUrl,\n * });\n * ```\n */\nexport function parseRedisUrl(value: string): string\n{\n // Parse URL (may throw TypeError)\n let url: URL;\n try\n {\n url = new URL(value);\n }\n catch (error)\n {\n if (error instanceof TypeError)\n {\n // Never echo the value — these URLs carry embedded credentials\n throw new Error('Invalid Redis URL');\n }\n throw error;\n }\n\n // Validate protocol\n if (url.protocol !== 'redis:' && url.protocol !== 'rediss:')\n {\n throw new Error(\n `Must be a Redis URL (redis:// or rediss://), got ${url.protocol}`,\n );\n }\n\n return value;\n}\n\n// ============================================================================\n// Enum Parser\n// ============================================================================\n\n/**\n * Parse and validate enum value\n *\n * @param value - Value to parse\n * @param allowed - Array of allowed values\n * @param caseInsensitive - Whether to perform case-insensitive comparison\n * @returns Validated enum value\n * @throws Error if value not in allowed list\n *\n * @example\n * ```typescript\n * const env = getEnvVar('NODE_ENV', {\n * validator: (val) => parseEnum(val, ['development', 'production', 'test']),\n * });\n * ```\n */\nexport function parseEnum(\n value: string,\n allowed: string[],\n caseInsensitive = false,\n): string\n{\n if (caseInsensitive)\n {\n const normalizedValue = value.toLowerCase();\n const normalizedAllowed = allowed.map((v) => v.toLowerCase());\n const index = normalizedAllowed.indexOf(normalizedValue);\n\n if (index === -1)\n {\n throw new Error(\n `Must be one of [${allowed.join(', ')}], got: ${value}`,\n );\n }\n\n return allowed[index]; // Return original case from allowed list\n }\n\n if (!allowed.includes(value))\n {\n throw new Error(\n `Must be one of [${allowed.join(', ')}], got: ${value}`,\n );\n }\n\n return value;\n}\n\n/**\n * Create an enum parser with specific allowed values\n *\n * @param allowed - Array of allowed values\n * @param caseInsensitive - Whether to perform case-insensitive comparison\n * @returns Parser function\n *\n * @example\n * ```typescript\n * const logLevel = getEnvVar('LOG_LEVEL', {\n * default: 'info',\n * validator: createEnumParser(['debug', 'info', 'warn', 'error']),\n * });\n * ```\n */\nexport function createEnumParser(allowed: string[], caseInsensitive = false): Parser<string>\n{\n return (value: string) => parseEnum(value, allowed, caseInsensitive);\n}\n\n// ============================================================================\n// JSON Parser\n// ============================================================================\n\n/**\n * Parse JSON string\n *\n * @param value - JSON string to parse\n * @returns Parsed JSON value\n * @throws Error if invalid JSON\n *\n * @example\n * ```typescript\n * const config = getEnvVar('CONFIG_JSON', {\n * validator: parseJson,\n * });\n * ```\n */\nexport function parseJson<T = any>(value: string): T\n{\n try\n {\n return JSON.parse(value) as T;\n }\n catch (error)\n {\n throw new Error(\n `Invalid JSON: ${error instanceof Error ? error.message : 'Unknown error'}`,\n );\n }\n}\n\n/**\n * Create a typed JSON parser\n *\n * @returns Parser function\n *\n * @example\n * ```typescript\n * interface Config {\n * host: string;\n * port: number;\n * }\n *\n * const config = getEnvVar('CONFIG_JSON', {\n * validator: createJsonParser<Config>(),\n * });\n * ```\n */\nexport function createJsonParser<T>(): Parser<T>\n{\n return (value: string) => parseJson<T>(value);\n}\n\n// ============================================================================\n// Array Parser\n// ============================================================================\n\n/**\n * Parse comma-separated values into array\n *\n * @param value - Comma-separated string\n * @param options - Parser options\n * @returns Array of strings\n *\n * @example\n * ```typescript\n * const hosts = getEnvVar('ALLOWED_HOSTS', {\n * validator: parseArray,\n * });\n * // \"localhost,example.com,api.example.com\" → ['localhost', 'example.com', 'api.example.com']\n * ```\n */\nexport function parseArray(\n value: string,\n options: {\n separator?: string;\n trim?: boolean;\n filter?: (item: string) => boolean;\n } = {},\n): string[]\n{\n const { separator = ',', trim = true, filter } = options;\n\n if (value.trim() === '')\n {\n return [];\n }\n\n let items = value.split(separator);\n\n if (trim)\n {\n items = items.map((item) => item.trim());\n }\n\n if (filter)\n {\n items = items.filter(filter);\n }\n\n return items;\n}\n\n/**\n * Create an array parser with item parser\n *\n * @param itemParser - Parser to apply to each array item\n * @param options - Array parsing options\n * @returns Parser function\n *\n * @example\n * ```typescript\n * // Parse comma-separated ports\n * const ports = getEnvVar('PORTS', {\n * validator: createArrayParser(\n * createNumberParser({ min: 1, max: 65535, integer: true })\n * ),\n * });\n * // \"3000,4000,5000\" → [3000, 4000, 5000]\n * ```\n */\nexport function createArrayParser<T>(\n itemParser: Parser<T>,\n options: { separator?: string } = {},\n): Parser<T[]>\n{\n return (value: string): T[] =>\n {\n const items = parseArray(value, options);\n\n return items.map((item, index) =>\n {\n try\n {\n return itemParser(item);\n }\n catch (error)\n {\n const message = error instanceof Error ? error.message : String(error);\n throw new Error(`Invalid item at index ${index}: ${message}`);\n }\n });\n };\n}\n\n// ============================================================================\n// Secure Secret Parser\n// ============================================================================\n\n/**\n * Calculate Shannon entropy of a string\n * Returns entropy in bits per character\n *\n * @param str - String to calculate entropy for\n * @returns Entropy value (0 to ~6.6 bits for printable ASCII)\n *\n * @example\n * ```typescript\n * const entropy = calculateEntropy('my-secret-key');\n * // Higher entropy = more random\n * // - Random lowercase: ~4.7 bits/char\n * // - Random alphanumeric: ~5.2 bits/char\n * // - Random printable ASCII: ~6.6 bits/char\n * // - \"aaaaaaa...\": ~0 bits/char\n * ```\n */\nfunction calculateEntropy(str: string): number\n{\n const len = str.length;\n const frequencies = new Map<string, number>();\n\n // Count character frequencies\n for (const char of str)\n {\n frequencies.set(char, (frequencies.get(char) || 0) + 1);\n }\n\n // Calculate Shannon entropy\n let entropy = 0;\n for (const count of frequencies.values())\n {\n const probability = count / len;\n entropy -= probability * Math.log2(probability);\n }\n\n return entropy;\n}\n\n/**\n * Create a secure secret parser with entropy validation\n *\n * Validates cryptographic secrets for sufficient length, character diversity, and randomness.\n * Uses Shannon entropy to measure randomness quality.\n *\n * @param options - Validation options\n * @returns Parser function\n *\n * @example\n * ```typescript\n * const sessionSecret = getEnvVar('SESSION_SECRET', {\n * validator: createSecureSecretParser({\n * minLength: 32, // Minimum 256-bit\n * minUniqueChars: 16, // Character diversity\n * minEntropy: 3.5, // Shannon entropy (bits/char)\n * }),\n * });\n * ```\n */\nexport function createSecureSecretParser(\n options: {\n minLength?: number;\n minUniqueChars?: number;\n minEntropy?: number;\n } = {},\n): Parser<string>\n{\n const {\n minLength = 32,\n minUniqueChars = 16,\n minEntropy = 3.5,\n } = options;\n\n return (value: string): string =>\n {\n const length = value.length;\n const uniqueChars = new Set(value).size;\n const entropy = calculateEntropy(value);\n\n // Check length (minimum for cryptographic strength)\n if (length < minLength)\n {\n throw new Error(\n `Secret too short: ${length} characters (minimum: ${minLength})`,\n );\n }\n\n // Check unique character diversity\n if (uniqueChars < minUniqueChars)\n {\n throw new Error(\n `Secret has low diversity: ${uniqueChars} unique characters (minimum: ${minUniqueChars})`,\n );\n }\n\n // Check Shannon entropy (randomness quality)\n // Reference values:\n // - Random lowercase: ~4.7 bits/char\n // - Random alphanumeric: ~5.2 bits/char\n // - Random printable ASCII: ~6.6 bits/char\n // - \"aaaaaaa...\": ~0 bits/char\n // - \"abcabcabc...\": ~1.58 bits/char\n if (entropy < minEntropy)\n {\n throw new Error(\n `Secret has low entropy: ${entropy.toFixed(2)} bits/char (minimum: ${minEntropy}). Use a more random secret.`,\n );\n }\n\n return value;\n };\n}\n\n// ============================================================================\n// Password Parser\n// ============================================================================\n\n/**\n * Create a password strength parser\n *\n * Validates password strength based on configurable requirements.\n * Useful for enforcing password policies in environment variables or user input.\n *\n * @param options - Validation options\n * @returns Parser function\n *\n * @example\n * ```typescript\n * const adminPassword = getEnvVar('ADMIN_PASSWORD', {\n * validator: createPasswordParser({\n * minLength: 12,\n * requireUppercase: true,\n * requireLowercase: true,\n * requireNumber: true,\n * requireSpecial: true,\n * }),\n * });\n * ```\n */\nexport function createPasswordParser(\n options: {\n minLength?: number;\n requireUppercase?: boolean;\n requireLowercase?: boolean;\n requireNumber?: boolean;\n requireSpecial?: boolean;\n } = {},\n): Parser<string>\n{\n const {\n minLength = 8,\n requireUppercase = true,\n requireLowercase = true,\n requireNumber = true,\n requireSpecial = true,\n } = options;\n\n return (value: string): string =>\n {\n const errors: string[] = [];\n\n // Length check\n if (value.length < minLength)\n {\n errors.push(`Must be at least ${minLength} characters`);\n }\n\n // Uppercase check\n if (requireUppercase && !/[A-Z]/.test(value))\n {\n errors.push('Must contain at least one uppercase letter');\n }\n\n // Lowercase check\n if (requireLowercase && !/[a-z]/.test(value))\n {\n errors.push('Must contain at least one lowercase letter');\n }\n\n // Number check\n if (requireNumber && !/[0-9]/.test(value))\n {\n errors.push('Must contain at least one number');\n }\n\n // Special character check\n if (requireSpecial && !/[^A-Za-z0-9]/.test(value))\n {\n errors.push('Must contain at least one special character');\n }\n\n if (errors.length > 0)\n {\n throw new Error(`Password validation failed: ${errors.join(', ')}`);\n }\n\n return value;\n };\n}\n\n// ============================================================================\n// Parser Composition\n// ============================================================================\n\n/**\n * Chain multiple parsers sequentially\n *\n * Each parser receives the output of the previous parser.\n * Useful for multi-step validation/transformation.\n *\n * @param parsers - Array of parser functions\n * @returns Combined parser function\n *\n * @example\n * ```typescript\n * const apiKey = getEnvVar('API_KEY', {\n * validator: chain(\n * parseString,\n * createStringParser({ minLength: 32, pattern: /^[A-Za-z0-9_-]+$/ }),\n * ),\n * });\n * ```\n */\nexport function chain<T>(...parsers: Array<Parser<T>>): Parser<T>\n{\n return (value: string): T =>\n {\n let result = value as any;\n\n for (const parser of parsers)\n {\n result = parser(result);\n }\n\n return result;\n };\n}\n\n/**\n * Apply parser with fallback value\n *\n * If parser throws, returns fallback instead.\n * Useful for optional environment variables with complex parsing.\n *\n * @param parser - Parser to attempt\n * @param fallback - Fallback value if parsing fails\n * @returns Parser function\n *\n * @example\n * ```typescript\n * const config = getEnvVar('CONFIG_JSON', {\n * validator: withFallback(parseJson, { host: 'localhost', port: 3000 }),\n * });\n * ```\n */\nexport function withFallback<T>(parser: Parser<T>, fallback: T): Parser<T>\n{\n return (value: string): T =>\n {\n try\n {\n return parser(value);\n }\n catch\n {\n return fallback;\n }\n };\n}\n\n/**\n * Make parser optional\n *\n * Returns undefined for empty strings instead of throwing.\n *\n * @param parser - Parser to make optional\n * @returns Parser function that returns T | undefined\n *\n * @example\n * ```typescript\n * const redisUrl = getEnvVar('REDIS_URL', {\n * validator: optional(parseRedisUrl),\n * });\n * // Empty string → undefined\n * // Valid URL → parsed URL\n * // Invalid URL → throws\n * ```\n */\nexport function optional<T>(parser: Parser<T>): Parser<T | undefined>\n{\n return (value: string): T | undefined =>\n {\n if (value.trim() === '')\n {\n return undefined;\n }\n\n return parser(value);\n };\n}\n","/**\n * Environment Variable Schema Definition System\n *\n * 환경변수에 메타데이터를 정의하여 중앙 관리, 문서화, 검증을 지원합니다.\n *\n * @example\n * ```typescript\n * const schema = defineEnvSchema({\n * DATABASE_URL: envUrl({\n * description: 'Database connection',\n * required: true,\n * validator: parsePostgresUrl,\n * sensitive: true,\n * })\n * });\n * ```\n *\n * @module env/schema\n */\n\nimport { parseBoolean, parseNumber, parseJson } from './validator';\n\n/**\n * 환경변수 스키마 정의\n */\nexport interface EnvVarSchema<T = string>\n{\n /** 환경변수 키 */\n key: string;\n\n /** 설명 (목적, 사용처) */\n description: string;\n\n /** 타입 */\n type: 'string' | 'number' | 'boolean' | 'url' | 'enum' | 'json';\n\n /** 필수 여부 */\n required?: boolean;\n\n /** 기본값 */\n default?: T;\n\n /** 검증/변환 함수 */\n validator?: (value: string) => T;\n\n // === 검증 옵션 ===\n\n /** Fallback 환경변수 키들 (backward compatibility) */\n fallbackKeys?: string[];\n\n /** 최소 길이 (문자열 타입) */\n minLength?: number;\n\n // === 메타데이터 ===\n\n /** 민감정보 여부 (로깅 시 마스킹) */\n sensitive?: boolean;\n\n /**\n * 자동 생성 방식 — 값이 미지정이면 프로비저닝(secrets 관리 CLI)이 이 방식으로 생성한다.\n * 외부에서 받아야 하는 시크릿(API 키 등)은 generate를 비워 사용자 입력으로 둔다.\n */\n generate?: 'hex32' | 'hex64' | 'uuid' | 'base64url32';\n\n /** 예시 값들 (타입과 일치해야 함) */\n examples?: T[];\n\n // === 파일 분리 ===\n\n /**\n * Next.js 프로세스에서 사용 여부\n *\n * - true: .env.local에 존재해야 함 (Next.js 서버 컴포넌트에서 접근 가능)\n * - false: .env.server에만 존재해야 함 (SPFN 서버에서만 접근)\n *\n * @default NEXT_PUBLIC_* 이면 true, 아니면 false\n */\n nextjs?: boolean;\n}\n\n/**\n * 스키마 컬렉션 타입\n */\nexport type EnvSchemaCollection = Record<string, EnvVarSchema<any>>;\n\n/**\n * Helper type: Check if field has default value\n */\ntype HasDefault<T> = T extends { default: any } ? true : false;\n\n/**\n * Helper type: Check if field is explicitly required\n */\ntype IsRequired<T> = T extends { required: true } ? true : false;\n\n/**\n * Helper type: Check if field should be required (has default OR required: true)\n */\ntype ShouldBeRequired<T> = HasDefault<T> extends true ? true : IsRequired<T>;\n\n/**\n * 스키마로부터 타입 추출\n *\n * required: true 또는 default가 있는 필드 → 필수\n * required: false 또는 미지정 → optional (| undefined)\n */\nexport type InferEnvType<T extends EnvSchemaCollection> = {\n // Required fields (required: true OR has default)\n [K in keyof T as ShouldBeRequired<T[K]> extends true ? K : never]:\n T[K] extends EnvVarSchema<infer U> ? U : string;\n} & {\n // Optional fields (required: false OR not specified)\n [K in keyof T as ShouldBeRequired<T[K]> extends true ? never : K]?:\n T[K] extends EnvVarSchema<infer U> ? U | undefined : string | undefined;\n};\n\n/**\n * 스키마 정의 헬퍼 (타입 추론 지원)\n *\n * Automatically fills in the `key` property from object keys.\n *\n * @example\n * ```typescript\n * const schema = defineEnvSchema({\n * DATABASE_URL: envString({ description: 'Database URL', required: true })\n * });\n * // Automatically adds key: 'DATABASE_URL'\n * ```\n */\nexport function defineEnvSchema<T extends Record<string, any>>(\n schema: T,\n): { [K in keyof T]: T[K] & { key: K } }\n{\n const result: any = {};\n\n for (const key in schema)\n {\n result[key] = {\n ...schema[key],\n key,\n };\n }\n\n return result;\n}\n\n/**\n * 문자열 스키마 헬퍼\n *\n * @example\n * ```typescript\n * const schema = {\n * API_KEY: {\n * ...envString({\n * description: 'API authentication key',\n * required: true,\n * sensitive: true,\n * }),\n * key: 'API_KEY',\n * }\n * };\n * ```\n */\nexport function envString<T extends Omit<EnvVarSchema, 'key' | 'type'>>(\n options: T,\n): T & { type: 'string'; validator: (value: string) => string }\n{\n return {\n ...options,\n type: 'string',\n validator: options.validator || ((value: string) => value),\n };\n}\n\n/**\n * 시크릿 스키마 헬퍼 — sensitive:true 자동.\n *\n * generate 지정 시 프로비저닝(secrets CLI)이 값을 자동 생성, 미지정이면 외부 입력(사용자가 붙여넣음).\n * 이로써 env.config.ts가 \"설정(envString) vs 외부 시크릿(envSecret) vs 자동 시크릿(envSecret+generate)\"을\n * 선언적으로 가른다 — CLI·프레임워크·프로비저닝이 한 원천을 읽는다.\n *\n * @example\n * ```typescript\n * SPFN_AUTH_SESSION_SECRET: envSecret({ description: '세션 서명 키', generate: 'base64url32' }),\n * OPENAI_API_KEY: envSecret({ description: 'OpenAI API 키' }), // generate 없음 → 외부 입력\n * ```\n */\nexport function envSecret<T extends Omit<EnvVarSchema, 'key' | 'type' | 'sensitive'>>(\n options: T,\n): T & { type: 'string'; sensitive: true; validator: (value: string) => string }\n{\n return {\n ...options,\n type: 'string',\n sensitive: true,\n validator: options.validator || ((value: string) => value),\n };\n}\n\n/**\n * 숫자 스키마 헬퍼\n *\n * @example\n * ```typescript\n * const schema = {\n * PORT: {\n * ...envNumber({\n * description: 'Server port',\n * default: 3000,\n * validator: createNumberParser({ min: 1, max: 65535 }),\n * }),\n * key: 'PORT',\n * }\n * };\n * ```\n */\nexport function envNumber<T extends Omit<EnvVarSchema<number>, 'key' | 'type'>>(\n options: T,\n): T & { type: 'number'; validator: (value: string) => number }\n{\n return {\n ...options,\n type: 'number',\n validator: options.validator || parseNumber,\n };\n}\n\n/**\n * Boolean 스키마 헬퍼\n *\n * @example\n * ```typescript\n * const schema = {\n * DEBUG: {\n * ...envBoolean({\n * description: 'Enable debug mode',\n * default: false,\n * }),\n * key: 'DEBUG',\n * }\n * };\n * ```\n */\nexport function envBoolean<T extends Omit<EnvVarSchema<boolean>, 'key' | 'type'>>(\n options: T,\n): T & { type: 'boolean'; validator: (value: string) => boolean }\n{\n return {\n ...options,\n type: 'boolean',\n validator: options.validator || parseBoolean,\n };\n}\n\n/**\n * URL 스키마 헬퍼\n *\n * @example\n * ```typescript\n * const schema = {\n * DATABASE_URL: {\n * ...envUrl({\n * description: 'Database connection URL',\n * required: true,\n * validator: parsePostgresUrl,\n * }),\n * key: 'DATABASE_URL',\n * }\n * };\n * ```\n */\nexport function envUrl<T extends Omit<EnvVarSchema, 'key' | 'type'>>(\n options: T,\n): T & { type: 'url' }\n{\n return {\n ...options,\n type: 'url',\n };\n}\n\n/**\n * Enum 스키마 헬퍼\n *\n * @example\n * ```typescript\n * const schema = {\n * LOG_LEVEL: {\n * ...envEnum(['debug', 'info', 'warn', 'error'] as const, {\n * description: 'Logging level',\n * default: 'info',\n * }),\n * key: 'LOG_LEVEL',\n * }\n * };\n * ```\n */\nexport function envEnum<\n T extends string,\n O extends Omit<EnvVarSchema<T>, 'key' | 'type' | 'validator'>,\n>(\n allowed: readonly T[],\n options: O,\n): O & { type: 'enum'; validator: (val: string) => T }\n{\n return {\n ...options,\n type: 'enum',\n validator: (val: string): T =>\n {\n if (!allowed.includes(val as T))\n {\n throw new Error(`Must be one of: ${allowed.join(', ')}, got: ${val}`);\n }\n\n return val as T;\n },\n };\n}\n\n/**\n * JSON 스키마 헬퍼\n *\n * @example\n * ```typescript\n * const schema = {\n * CONFIG_JSON: {\n * ...envJson<{ host: string; port: number }>({\n * description: 'JSON configuration object',\n * required: true,\n * }),\n * key: 'CONFIG_JSON',\n * }\n * };\n * ```\n */\nexport function envJson<\n T = any,\n O extends Omit<EnvVarSchema<T>, 'key' | 'type' | 'validator'> = Omit<EnvVarSchema<T>, 'key' | 'type' | 'validator'>,\n>(\n options: O,\n): O & { type: 'json'; validator: (val: string) => T }\n{\n return {\n ...options,\n type: 'json',\n validator: (val: string) => parseJson<T>(val),\n };\n}\n\n/**\n * 환경변수가 클라이언트에서 접근 가능한지 확인\n * (NEXT_PUBLIC_ 접두사로 판단)\n *\n * @param key - 환경변수 키\n * @returns 클라이언트에서 접근 가능하면 true\n *\n * @example\n * ```typescript\n * isClientAccessible('NEXT_PUBLIC_API_URL'); // true\n * isClientAccessible('DATABASE_URL'); // false\n * ```\n */\nexport function isClientAccessible(key: string): boolean\n{\n return key.startsWith('NEXT_PUBLIC_');\n}\n\n/**\n * 환경변수가 서버 전용인지 확인\n * (NEXT_PUBLIC_ 접두사가 없으면 서버 전용)\n *\n * @param key - 환경변수 키\n * @returns 서버 전용이면 true\n *\n * @example\n * ```typescript\n * isServerOnly('DATABASE_URL'); // true\n * isServerOnly('NEXT_PUBLIC_API_URL'); // false\n * ```\n */\nexport function isServerOnly(key: string): boolean\n{\n return !isClientAccessible(key);\n}\n\n/**\n * 스키마의 nextjs 옵션 값 결정\n *\n * 명시적으로 지정되지 않은 경우:\n * - NEXT_PUBLIC_* → true\n * - 그 외 → false\n *\n * @param schema - 환경변수 스키마\n * @returns Next.js 프로세스에서 사용 가능 여부\n */\nexport function isNextjsAccessible(schema: EnvVarSchema): boolean\n{\n if (schema.nextjs !== undefined)\n {\n return schema.nextjs;\n }\n\n return isClientAccessible(schema.key);\n}\n\n/**\n * 스키마가 SPFN 서버 전용인지 확인\n *\n * @param schema - 환경변수 스키마\n * @returns SPFN 서버에서만 사용되면 true\n */\nexport function isSpfnServerOnly(schema: EnvVarSchema): boolean\n{\n return !isNextjsAccessible(schema);\n}\n","/**\n * Environment Variable Registry\n *\n * 환경변수 스키마를 등록하고 타입 안전하게 접근할 수 있는 레지스트리\n *\n * @example\n * ```typescript\n * const schema = defineEnvSchema({\n * DATABASE_URL: envString({ description: 'Database URL', required: true })\n * });\n *\n * const registry = createEnvRegistry(schema);\n * const env = registry.validate(); // 검증 + env 반환\n * console.log(env.DATABASE_URL);\n * ```\n *\n * @module env/registry\n */\nimport type { EnvVarSchema, EnvSchemaCollection, InferEnvType } from './schema';\nimport { isClientAccessible } from './schema';\nimport { logger } from '@spfn/core/logger';\n\nconst envLogger = logger.child('@spfn/core:env-registry');\n\n/**\n * 환경변수 레지스트리\n *\n * 스키마 기반 환경변수 관리 및 검증\n */\nexport class EnvRegistry<T extends EnvSchemaCollection = EnvSchemaCollection>\n{\n private schemas = new Map<string, EnvVarSchema>();\n private hasValidated = false;\n\n constructor(schemas?: T)\n {\n if (schemas)\n {\n this.registerMultiple(schemas);\n }\n }\n\n /**\n * 스키마 등록\n */\n register(schema: EnvVarSchema): void\n {\n this.schemas.set(schema.key, schema);\n }\n\n /**\n * 여러 스키마 등록\n */\n registerMultiple(schemas: EnvSchemaCollection): void\n {\n for (const [key, schema] of Object.entries(schemas))\n {\n this.register({ ...schema, key });\n }\n }\n\n /**\n * 검증 상태 리셋 (테스트용)\n */\n reset(): void\n {\n this.hasValidated = false;\n }\n\n /**\n * 환경변수 원시값 가져오기 (fallback 지원)\n */\n private getRawValue(key: string, fallbackKeys?: string[]): string | undefined\n {\n let value = process.env[key];\n\n if (!value && fallbackKeys)\n {\n for (const fallbackKey of fallbackKeys)\n {\n value = process.env[fallbackKey];\n if (value)\n {\n break;\n }\n }\n }\n\n return value;\n }\n\n /**\n * 값에 validator 적용\n */\n private applyValidator<U>(\n value: string,\n schema: EnvVarSchema<U>,\n ): U\n {\n if (schema.validator)\n {\n return schema.validator(value) as U;\n }\n\n return value as U;\n }\n\n /**\n * 스키마 검증 수행 (값 읽기 없이)\n *\n * @internal\n */\n private validateSchemas(): void\n {\n // Skip if already validated\n if (this.hasValidated)\n {\n return;\n }\n\n const warnings: string[] = [];\n\n // 클라이언트 변수 중 민감정보 경고\n for (const [key, schema] of this.schemas)\n {\n if (isClientAccessible(key) && schema.sensitive)\n {\n warnings.push(\n `${key} is marked as sensitive but accessible from client (NEXT_PUBLIC_*). Remove NEXT_PUBLIC_ prefix or unmark as sensitive.`,\n );\n }\n }\n\n // Log warnings\n if (warnings.length > 0)\n {\n envLogger.warn('Environment validation warnings:');\n warnings.forEach(w => envLogger.warn(` - ${w}`));\n }\n\n this.hasValidated = true;\n }\n\n /**\n * SKIP_ENV_VALIDATION 환경변수 확인\n */\n private shouldSkipValidation(): boolean\n {\n const skip = process.env.SKIP_ENV_VALIDATION;\n\n return skip === 'true' || skip === '1';\n }\n\n /**\n * 실제 접근 시점에 환경변수 값 가져오기 및 검증\n *\n * @internal\n */\n private getAndValidate(key: string): any\n {\n const schema = this.schemas.get(key);\n if (!schema)\n {\n return undefined;\n }\n\n // Get raw value using common helper\n const value = this.getRawValue(key, schema.fallbackKeys);\n\n // Check if required (skip if SKIP_ENV_VALIDATION is set)\n if (schema.required && !value && !this.shouldSkipValidation())\n {\n const fallbackHint = schema.fallbackKeys\n ? ` (or ${schema.fallbackKeys.join(', ')})`\n : '';\n\n const errorMsg = `${key}${fallbackHint} is required but not set. ${schema.description || ''}`;\n envLogger.error(`Environment validation failed:\\n - ${errorMsg}`);\n throw new Error('Environment validation failed');\n }\n\n // If no value and not required, use default\n if (!value)\n {\n return schema.default;\n }\n\n // Check minLength\n if (schema.minLength !== undefined && value.length < schema.minLength)\n {\n const errorMsg = `${key} must be at least ${schema.minLength} characters long (current: ${value.length})`;\n envLogger.error(`Environment validation failed:\\n - ${errorMsg}`);\n throw new Error('Environment validation failed');\n }\n\n // Apply validator\n try\n {\n return this.applyValidator(value, schema);\n }\n catch (error)\n {\n // For sensitive keys, never log the validator's message — it may have\n // interpolated the secret value (e.g. a DB/Redis URL with credentials).\n const detail = schema.sensitive\n ? '(value hidden — sensitive key)'\n : (error instanceof Error ? error.message : String(error));\n const errorMsg = `${key} validation failed: ${detail}`;\n envLogger.error(`Environment validation failed:\\n - ${errorMsg}`);\n throw new Error('Environment validation failed');\n }\n }\n\n /**\n * 모든 환경변수를 명시적으로 검증 (SKIP_ENV_VALIDATION 무시)\n *\n * CLI에서 사용하기 위한 메서드로, 모든 required 환경변수를 강제 검증합니다.\n *\n * @returns 검증 결과 (errors, warnings)\n */\n validateAll(): { errors: Array<{ key: string; message: string }>; warnings: Array<{ key: string; message: string }> }\n {\n const errors: Array<{ key: string; message: string }> = [];\n const warnings: Array<{ key: string; message: string }> = [];\n\n for (const [key, schema] of this.schemas)\n {\n // 클라이언트 변수 중 민감정보 경고\n if (isClientAccessible(key) && schema.sensitive)\n {\n warnings.push({\n key,\n message: `${key} is marked as sensitive but accessible from client (NEXT_PUBLIC_*).`,\n });\n }\n\n const value = this.getRawValue(key, schema.fallbackKeys);\n\n // Check required\n if (schema.required && !value)\n {\n const fallbackHint = schema.fallbackKeys\n ? ` (or ${schema.fallbackKeys.join(', ')})`\n : '';\n errors.push({\n key,\n message: `${key}${fallbackHint} is required but not set. ${schema.description || ''}`,\n });\n continue;\n }\n\n // Check minLength\n if (value && schema.minLength !== undefined && value.length < schema.minLength)\n {\n errors.push({\n key,\n message: `${key} must be at least ${schema.minLength} characters long (current: ${value.length})`,\n });\n continue;\n }\n\n // Check validator\n if (value && schema.validator)\n {\n try\n {\n schema.validator(value);\n }\n catch (error)\n {\n errors.push({\n key,\n message: `${key} validation failed: ${error instanceof Error ? error.message : String(error)}`,\n });\n }\n }\n }\n\n return { errors, warnings };\n }\n\n /**\n * 환경변수 검증 및 타입 안전한 env 객체 반환\n *\n * Proxy 기반으로 구현되어 실제 환경변수 접근 시점에 값을 읽고 검증합니다.\n * 이를 통해 dotenv 로딩 타이밍과 무관하게 최신 환경변수 값을 가져올 수 있습니다.\n *\n * @returns 검증된 환경변수 객체 (Proxy)\n * @throws {Error} 필수 변수 누락 또는 검증 실패 시\n *\n * @example\n * ```typescript\n * const registry = createEnvRegistry(schema);\n * const env = registry.validate(); // 스키마만 검증\n * // ... dotenv 로딩 ...\n * console.log(env.DATABASE_URL); // 이 시점에 실제 값 읽기\n * ```\n */\n validate(): InferEnvType<T>\n {\n // Perform schema-level validation (without reading values)\n this.validateSchemas();\n\n // Return Proxy that lazily reads and validates on access\n return new Proxy({} as InferEnvType<T>, {\n get: (_target, prop: string) =>\n {\n return this.getAndValidate(prop);\n },\n\n ownKeys: () =>\n {\n return Array.from(this.schemas.keys());\n },\n\n getOwnPropertyDescriptor: (_target, prop: string) =>\n {\n if (this.schemas.has(prop))\n {\n return {\n enumerable: true,\n configurable: true,\n get: () => this.getAndValidate(prop),\n };\n }\n\n return undefined;\n },\n\n has: (_target, prop: string) =>\n {\n return this.schemas.has(prop);\n },\n });\n }\n}\n\n/**\n * 환경변수 검증 결과\n */\nexport interface EnvValidationResult\n{\n valid: boolean;\n errors: Array<{ key: string; message: string }>;\n warnings: Array<{ key: string; message: string }>;\n}\n\n/**\n * 레지스트리 생성 헬퍼\n *\n * @example\n * ```typescript\n * const schema = defineEnvSchema({\n * DATABASE_URL: envString({ description: 'Database URL', required: true })\n * });\n *\n * const registry = createEnvRegistry(schema);\n * const env = registry.validate();\n * ```\n */\nexport function createEnvRegistry<T extends EnvSchemaCollection>(\n schemas: T,\n): EnvRegistry<T>\n{\n return new EnvRegistry(schemas);\n}\n\n/**\n * 모든 환경변수를 명시적으로 검증 (SKIP_ENV_VALIDATION 무시)\n *\n * CLI `spfn env validate` 명령어에서 사용\n *\n * @param registries - 검증할 레지스트리 배열\n * @returns 검증 결과\n *\n * @example\n * ```typescript\n * const result = validateAllEnv([coreRegistry, authRegistry]);\n * if (!result.valid) {\n * console.error('Missing env vars:', result.errors);\n * process.exit(1);\n * }\n * ```\n */\nexport function validateAllEnv(\n registries: EnvRegistry<any>[],\n): EnvValidationResult\n{\n const errors: Array<{ key: string; message: string }> = [];\n const warnings: Array<{ key: string; message: string }> = [];\n\n for (const registry of registries)\n {\n const result = registry.validateAll();\n errors.push(...result.errors);\n warnings.push(...result.warnings);\n }\n\n return {\n valid: errors.length === 0,\n errors,\n warnings,\n };\n}\n"]}
1
+ {"version":3,"sources":["../../src/env/validator.ts","../../src/env/schema.ts","../../src/env/registry.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Environment Variable Management - Parsers\n *\n * Parser functions that transform and validate environment variable strings.\n * All parsers follow the pattern: (value: string) => T or throw Error\n *\n * An error message never contains the input. Messages are logged — by the\n * registry and by the server's boot check — and the input may be a secret or a\n * URL carrying credentials. Describe the expected shape instead.\n */\n\n// ============================================================================\n// Core Types\n// ============================================================================\n\n/**\n * Parser function that transforms and validates a string value\n * @throws Error if validation fails\n */\nexport type Parser<T> = (value: string) => T;\n\n// ============================================================================\n// String Parsers\n// ============================================================================\n\n/**\n * Parse a non-empty string\n *\n * @param value - Value to parse\n * @returns Trimmed string\n * @throws Error if string is empty after trimming\n *\n * @example\n * ```typescript\n * const name = getEnvVar('APP_NAME', {\n * validator: parseString,\n * });\n * ```\n */\nexport function parseString(value: string): string\n{\n const trimmed = value.trim();\n\n if (trimmed.length === 0)\n {\n throw new Error('Value cannot be empty');\n }\n\n return trimmed;\n}\n\n/**\n * Create a string parser with validation rules\n *\n * @param options - Validation options\n * @returns Parser function\n *\n * @example\n * ```typescript\n * const apiKey = getEnvVar('API_KEY', {\n * validator: createStringParser({\n * minLength: 32,\n * pattern: /^[A-Za-z0-9_-]+$/,\n * }),\n * });\n * ```\n */\nexport function createStringParser(\n options: {\n minLength?: number;\n maxLength?: number;\n pattern?: RegExp;\n trim?: boolean;\n } = {},\n): Parser<string>\n{\n return (value: string): string =>\n {\n const { minLength, maxLength, pattern, trim = true } = options;\n\n let result = trim ? value.trim() : value;\n\n // Empty check\n if (result.length === 0)\n {\n throw new Error('Value cannot be empty');\n }\n\n // Length validation\n if (minLength !== undefined && result.length < minLength)\n {\n throw new Error(`Must be at least ${minLength} characters long (current: ${result.length})`);\n }\n\n if (maxLength !== undefined && result.length > maxLength)\n {\n throw new Error(`Must be at most ${maxLength} characters long (current: ${result.length})`);\n }\n\n // Pattern validation\n if (pattern && !pattern.test(result))\n {\n throw new Error(`Must match pattern ${pattern}`);\n }\n\n return result;\n };\n}\n\n// ============================================================================\n// Boolean Parser\n// ============================================================================\n\n/**\n * Parse a boolean environment variable\n *\n * Accepts: 'true', '1', 'yes' (case-insensitive) → true\n * 'false', '0', 'no' (case-insensitive) → false\n *\n * @param value - Value to parse\n * @returns Boolean value\n * @throws Error if value is not a valid boolean string\n *\n * @example\n * ```typescript\n * const debug = getEnvVar('DEBUG', {\n * default: 'false',\n * validator: parseBoolean,\n * });\n * ```\n */\nexport function parseBoolean(value: string): boolean\n{\n const normalized = value.toLowerCase().trim();\n\n if (['true', '1', 'yes'].includes(normalized))\n {\n return true;\n }\n\n if (['false', '0', 'no'].includes(normalized))\n {\n return false;\n }\n\n throw new Error('Must be a boolean value (true/false, 1/0, yes/no)');\n}\n\n// ============================================================================\n// Number Parsers\n// ============================================================================\n\n/**\n * Parse and validate number\n *\n * @param value - Value to parse\n * @param options - Validation options\n * @returns Parsed number\n * @throws Error if invalid number or constraint violation\n *\n * @example\n * ```typescript\n * const port = getEnvVar('PORT', {\n * default: '3000',\n * validator: (val) => parseNumber(val, { min: 1, max: 65535, integer: true }),\n * });\n * ```\n */\nexport function parseNumber(\n value: string,\n options: { min?: number; max?: number; integer?: boolean } = {},\n): number\n{\n const { min, max, integer = false } = options;\n\n // Reject empty strings\n if (value.trim() === '')\n {\n throw new Error('Value cannot be empty');\n }\n\n const num = Number(value);\n\n if (isNaN(num))\n {\n throw new Error('Must be a valid number');\n }\n\n if (integer && !Number.isInteger(num))\n {\n throw new Error('Must be an integer');\n }\n\n if (min !== undefined && num < min)\n {\n throw new Error(`Must be at least ${min}`);\n }\n\n if (max !== undefined && num > max)\n {\n throw new Error(`Must be at most ${max}`);\n }\n\n return num;\n}\n\n/**\n * Create a number parser with specific constraints\n *\n * @param options - Validation constraints\n * @returns Parser function\n *\n * @example\n * ```typescript\n * const port = getEnvVar('PORT', {\n * default: '3000',\n * validator: createNumberParser({ min: 1, max: 65535, integer: true }),\n * });\n * ```\n */\nexport function createNumberParser(\n options: { min?: number; max?: number; integer?: boolean } = {},\n): Parser<number>\n{\n return (value: string) => parseNumber(value, options);\n}\n\n/**\n * Parse integer with optional constraints\n *\n * @param value - Value to parse\n * @param options - Min/max constraints\n * @returns Parsed integer\n * @throws Error if invalid or out of range\n *\n * @example\n * ```typescript\n * const retries = getEnvVar('MAX_RETRIES', {\n * default: '3',\n * validator: (val) => parseInteger(val, { min: 1, max: 10 }),\n * });\n * ```\n */\nexport function parseInteger(\n value: string,\n options: { min?: number; max?: number } = {},\n): number\n{\n return parseNumber(value, { ...options, integer: true });\n}\n\n/**\n * Parse float/decimal number with optional constraints\n *\n * @param value - Value to parse\n * @param options - Min/max constraints\n * @returns Parsed decimal number\n * @throws Error if invalid or out of range\n *\n * @example\n * ```typescript\n * const ratio = getEnvVar('CACHE_RATIO', {\n * default: '0.75',\n * validator: (val) => parseDecimal(val, { min: 0, max: 1 }),\n * });\n * ```\n */\nexport function parseDecimal(\n value: string,\n options: { min?: number; max?: number } = {},\n): number\n{\n return parseNumber(value, { ...options, integer: false });\n}\n\n// ============================================================================\n// URL Parsers\n// ============================================================================\n\n/**\n * Parse and validate URL\n *\n * @param value - Value to parse\n * @param options - Validation options\n * @returns Validated URL string\n * @throws Error if invalid URL or protocol mismatch\n *\n * @example\n * ```typescript\n * const apiUrl = getEnvVar('API_URL', {\n * validator: (val) => parseUrl(val, { protocol: 'https' }),\n * });\n * ```\n */\nexport function parseUrl(\n value: string,\n options: { protocol?: 'http' | 'https' | 'any' } = {},\n): string\n{\n const { protocol = 'any' } = options;\n\n // Parse URL (may throw TypeError)\n let url: URL;\n try\n {\n url = new URL(value);\n }\n catch (error)\n {\n if (error instanceof TypeError)\n {\n throw new Error('Invalid URL');\n }\n throw error;\n }\n\n // Validate protocol\n if (protocol === 'http' && url.protocol !== 'http:')\n {\n throw new Error(`URL must use HTTP protocol, got ${url.protocol}`);\n }\n\n if (protocol === 'https' && url.protocol !== 'https:')\n {\n throw new Error(`URL must use HTTPS protocol, got ${url.protocol}`);\n }\n\n return value;\n}\n\n/**\n * Create a URL parser with specific protocol requirement\n *\n * @param protocol - Required protocol ('http', 'https', or 'any')\n * @returns Parser function\n *\n * @example\n * ```typescript\n * const apiUrl = getEnvVar('API_URL', {\n * validator: createUrlParser('https'),\n * });\n * ```\n */\nexport function createUrlParser(protocol: 'http' | 'https' | 'any' = 'any'): Parser<string>\n{\n return (value: string) => parseUrl(value, { protocol });\n}\n\n/**\n * Parse PostgreSQL connection string\n *\n * @param value - Value to parse\n * @returns Validated PostgreSQL URL string\n * @throws Error if invalid PostgreSQL URL\n *\n * @example\n * ```typescript\n * const dbUrl = getEnvVar('DATABASE_URL', {\n * required: true,\n * validator: parsePostgresUrl,\n * });\n * ```\n */\nexport function parsePostgresUrl(value: string): string\n{\n // Parse URL (may throw TypeError)\n let url: URL;\n try\n {\n url = new URL(value);\n }\n catch (error)\n {\n if (error instanceof TypeError)\n {\n // Never echo the value — these URLs carry embedded credentials\n throw new Error('Invalid PostgreSQL URL');\n }\n throw error;\n }\n\n // Validate protocol\n if (url.protocol !== 'postgres:' && url.protocol !== 'postgresql:')\n {\n throw new Error(\n `Must be a PostgreSQL URL (postgres:// or postgresql://), got ${url.protocol}`,\n );\n }\n\n return value;\n}\n\n/**\n * Parse Redis connection string\n *\n * @param value - Value to parse\n * @returns Validated Redis URL string\n * @throws Error if invalid Redis URL\n *\n * @example\n * ```typescript\n * const redisUrl = getEnvVar('REDIS_URL', {\n * required: true,\n * validator: parseRedisUrl,\n * });\n * ```\n */\nexport function parseRedisUrl(value: string): string\n{\n // Parse URL (may throw TypeError)\n let url: URL;\n try\n {\n url = new URL(value);\n }\n catch (error)\n {\n if (error instanceof TypeError)\n {\n // Never echo the value — these URLs carry embedded credentials\n throw new Error('Invalid Redis URL');\n }\n throw error;\n }\n\n // Validate protocol\n if (url.protocol !== 'redis:' && url.protocol !== 'rediss:')\n {\n throw new Error(\n `Must be a Redis URL (redis:// or rediss://), got ${url.protocol}`,\n );\n }\n\n return value;\n}\n\n// ============================================================================\n// Enum Parser\n// ============================================================================\n\n/**\n * Parse and validate enum value\n *\n * @param value - Value to parse\n * @param allowed - Array of allowed values\n * @param caseInsensitive - Whether to perform case-insensitive comparison\n * @returns Validated enum value\n * @throws Error if value not in allowed list\n *\n * @example\n * ```typescript\n * const env = getEnvVar('NODE_ENV', {\n * validator: (val) => parseEnum(val, ['development', 'production', 'test']),\n * });\n * ```\n */\nexport function parseEnum(\n value: string,\n allowed: string[],\n caseInsensitive = false,\n): string\n{\n if (caseInsensitive)\n {\n const normalizedValue = value.toLowerCase();\n const normalizedAllowed = allowed.map((v) => v.toLowerCase());\n const index = normalizedAllowed.indexOf(normalizedValue);\n\n if (index === -1)\n {\n throw new Error(\n `Must be one of [${allowed.join(', ')}]`,\n );\n }\n\n return allowed[index]; // Return original case from allowed list\n }\n\n if (!allowed.includes(value))\n {\n throw new Error(\n `Must be one of [${allowed.join(', ')}]`,\n );\n }\n\n return value;\n}\n\n/**\n * Create an enum parser with specific allowed values\n *\n * @param allowed - Array of allowed values\n * @param caseInsensitive - Whether to perform case-insensitive comparison\n * @returns Parser function\n *\n * @example\n * ```typescript\n * const logLevel = getEnvVar('LOG_LEVEL', {\n * default: 'info',\n * validator: createEnumParser(['debug', 'info', 'warn', 'error']),\n * });\n * ```\n */\nexport function createEnumParser(allowed: string[], caseInsensitive = false): Parser<string>\n{\n return (value: string) => parseEnum(value, allowed, caseInsensitive);\n}\n\n// ============================================================================\n// JSON Parser\n// ============================================================================\n\n/**\n * Parse JSON string\n *\n * @param value - JSON string to parse\n * @returns Parsed JSON value\n * @throws Error if invalid JSON\n *\n * @example\n * ```typescript\n * const config = getEnvVar('CONFIG_JSON', {\n * validator: parseJson,\n * });\n * ```\n */\nexport function parseJson<T = any>(value: string): T\n{\n try\n {\n return JSON.parse(value) as T;\n }\n catch\n {\n // JSON.parse quotes the input in its message; the input may be a secret.\n throw new Error('Invalid JSON');\n }\n}\n\n/**\n * Create a typed JSON parser\n *\n * @returns Parser function\n *\n * @example\n * ```typescript\n * interface Config {\n * host: string;\n * port: number;\n * }\n *\n * const config = getEnvVar('CONFIG_JSON', {\n * validator: createJsonParser<Config>(),\n * });\n * ```\n */\nexport function createJsonParser<T>(): Parser<T>\n{\n return (value: string) => parseJson<T>(value);\n}\n\n// ============================================================================\n// Array Parser\n// ============================================================================\n\n/**\n * Parse comma-separated values into array\n *\n * @param value - Comma-separated string\n * @param options - Parser options\n * @returns Array of strings\n *\n * @example\n * ```typescript\n * const hosts = getEnvVar('ALLOWED_HOSTS', {\n * validator: parseArray,\n * });\n * // \"localhost,example.com,api.example.com\" → ['localhost', 'example.com', 'api.example.com']\n * ```\n */\nexport function parseArray(\n value: string,\n options: {\n separator?: string;\n trim?: boolean;\n filter?: (item: string) => boolean;\n } = {},\n): string[]\n{\n const { separator = ',', trim = true, filter } = options;\n\n if (value.trim() === '')\n {\n return [];\n }\n\n let items = value.split(separator);\n\n if (trim)\n {\n items = items.map((item) => item.trim());\n }\n\n if (filter)\n {\n items = items.filter(filter);\n }\n\n return items;\n}\n\n/**\n * Create an array parser with item parser\n *\n * @param itemParser - Parser to apply to each array item\n * @param options - Array parsing options\n * @returns Parser function\n *\n * @example\n * ```typescript\n * // Parse comma-separated ports\n * const ports = getEnvVar('PORTS', {\n * validator: createArrayParser(\n * createNumberParser({ min: 1, max: 65535, integer: true })\n * ),\n * });\n * // \"3000,4000,5000\" → [3000, 4000, 5000]\n * ```\n */\nexport function createArrayParser<T>(\n itemParser: Parser<T>,\n options: { separator?: string } = {},\n): Parser<T[]>\n{\n return (value: string): T[] =>\n {\n const items = parseArray(value, options);\n\n return items.map((item, index) =>\n {\n try\n {\n return itemParser(item);\n }\n catch (error)\n {\n const message = error instanceof Error ? error.message : String(error);\n throw new Error(`Invalid item at index ${index}: ${message}`);\n }\n });\n };\n}\n\n// ============================================================================\n// Secure Secret Parser\n// ============================================================================\n\n/**\n * Calculate Shannon entropy of a string\n * Returns entropy in bits per character\n *\n * @param str - String to calculate entropy for\n * @returns Entropy value (0 to ~6.6 bits for printable ASCII)\n *\n * @example\n * ```typescript\n * const entropy = calculateEntropy('my-secret-key');\n * // Higher entropy = more random\n * // - Random lowercase: ~4.7 bits/char\n * // - Random alphanumeric: ~5.2 bits/char\n * // - Random printable ASCII: ~6.6 bits/char\n * // - \"aaaaaaa...\": ~0 bits/char\n * ```\n */\nfunction calculateEntropy(str: string): number\n{\n const len = str.length;\n const frequencies = new Map<string, number>();\n\n // Count character frequencies\n for (const char of str)\n {\n frequencies.set(char, (frequencies.get(char) || 0) + 1);\n }\n\n // Calculate Shannon entropy\n let entropy = 0;\n for (const count of frequencies.values())\n {\n const probability = count / len;\n entropy -= probability * Math.log2(probability);\n }\n\n return entropy;\n}\n\n/**\n * Create a secure secret parser with entropy validation\n *\n * Validates cryptographic secrets for sufficient length, character diversity, and randomness.\n * Uses Shannon entropy to measure randomness quality.\n *\n * @param options - Validation options\n * @returns Parser function\n *\n * @example\n * ```typescript\n * const sessionSecret = getEnvVar('SESSION_SECRET', {\n * validator: createSecureSecretParser({\n * minLength: 32, // Minimum 256-bit\n * minUniqueChars: 16, // Character diversity\n * minEntropy: 3.5, // Shannon entropy (bits/char)\n * }),\n * });\n * ```\n */\nexport function createSecureSecretParser(\n options: {\n minLength?: number;\n minUniqueChars?: number;\n minEntropy?: number;\n } = {},\n): Parser<string>\n{\n const {\n minLength = 32,\n minUniqueChars = 16,\n minEntropy = 3.5,\n } = options;\n\n return (value: string): string =>\n {\n const length = value.length;\n const uniqueChars = new Set(value).size;\n const entropy = calculateEntropy(value);\n\n // Check length (minimum for cryptographic strength)\n if (length < minLength)\n {\n throw new Error(\n `Secret too short: ${length} characters (minimum: ${minLength})`,\n );\n }\n\n // Check unique character diversity\n if (uniqueChars < minUniqueChars)\n {\n throw new Error(\n `Secret has low diversity: ${uniqueChars} unique characters (minimum: ${minUniqueChars})`,\n );\n }\n\n // Check Shannon entropy (randomness quality)\n // Reference values:\n // - Random lowercase: ~4.7 bits/char\n // - Random alphanumeric: ~5.2 bits/char\n // - Random printable ASCII: ~6.6 bits/char\n // - \"aaaaaaa...\": ~0 bits/char\n // - \"abcabcabc...\": ~1.58 bits/char\n if (entropy < minEntropy)\n {\n throw new Error(\n `Secret has low entropy: ${entropy.toFixed(2)} bits/char (minimum: ${minEntropy}). Use a more random secret.`,\n );\n }\n\n return value;\n };\n}\n\n// ============================================================================\n// Password Parser\n// ============================================================================\n\n/**\n * Create a password strength parser\n *\n * Validates password strength based on configurable requirements.\n * Useful for enforcing password policies in environment variables or user input.\n *\n * @param options - Validation options\n * @returns Parser function\n *\n * @example\n * ```typescript\n * const adminPassword = getEnvVar('ADMIN_PASSWORD', {\n * validator: createPasswordParser({\n * minLength: 12,\n * requireUppercase: true,\n * requireLowercase: true,\n * requireNumber: true,\n * requireSpecial: true,\n * }),\n * });\n * ```\n */\nexport function createPasswordParser(\n options: {\n minLength?: number;\n requireUppercase?: boolean;\n requireLowercase?: boolean;\n requireNumber?: boolean;\n requireSpecial?: boolean;\n } = {},\n): Parser<string>\n{\n const {\n minLength = 8,\n requireUppercase = true,\n requireLowercase = true,\n requireNumber = true,\n requireSpecial = true,\n } = options;\n\n return (value: string): string =>\n {\n const errors: string[] = [];\n\n // Length check\n if (value.length < minLength)\n {\n errors.push(`Must be at least ${minLength} characters`);\n }\n\n // Uppercase check\n if (requireUppercase && !/[A-Z]/.test(value))\n {\n errors.push('Must contain at least one uppercase letter');\n }\n\n // Lowercase check\n if (requireLowercase && !/[a-z]/.test(value))\n {\n errors.push('Must contain at least one lowercase letter');\n }\n\n // Number check\n if (requireNumber && !/[0-9]/.test(value))\n {\n errors.push('Must contain at least one number');\n }\n\n // Special character check\n if (requireSpecial && !/[^A-Za-z0-9]/.test(value))\n {\n errors.push('Must contain at least one special character');\n }\n\n if (errors.length > 0)\n {\n throw new Error(`Password validation failed: ${errors.join(', ')}`);\n }\n\n return value;\n };\n}\n\n// ============================================================================\n// Parser Composition\n// ============================================================================\n\n/**\n * Chain multiple parsers sequentially\n *\n * Each parser receives the output of the previous parser.\n * Useful for multi-step validation/transformation.\n *\n * @param parsers - Array of parser functions\n * @returns Combined parser function\n *\n * @example\n * ```typescript\n * const apiKey = getEnvVar('API_KEY', {\n * validator: chain(\n * parseString,\n * createStringParser({ minLength: 32, pattern: /^[A-Za-z0-9_-]+$/ }),\n * ),\n * });\n * ```\n */\nexport function chain<T>(...parsers: Array<Parser<T>>): Parser<T>\n{\n return (value: string): T =>\n {\n let result = value as any;\n\n for (const parser of parsers)\n {\n result = parser(result);\n }\n\n return result;\n };\n}\n\n/**\n * Apply parser with fallback value\n *\n * If parser throws, returns fallback instead.\n * Useful for optional environment variables with complex parsing.\n *\n * @param parser - Parser to attempt\n * @param fallback - Fallback value if parsing fails\n * @returns Parser function\n *\n * @example\n * ```typescript\n * const config = getEnvVar('CONFIG_JSON', {\n * validator: withFallback(parseJson, { host: 'localhost', port: 3000 }),\n * });\n * ```\n */\nexport function withFallback<T>(parser: Parser<T>, fallback: T): Parser<T>\n{\n return (value: string): T =>\n {\n try\n {\n return parser(value);\n }\n catch\n {\n return fallback;\n }\n };\n}\n\n/**\n * Make parser optional\n *\n * Returns undefined for empty strings instead of throwing.\n *\n * @param parser - Parser to make optional\n * @returns Parser function that returns T | undefined\n *\n * @example\n * ```typescript\n * const redisUrl = getEnvVar('REDIS_URL', {\n * validator: optional(parseRedisUrl),\n * });\n * // Empty string → undefined\n * // Valid URL → parsed URL\n * // Invalid URL → throws\n * ```\n */\nexport function optional<T>(parser: Parser<T>): Parser<T | undefined>\n{\n return (value: string): T | undefined =>\n {\n if (value.trim() === '')\n {\n return undefined;\n }\n\n return parser(value);\n };\n}\n","/**\n * Environment Variable Schema Definition System\n *\n * 환경변수에 메타데이터를 정의하여 중앙 관리, 문서화, 검증을 지원합니다.\n *\n * @example\n * ```typescript\n * const schema = defineEnvSchema({\n * DATABASE_URL: envUrl({\n * description: 'Database connection',\n * required: true,\n * validator: parsePostgresUrl,\n * sensitive: true,\n * })\n * });\n * ```\n *\n * @module env/schema\n */\n\nimport { parseBoolean, parseNumber, parseJson } from './validator';\n\n/**\n * 환경변수 스키마 정의\n */\nexport interface EnvVarSchema<T = string>\n{\n /** 환경변수 키 */\n key: string;\n\n /** 설명 (목적, 사용처) */\n description: string;\n\n /** 타입 */\n type: 'string' | 'number' | 'boolean' | 'url' | 'enum' | 'json';\n\n /** 필수 여부 */\n required?: boolean;\n\n /** 기본값 */\n default?: T;\n\n /** 검증/변환 함수 */\n validator?: (value: string) => T;\n\n // === 검증 옵션 ===\n\n /** Fallback 환경변수 키들 (backward compatibility) */\n fallbackKeys?: string[];\n\n /** 최소 길이 (문자열 타입) */\n minLength?: number;\n\n // === 메타데이터 ===\n\n /** 민감정보 여부 (로깅 시 마스킹) */\n sensitive?: boolean;\n\n /**\n * 자동 생성 방식 — 값이 미지정이면 프로비저닝(secrets 관리 CLI)이 이 방식으로 생성한다.\n * 외부에서 받아야 하는 시크릿(API 키 등)은 generate를 비워 사용자 입력으로 둔다.\n */\n generate?: 'hex32' | 'hex64' | 'uuid' | 'base64url32';\n\n /** 예시 값들 (타입과 일치해야 함) */\n examples?: T[];\n\n // === 파일 분리 ===\n\n /**\n * Next.js 프로세스에서 사용 여부\n *\n * - true: .env.local에 존재해야 함 (Next.js 서버 컴포넌트에서 접근 가능)\n * - false: .env.server에만 존재해야 함 (SPFN 서버에서만 접근)\n *\n * @default NEXT_PUBLIC_* 이면 true, 아니면 false\n */\n nextjs?: boolean;\n\n // === Deployment layer ===\n\n /**\n * Which layer of a deployment must supply the value.\n *\n * - `'environment'`: one value shared by every instance of an environment\n * (a third-party API key) — kept in `secrets/<env>.enc.json`.\n * - `'instance'`: a value that differs per instance — kept in\n * `secrets/<env>.<instance>.enc.json` or computed by the deploy script\n * and passed to `spfn secret export --with <file>`.\n *\n * Unset: the value may come from any layer. Set it to pin where a value\n * must live — `spfn secret export` then fails when the value arrives from\n * another layer. `--all` on `spfn secret set`/`generate`/`rotate` gives an\n * unset entry to the environment target only.\n */\n layer?: EnvLayer;\n\n // === Audit ===\n\n /**\n * Files that read this variable under a computed key, as paths relative to\n * the project root — e.g. `['src/server/config/providers.ts']`.\n *\n * `spfn env audit` counts a name as read when it appears as a property\n * access or a string literal; a name only ever reached through\n * `env[name]` needs this instead. The name passes when every listed file\n * exists and is scanned. The runtime ignores `readBy`.\n */\n readBy?: string[];\n}\n\n/**\n * The layer of a deployment an env value comes from — see {@link EnvVarSchema.layer}.\n */\nexport type EnvLayer = 'environment' | 'instance';\n\n/**\n * 스키마 컬렉션 타입\n */\nexport type EnvSchemaCollection = Record<string, EnvVarSchema<any>>;\n\n/**\n * Helper type: Check if field has default value\n */\ntype HasDefault<T> = T extends { default: any } ? true : false;\n\n/**\n * Helper type: Check if field is explicitly required\n */\ntype IsRequired<T> = T extends { required: true } ? true : false;\n\n/**\n * Helper type: Check if field should be required (has default OR required: true)\n */\ntype ShouldBeRequired<T> = HasDefault<T> extends true ? true : IsRequired<T>;\n\n/**\n * 스키마로부터 타입 추출\n *\n * required: true 또는 default가 있는 필드 → 필수\n * required: false 또는 미지정 → optional (| undefined)\n */\nexport type InferEnvType<T extends EnvSchemaCollection> = {\n // Required fields (required: true OR has default)\n [K in keyof T as ShouldBeRequired<T[K]> extends true ? K : never]:\n T[K] extends EnvVarSchema<infer U> ? U : string;\n} & {\n // Optional fields (required: false OR not specified)\n [K in keyof T as ShouldBeRequired<T[K]> extends true ? never : K]?:\n T[K] extends EnvVarSchema<infer U> ? U | undefined : string | undefined;\n};\n\n/**\n * 스키마 정의 헬퍼 (타입 추론 지원)\n *\n * Automatically fills in the `key` property from object keys. Every other\n * field passes through as written — an entry without `layer` may come from\n * any deployment layer.\n *\n * @example\n * ```typescript\n * const schema = defineEnvSchema({\n * DATABASE_URL: envString({ description: 'Database URL', required: true })\n * });\n * // Automatically adds key: 'DATABASE_URL'\n * ```\n */\nexport function defineEnvSchema<T extends Record<string, any>>(\n schema: T,\n): { [K in keyof T]: T[K] & { key: K } }\n{\n const result: any = {};\n\n for (const key in schema)\n {\n result[key] = {\n ...schema[key],\n key,\n };\n }\n\n return result;\n}\n\n/**\n * 문자열 스키마 헬퍼\n *\n * @example\n * ```typescript\n * const schema = {\n * API_KEY: {\n * ...envString({\n * description: 'API authentication key',\n * required: true,\n * sensitive: true,\n * }),\n * key: 'API_KEY',\n * }\n * };\n * ```\n */\nexport function envString<T extends Omit<EnvVarSchema, 'key' | 'type'>>(\n options: T,\n): T & { type: 'string'; validator: (value: string) => string }\n{\n return {\n ...options,\n type: 'string',\n validator: options.validator || ((value: string) => value),\n };\n}\n\n/**\n * 시크릿 스키마 헬퍼 — sensitive:true 자동.\n *\n * generate 지정 시 프로비저닝(secrets CLI)이 값을 자동 생성, 미지정이면 외부 입력(사용자가 붙여넣음).\n * 이로써 env.config.ts가 \"설정(envString) vs 외부 시크릿(envSecret) vs 자동 시크릿(envSecret+generate)\"을\n * 선언적으로 가른다 — CLI·프레임워크·프로비저닝이 한 원천을 읽는다.\n *\n * @example\n * ```typescript\n * SPFN_AUTH_SESSION_SECRET: envSecret({ description: '세션 서명 키', generate: 'base64url32' }),\n * OPENAI_API_KEY: envSecret({ description: 'OpenAI API 키' }), // generate 없음 → 외부 입력\n * ```\n */\nexport function envSecret<T extends Omit<EnvVarSchema, 'key' | 'type' | 'sensitive'>>(\n options: T,\n): T & { type: 'string'; sensitive: true; validator: (value: string) => string }\n{\n return {\n ...options,\n type: 'string',\n sensitive: true,\n validator: options.validator || ((value: string) => value),\n };\n}\n\n/**\n * 숫자 스키마 헬퍼\n *\n * @example\n * ```typescript\n * const schema = {\n * PORT: {\n * ...envNumber({\n * description: 'Server port',\n * default: 3000,\n * validator: createNumberParser({ min: 1, max: 65535 }),\n * }),\n * key: 'PORT',\n * }\n * };\n * ```\n */\nexport function envNumber<T extends Omit<EnvVarSchema<number>, 'key' | 'type'>>(\n options: T,\n): T & { type: 'number'; validator: (value: string) => number }\n{\n return {\n ...options,\n type: 'number',\n validator: options.validator || parseNumber,\n };\n}\n\n/**\n * Boolean 스키마 헬퍼\n *\n * @example\n * ```typescript\n * const schema = {\n * DEBUG: {\n * ...envBoolean({\n * description: 'Enable debug mode',\n * default: false,\n * }),\n * key: 'DEBUG',\n * }\n * };\n * ```\n */\nexport function envBoolean<T extends Omit<EnvVarSchema<boolean>, 'key' | 'type'>>(\n options: T,\n): T & { type: 'boolean'; validator: (value: string) => boolean }\n{\n return {\n ...options,\n type: 'boolean',\n validator: options.validator || parseBoolean,\n };\n}\n\n/**\n * URL 스키마 헬퍼\n *\n * @example\n * ```typescript\n * const schema = {\n * DATABASE_URL: {\n * ...envUrl({\n * description: 'Database connection URL',\n * required: true,\n * validator: parsePostgresUrl,\n * }),\n * key: 'DATABASE_URL',\n * }\n * };\n * ```\n */\nexport function envUrl<T extends Omit<EnvVarSchema, 'key' | 'type'>>(\n options: T,\n): T & { type: 'url' }\n{\n return {\n ...options,\n type: 'url',\n };\n}\n\n/**\n * Enum 스키마 헬퍼\n *\n * @example\n * ```typescript\n * const schema = {\n * LOG_LEVEL: {\n * ...envEnum(['debug', 'info', 'warn', 'error'] as const, {\n * description: 'Logging level',\n * default: 'info',\n * }),\n * key: 'LOG_LEVEL',\n * }\n * };\n * ```\n */\nexport function envEnum<\n T extends string,\n O extends Omit<EnvVarSchema<T>, 'key' | 'type' | 'validator'>,\n>(\n allowed: readonly T[],\n options: O,\n): O & { type: 'enum'; validator: (val: string) => T }\n{\n return {\n ...options,\n type: 'enum',\n validator: (val: string): T =>\n {\n if (!allowed.includes(val as T))\n {\n throw new Error(`Must be one of: ${allowed.join(', ')}`);\n }\n\n return val as T;\n },\n };\n}\n\n/**\n * JSON 스키마 헬퍼\n *\n * @example\n * ```typescript\n * const schema = {\n * CONFIG_JSON: {\n * ...envJson<{ host: string; port: number }>({\n * description: 'JSON configuration object',\n * required: true,\n * }),\n * key: 'CONFIG_JSON',\n * }\n * };\n * ```\n */\nexport function envJson<\n T = any,\n O extends Omit<EnvVarSchema<T>, 'key' | 'type' | 'validator'> = Omit<EnvVarSchema<T>, 'key' | 'type' | 'validator'>,\n>(\n options: O,\n): O & { type: 'json'; validator: (val: string) => T }\n{\n return {\n ...options,\n type: 'json',\n validator: (val: string) => parseJson<T>(val),\n };\n}\n\n/**\n * 환경변수가 클라이언트에서 접근 가능한지 확인\n * (NEXT_PUBLIC_ 접두사로 판단)\n *\n * @param key - 환경변수 키\n * @returns 클라이언트에서 접근 가능하면 true\n *\n * @example\n * ```typescript\n * isClientAccessible('NEXT_PUBLIC_API_URL'); // true\n * isClientAccessible('DATABASE_URL'); // false\n * ```\n */\nexport function isClientAccessible(key: string): boolean\n{\n return key.startsWith('NEXT_PUBLIC_');\n}\n\n/**\n * 환경변수가 서버 전용인지 확인\n * (NEXT_PUBLIC_ 접두사가 없으면 서버 전용)\n *\n * @param key - 환경변수 키\n * @returns 서버 전용이면 true\n *\n * @example\n * ```typescript\n * isServerOnly('DATABASE_URL'); // true\n * isServerOnly('NEXT_PUBLIC_API_URL'); // false\n * ```\n */\nexport function isServerOnly(key: string): boolean\n{\n return !isClientAccessible(key);\n}\n\n/**\n * 스키마의 nextjs 옵션 값 결정\n *\n * 명시적으로 지정되지 않은 경우:\n * - NEXT_PUBLIC_* → true\n * - 그 외 → false\n *\n * @param schema - 환경변수 스키마\n * @returns Next.js 프로세스에서 사용 가능 여부\n */\nexport function isNextjsAccessible(schema: EnvVarSchema): boolean\n{\n if (schema.nextjs !== undefined)\n {\n return schema.nextjs;\n }\n\n return isClientAccessible(schema.key);\n}\n\n/**\n * 스키마가 SPFN 서버 전용인지 확인\n *\n * @param schema - 환경변수 스키마\n * @returns SPFN 서버에서만 사용되면 true\n */\nexport function isSpfnServerOnly(schema: EnvVarSchema): boolean\n{\n return !isNextjsAccessible(schema);\n}\n","/**\n * Environment Variable Registry\n *\n * 환경변수 스키마를 등록하고 타입 안전하게 접근할 수 있는 레지스트리\n *\n * @example\n * ```typescript\n * const schema = defineEnvSchema({\n * DATABASE_URL: envString({ description: 'Database URL', required: true })\n * });\n *\n * const registry = createEnvRegistry(schema);\n * const env = registry.validate(); // 검증 + env 반환\n * console.log(env.DATABASE_URL);\n * ```\n *\n * @module env/registry\n */\nimport type { EnvVarSchema, EnvSchemaCollection, InferEnvType } from './schema';\nimport { isClientAccessible } from './schema';\nimport { logger } from '@spfn/core/logger';\n\nconst envLogger = logger.child('@spfn/core:env-registry');\n\n/**\n * 환경변수 레지스트리\n *\n * 스키마 기반 환경변수 관리 및 검증\n */\nexport class EnvRegistry<T extends EnvSchemaCollection = EnvSchemaCollection>\n{\n private schemas = new Map<string, EnvVarSchema>();\n private hasValidated = false;\n\n constructor(schemas?: T)\n {\n if (schemas)\n {\n this.registerMultiple(schemas);\n }\n }\n\n /**\n * 스키마 등록\n */\n register(schema: EnvVarSchema): void\n {\n this.schemas.set(schema.key, schema);\n }\n\n /**\n * 여러 스키마 등록\n */\n registerMultiple(schemas: EnvSchemaCollection): void\n {\n for (const [key, schema] of Object.entries(schemas))\n {\n this.register({ ...schema, key });\n }\n }\n\n /**\n * 검증 상태 리셋 (테스트용)\n */\n reset(): void\n {\n this.hasValidated = false;\n }\n\n /**\n * 환경변수 원시값 가져오기 (fallback 지원)\n */\n private getRawValue(key: string, fallbackKeys?: string[]): string | undefined\n {\n let value = process.env[key];\n\n if (!value && fallbackKeys)\n {\n for (const fallbackKey of fallbackKeys)\n {\n value = process.env[fallbackKey];\n if (value)\n {\n break;\n }\n }\n }\n\n return value;\n }\n\n /**\n * 값에 validator 적용\n */\n private applyValidator<U>(\n value: string,\n schema: EnvVarSchema<U>,\n ): U\n {\n if (schema.validator)\n {\n return schema.validator(value) as U;\n }\n\n return value as U;\n }\n\n /**\n * 스키마 검증 수행 (값 읽기 없이)\n *\n * @internal\n */\n private validateSchemas(): void\n {\n // Skip if already validated\n if (this.hasValidated)\n {\n return;\n }\n\n const warnings: string[] = [];\n\n // 클라이언트 변수 중 민감정보 경고\n for (const [key, schema] of this.schemas)\n {\n if (isClientAccessible(key) && schema.sensitive)\n {\n warnings.push(\n `${key} is marked as sensitive but accessible from client (NEXT_PUBLIC_*). Remove NEXT_PUBLIC_ prefix or unmark as sensitive.`,\n );\n }\n }\n\n // Log warnings\n if (warnings.length > 0)\n {\n envLogger.warn('Environment validation warnings:');\n warnings.forEach(w => envLogger.warn(` - ${w}`));\n }\n\n this.hasValidated = true;\n }\n\n /**\n * SKIP_ENV_VALIDATION 환경변수 확인\n */\n private shouldSkipValidation(): boolean\n {\n const skip = process.env.SKIP_ENV_VALIDATION;\n\n return skip === 'true' || skip === '1';\n }\n\n /**\n * 실제 접근 시점에 환경변수 값 가져오기 및 검증\n *\n * @internal\n */\n private getAndValidate(key: string): any\n {\n const schema = this.schemas.get(key);\n if (!schema)\n {\n return undefined;\n }\n\n // Get raw value using common helper\n const value = this.getRawValue(key, schema.fallbackKeys);\n\n // Check if required (skip if SKIP_ENV_VALIDATION is set)\n if (schema.required && !value && !this.shouldSkipValidation())\n {\n const fallbackHint = schema.fallbackKeys\n ? ` (or ${schema.fallbackKeys.join(', ')})`\n : '';\n\n const errorMsg = `${key}${fallbackHint} is required but not set. ${schema.description || ''}`;\n envLogger.error(`Environment validation failed:\\n - ${errorMsg}`);\n throw new Error('Environment validation failed');\n }\n\n // If no value and not required, use default\n if (!value)\n {\n return schema.default;\n }\n\n // Check minLength\n if (schema.minLength !== undefined && value.length < schema.minLength)\n {\n const errorMsg = `${key} must be at least ${schema.minLength} characters long (current: ${value.length})`;\n envLogger.error(`Environment validation failed:\\n - ${errorMsg}`);\n throw new Error('Environment validation failed');\n }\n\n // Apply validator\n try\n {\n return this.applyValidator(value, schema);\n }\n catch (error)\n {\n // For sensitive keys, never log the validator's message — it may have\n // interpolated the secret value (e.g. a DB/Redis URL with credentials).\n const detail = schema.sensitive\n ? '(value hidden — sensitive key)'\n : (error instanceof Error ? error.message : String(error));\n const errorMsg = `${key} validation failed: ${detail}`;\n envLogger.error(`Environment validation failed:\\n - ${errorMsg}`);\n throw new Error('Environment validation failed');\n }\n }\n\n /**\n * 모든 환경변수를 명시적으로 검증 (SKIP_ENV_VALIDATION 무시)\n *\n * CLI에서 사용하기 위한 메서드로, 모든 required 환경변수를 강제 검증합니다.\n *\n * @returns 검증 결과 (errors, warnings)\n */\n validateAll(): { errors: Array<{ key: string; message: string }>; warnings: Array<{ key: string; message: string }> }\n {\n const errors: Array<{ key: string; message: string }> = [];\n const warnings: Array<{ key: string; message: string }> = [];\n\n for (const [key, schema] of this.schemas)\n {\n // 클라이언트 변수 중 민감정보 경고\n if (isClientAccessible(key) && schema.sensitive)\n {\n warnings.push({\n key,\n message: `${key} is marked as sensitive but accessible from client (NEXT_PUBLIC_*).`,\n });\n }\n\n const value = this.getRawValue(key, schema.fallbackKeys);\n\n // Check required\n if (schema.required && !value)\n {\n const fallbackHint = schema.fallbackKeys\n ? ` (or ${schema.fallbackKeys.join(', ')})`\n : '';\n errors.push({\n key,\n message: `${key}${fallbackHint} is required but not set. ${schema.description || ''}`,\n });\n continue;\n }\n\n // Check minLength\n if (value && schema.minLength !== undefined && value.length < schema.minLength)\n {\n errors.push({\n key,\n message: `${key} must be at least ${schema.minLength} characters long (current: ${value.length})`,\n });\n continue;\n }\n\n // Check validator\n if (value && schema.validator)\n {\n try\n {\n schema.validator(value);\n }\n catch (error)\n {\n // Same rule as the lazy path: a sensitive key's validator\n // message may have interpolated the value, so it is dropped.\n const detail = schema.sensitive\n ? '(value hidden — sensitive key)'\n : (error instanceof Error ? error.message : String(error));\n\n errors.push({\n key,\n message: `${key} validation failed: ${detail}`,\n });\n }\n }\n }\n\n return { errors, warnings };\n }\n\n /**\n * 환경변수 검증 및 타입 안전한 env 객체 반환\n *\n * Proxy 기반으로 구현되어 실제 환경변수 접근 시점에 값을 읽고 검증합니다.\n * 이를 통해 dotenv 로딩 타이밍과 무관하게 최신 환경변수 값을 가져올 수 있습니다.\n *\n * @returns 검증된 환경변수 객체 (Proxy)\n * @throws {Error} 필수 변수 누락 또는 검증 실패 시\n *\n * @example\n * ```typescript\n * const registry = createEnvRegistry(schema);\n * const env = registry.validate(); // 스키마만 검증\n * // ... dotenv 로딩 ...\n * console.log(env.DATABASE_URL); // 이 시점에 실제 값 읽기\n * ```\n */\n validate(): InferEnvType<T>\n {\n // Perform schema-level validation (without reading values)\n this.validateSchemas();\n\n // Return Proxy that lazily reads and validates on access\n return new Proxy({} as InferEnvType<T>, {\n get: (_target, prop: string) =>\n {\n return this.getAndValidate(prop);\n },\n\n ownKeys: () =>\n {\n return Array.from(this.schemas.keys());\n },\n\n getOwnPropertyDescriptor: (_target, prop: string) =>\n {\n if (this.schemas.has(prop))\n {\n return {\n enumerable: true,\n configurable: true,\n get: () => this.getAndValidate(prop),\n };\n }\n\n return undefined;\n },\n\n has: (_target, prop: string) =>\n {\n return this.schemas.has(prop);\n },\n });\n }\n}\n\n/**\n * 환경변수 검증 결과\n */\nexport interface EnvValidationResult\n{\n valid: boolean;\n errors: Array<{ key: string; message: string }>;\n warnings: Array<{ key: string; message: string }>;\n}\n\n/**\n * 레지스트리 생성 헬퍼\n *\n * @example\n * ```typescript\n * const schema = defineEnvSchema({\n * DATABASE_URL: envString({ description: 'Database URL', required: true })\n * });\n *\n * const registry = createEnvRegistry(schema);\n * const env = registry.validate();\n * ```\n */\nexport function createEnvRegistry<T extends EnvSchemaCollection>(\n schemas: T,\n): EnvRegistry<T>\n{\n return new EnvRegistry(schemas);\n}\n\n/**\n * 모든 환경변수를 명시적으로 검증 (SKIP_ENV_VALIDATION 무시)\n *\n * CLI `spfn env validate` 명령어에서 사용\n *\n * @param registries - 검증할 레지스트리 배열\n * @returns 검증 결과\n *\n * @example\n * ```typescript\n * const result = validateAllEnv([coreRegistry, authRegistry]);\n * if (!result.valid) {\n * console.error('Missing env vars:', result.errors);\n * process.exit(1);\n * }\n * ```\n */\nexport function validateAllEnv(\n registries: EnvRegistry<any>[],\n): EnvValidationResult\n{\n const errors: Array<{ key: string; message: string }> = [];\n const warnings: Array<{ key: string; message: string }> = [];\n\n for (const registry of registries)\n {\n const result = registry.validateAll();\n errors.push(...result.errors);\n warnings.push(...result.warnings);\n }\n\n return {\n valid: errors.length === 0,\n errors,\n warnings,\n };\n}\n"]}
@@ -26,9 +26,10 @@ function parseEnvFile(filePath) {
26
26
  return parse(readFileSync(filePath, "utf-8"));
27
27
  }
28
28
  function loadEnv(options = {}) {
29
+ const nodeEnvAtEntry = process.env.NODE_ENV;
29
30
  const {
30
31
  cwd = process.cwd(),
31
- nodeEnv = process.env.NODE_ENV || "local",
32
+ nodeEnv = nodeEnvAtEntry || "local",
32
33
  server = true,
33
34
  debug = false,
34
35
  override = false
@@ -58,8 +59,30 @@ function loadEnv(options = {}) {
58
59
  envLogger.debug(`Loaded env files: ${loadedFiles.join(", ")}`);
59
60
  envLogger.debug(`Loaded ${loadedKeys.length} environment variables`);
60
61
  }
62
+ warnIfEnvGuessCostSomething(cwd, nodeEnvAtEntry, options.nodeEnv, nodeEnv);
61
63
  return { loadedFiles, loadedKeys };
62
64
  }
65
+ function warnIfEnvGuessCostSomething(cwd, nodeEnvAtEntry, explicitNodeEnv, resolvedNodeEnv) {
66
+ if (nodeEnvAtEntry || explicitNodeEnv) {
67
+ return;
68
+ }
69
+ const declared = process.env.NODE_ENV;
70
+ if (!declared) {
71
+ envLogger.warn(
72
+ `NODE_ENV is not set anywhere; env files were resolved for "${resolvedNodeEnv}". Set NODE_ENV in the process environment to choose the file set.`
73
+ );
74
+ return;
75
+ }
76
+ if (declared === resolvedNodeEnv) {
77
+ return;
78
+ }
79
+ if (!existsSync(resolve(cwd, `.env.${declared}`))) {
80
+ return;
81
+ }
82
+ envLogger.warn(
83
+ `.env.${declared} was NOT loaded: NODE_ENV was not set when the process started, so env files were resolved for "${resolvedNodeEnv}" before a file declared NODE_ENV=${declared}. Set NODE_ENV in the process environment, not in a .env file.`
84
+ );
85
+ }
63
86
  var isEnvLoaded = false;
64
87
  function loadEnvOnce(options = {}) {
65
88
  if (isEnvLoaded) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/env/loader.ts"],"names":[],"mappings":"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","file":"loader.js","sourcesContent":["/**\n * Environment Variable Loader\n *\n * Next.js 스타일의 환경변수 파일 로딩 (환경별 분리 지원)\n *\n * 로딩 우선순위 (낮음 -> 높음, 나중 파일이 덮어씀):\n * 1. .env - 기본값 (committed)\n * 2. .env.{NODE_ENV} - 환경별 오버라이드 (committed)\n * 3. .env.local - 로컬 오버라이드 (gitignored, test에서 스킵)\n * 4. .env.{NODE_ENV}.local - 환경별 시크릿 (gitignored)\n * 5. .env.server - 서버 전용 (gitignored, Next.js 미로드)\n *\n * @example\n * ```typescript\n * import { loadEnv } from '@spfn/core/env/loader';\n *\n * // 기본 사용 (NODE_ENV 자동 감지)\n * loadEnv();\n *\n * // 특정 환경 지정\n * loadEnv({ nodeEnv: 'production' });\n *\n * // 서버 레이어 제외 (Next.js 클라이언트용)\n * loadEnv({ server: false });\n * ```\n *\n * @module env/loader\n */\n\nimport { existsSync, readFileSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport { parse } from 'dotenv';\nimport { logger } from '@spfn/core/logger';\n\nconst envLogger = logger.child('@spfn/core:env-loader');\n\n/**\n * loadEnv 옵션\n */\nexport interface LoadEnvOptions\n{\n /**\n * 프로젝트 루트 경로\n * @default process.cwd()\n */\n cwd?: string;\n\n /**\n * NODE_ENV 값 (환경별 .env 파일 결정)\n * @default process.env.NODE_ENV || 'local'\n */\n nodeEnv?: string;\n\n /**\n * 서버 전용 파일 포함 여부 (.env.server)\n * @default true\n */\n server?: boolean;\n\n /**\n * 디버그 모드 (로드된 파일 로깅)\n * @default false\n */\n debug?: boolean;\n\n /**\n * 기존 process.env 값 덮어쓰기 허용\n * @default false\n */\n override?: boolean;\n}\n\n/**\n * 환경변수 로드 결과\n */\nexport interface LoadEnvResult\n{\n /**\n * 로드된 파일 목록\n */\n loadedFiles: string[];\n\n /**\n * 로드된 환경변수 키 목록\n */\n loadedKeys: string[];\n}\n\n/**\n * NODE_ENV에 따른 .env 파일 목록 생성 (우선순위 낮음 -> 높음)\n *\n * 더 구체적인 파일이 승리:\n * - environment > base\n * - server > shared\n * - local > committed\n */\nfunction getEnvFiles(nodeEnv: string, server: boolean): string[]\n{\n const files: string[] = [\n '.env',\n `.env.${nodeEnv}`,\n ];\n\n // test 환경에서는 .env.local 스킵 (테스트 결정론성 보장)\n if (nodeEnv !== 'test')\n {\n files.push('.env.local');\n }\n\n files.push(`.env.${nodeEnv}.local`);\n\n if (server)\n {\n files.push('.env.server');\n }\n\n return files;\n}\n\n/**\n * 단일 .env 파일 파싱\n */\nfunction parseEnvFile(filePath: string): Record<string, string> | null\n{\n if (!existsSync(filePath))\n {\n return null;\n }\n\n return parse(readFileSync(filePath, 'utf-8'));\n}\n\n/**\n * 프로젝트 루트의 환경변수 파일들을 규칙에 따라 로드\n *\n * 모든 파일을 파싱 후 머지한 뒤 process.env에 한번에 적용.\n * 이미 process.env에 존재하는 키는 덮어쓰지 않음 (플랫폼 주입 보호).\n *\n * @param options - 로드 옵션\n * @returns 로드 결과 (로드된 파일, 키 목록)\n */\nexport function loadEnv(options: LoadEnvOptions = {}): LoadEnvResult\n{\n const {\n cwd = process.cwd(),\n nodeEnv = process.env.NODE_ENV || 'local',\n server = true,\n debug = false,\n override = false,\n } = options;\n\n const envFiles = getEnvFiles(nodeEnv, server);\n const loadedFiles: string[] = [];\n\n // 1) 기존 process.env 키 스냅샷 저장\n const existingKeys = new Set(Object.keys(process.env));\n\n // 2) 모든 .env 파일 파싱 후 머지 (나중 파일이 승리)\n const merged: Record<string, string> = {};\n\n for (const fileName of envFiles)\n {\n const filePath = resolve(cwd, fileName);\n const parsed = parseEnvFile(filePath);\n\n if (parsed === null)\n {\n continue;\n }\n\n loadedFiles.push(fileName);\n Object.assign(merged, parsed);\n }\n\n // 3) 머지된 결과를 process.env에 적용\n const loadedKeys: string[] = [];\n\n for (const [key, value] of Object.entries(merged))\n {\n // 기존 process.env에 이미 있는 키는 스킵 (override가 false일 때)\n if (!override && existingKeys.has(key))\n {\n continue;\n }\n\n process.env[key] = value;\n loadedKeys.push(key);\n }\n\n if (debug && loadedFiles.length > 0)\n {\n envLogger.debug(`Loaded env files: ${loadedFiles.join(', ')}`);\n envLogger.debug(`Loaded ${loadedKeys.length} environment variables`);\n }\n\n return { loadedFiles, loadedKeys };\n}\n\n/**\n * 환경변수가 이미 로드되었는지 확인하는 플래그\n */\nlet isEnvLoaded = false;\n\n/**\n * 환경변수를 한 번만 로드 (중복 호출 방지)\n *\n * @param options - 로드 옵션\n * @returns 로드 결과 (이미 로드된 경우 빈 결과)\n */\nexport function loadEnvOnce(options: LoadEnvOptions = {}): LoadEnvResult\n{\n if (isEnvLoaded)\n {\n return { loadedFiles: [], loadedKeys: [] };\n }\n\n isEnvLoaded = true;\n\n return loadEnv(options);\n}\n\n/**\n * 환경변수 로드 상태 리셋 (테스트용)\n */\nexport function resetEnvLoadState(): void\n{\n isEnvLoaded = false;\n}\n"]}
1
+ {"version":3,"sources":["../../src/env/loader.ts"],"names":[],"mappings":"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","file":"loader.js","sourcesContent":["/**\n * Environment Variable Loader\n *\n * Next.js 스타일의 환경변수 파일 로딩 (환경별 분리 지원)\n *\n * 로딩 우선순위 (낮음 -> 높음, 나중 파일이 덮어씀):\n * 1. .env - 기본값 (committed)\n * 2. .env.{NODE_ENV} - 환경별 오버라이드 (committed)\n * 3. .env.local - 로컬 오버라이드 (gitignored, test에서 스킵)\n * 4. .env.{NODE_ENV}.local - 환경별 시크릿 (gitignored)\n * 5. .env.server - 서버 전용 (gitignored, Next.js 미로드)\n *\n * @example\n * ```typescript\n * import { loadEnv } from '@spfn/core/env/loader';\n *\n * // 기본 사용 (NODE_ENV 자동 감지)\n * loadEnv();\n *\n * // 특정 환경 지정\n * loadEnv({ nodeEnv: 'production' });\n *\n * // 서버 레이어 제외 (Next.js 클라이언트용)\n * loadEnv({ server: false });\n * ```\n *\n * @module env/loader\n */\n\nimport { existsSync, readFileSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport { parse } from 'dotenv';\nimport { logger } from '@spfn/core/logger';\n\nconst envLogger = logger.child('@spfn/core:env-loader');\n\n/**\n * loadEnv 옵션\n */\nexport interface LoadEnvOptions\n{\n /**\n * 프로젝트 루트 경로\n * @default process.cwd()\n */\n cwd?: string;\n\n /**\n * NODE_ENV 값 (환경별 .env 파일 결정)\n * @default process.env.NODE_ENV || 'local'\n */\n nodeEnv?: string;\n\n /**\n * 서버 전용 파일 포함 여부 (.env.server)\n * @default true\n */\n server?: boolean;\n\n /**\n * 디버그 모드 (로드된 파일 로깅)\n * @default false\n */\n debug?: boolean;\n\n /**\n * 기존 process.env 값 덮어쓰기 허용\n * @default false\n */\n override?: boolean;\n}\n\n/**\n * 환경변수 로드 결과\n */\nexport interface LoadEnvResult\n{\n /**\n * 로드된 파일 목록\n */\n loadedFiles: string[];\n\n /**\n * 로드된 환경변수 키 목록\n */\n loadedKeys: string[];\n}\n\n/**\n * NODE_ENV에 따른 .env 파일 목록 생성 (우선순위 낮음 -> 높음)\n *\n * 더 구체적인 파일이 승리:\n * - environment > base\n * - server > shared\n * - local > committed\n */\nfunction getEnvFiles(nodeEnv: string, server: boolean): string[]\n{\n const files: string[] = [\n '.env',\n `.env.${nodeEnv}`,\n ];\n\n // test 환경에서는 .env.local 스킵 (테스트 결정론성 보장)\n if (nodeEnv !== 'test')\n {\n files.push('.env.local');\n }\n\n files.push(`.env.${nodeEnv}.local`);\n\n if (server)\n {\n files.push('.env.server');\n }\n\n return files;\n}\n\n/**\n * 단일 .env 파일 파싱\n */\nfunction parseEnvFile(filePath: string): Record<string, string> | null\n{\n if (!existsSync(filePath))\n {\n return null;\n }\n\n return parse(readFileSync(filePath, 'utf-8'));\n}\n\n/**\n * 프로젝트 루트의 환경변수 파일들을 규칙에 따라 로드\n *\n * 모든 파일을 파싱 후 머지한 뒤 process.env에 한번에 적용.\n * 이미 process.env에 존재하는 키는 덮어쓰지 않음 (플랫폼 주입 보호).\n *\n * @param options - 로드 옵션\n * @returns 로드 결과 (로드된 파일, 키 목록)\n */\nexport function loadEnv(options: LoadEnvOptions = {}): LoadEnvResult\n{\n // Captured before any file is read: NODE_ENV decides WHICH files load, so a\n // value that only arrives from inside a file arrives too late to matter.\n const nodeEnvAtEntry = process.env.NODE_ENV;\n\n const {\n cwd = process.cwd(),\n nodeEnv = nodeEnvAtEntry || 'local',\n server = true,\n debug = false,\n override = false,\n } = options;\n\n const envFiles = getEnvFiles(nodeEnv, server);\n const loadedFiles: string[] = [];\n\n // 1) 기존 process.env 키 스냅샷 저장\n const existingKeys = new Set(Object.keys(process.env));\n\n // 2) 모든 .env 파일 파싱 후 머지 (나중 파일이 승리)\n const merged: Record<string, string> = {};\n\n for (const fileName of envFiles)\n {\n const filePath = resolve(cwd, fileName);\n const parsed = parseEnvFile(filePath);\n\n if (parsed === null)\n {\n continue;\n }\n\n loadedFiles.push(fileName);\n Object.assign(merged, parsed);\n }\n\n // 3) 머지된 결과를 process.env에 적용\n const loadedKeys: string[] = [];\n\n for (const [key, value] of Object.entries(merged))\n {\n // 기존 process.env에 이미 있는 키는 스킵 (override가 false일 때)\n if (!override && existingKeys.has(key))\n {\n continue;\n }\n\n process.env[key] = value;\n loadedKeys.push(key);\n }\n\n if (debug && loadedFiles.length > 0)\n {\n envLogger.debug(`Loaded env files: ${loadedFiles.join(', ')}`);\n envLogger.debug(`Loaded ${loadedKeys.length} environment variables`);\n }\n\n warnIfEnvGuessCostSomething(cwd, nodeEnvAtEntry, options.nodeEnv, nodeEnv);\n\n return { loadedFiles, loadedKeys };\n}\n\n/**\n * Warn when guessing NODE_ENV actually cost the caller something.\n *\n * `getEnvFiles` runs before any file is parsed, so NODE_ENV decides WHICH files\n * load and a value set inside .env.server arrives too late to affect that. The\n * warning therefore reports a missed file, not a missing variable — the previous\n * version announced the variable itself and fired on every boot of a correctly\n * configured app (issue #136).\n *\n * Silent when the guess cost nothing: the guess matched what the files declare,\n * or it did not match but no `.env.<declared>` file exists to have been skipped.\n *\n * @param cwd - project root the files were resolved against\n * @param nodeEnvAtEntry - NODE_ENV as the process was started with\n * @param explicitNodeEnv - nodeEnv passed by the caller, if any\n * @param resolvedNodeEnv - the value the file list was actually built from\n */\nfunction warnIfEnvGuessCostSomething(\n cwd: string,\n nodeEnvAtEntry: string | undefined,\n explicitNodeEnv: string | undefined,\n resolvedNodeEnv: string,\n): void\n{\n // Nothing was guessed — the process or the caller named the environment.\n if (nodeEnvAtEntry || explicitNodeEnv)\n {\n return;\n }\n\n const declared = process.env.NODE_ENV;\n\n if (!declared)\n {\n envLogger.warn(\n `NODE_ENV is not set anywhere; env files were resolved for \"${resolvedNodeEnv}\".`\n + ' Set NODE_ENV in the process environment to choose the file set.',\n );\n\n return;\n }\n\n if (declared === resolvedNodeEnv)\n {\n return;\n }\n\n // The guess was wrong, but a file set only goes missing if the file exists.\n if (!existsSync(resolve(cwd, `.env.${declared}`)))\n {\n return;\n }\n\n envLogger.warn(\n `.env.${declared} was NOT loaded: NODE_ENV was not set when the process started,`\n + ` so env files were resolved for \"${resolvedNodeEnv}\" before a file declared`\n + ` NODE_ENV=${declared}. Set NODE_ENV in the process environment, not in a .env file.`,\n );\n}\n\n/**\n * 환경변수가 이미 로드되었는지 확인하는 플래그\n */\nlet isEnvLoaded = false;\n\n/**\n * 환경변수를 한 번만 로드 (중복 호출 방지)\n *\n * @param options - 로드 옵션\n * @returns 로드 결과 (이미 로드된 경우 빈 결과)\n */\nexport function loadEnvOnce(options: LoadEnvOptions = {}): LoadEnvResult\n{\n if (isEnvLoaded)\n {\n return { loadedFiles: [], loadedKeys: [] };\n }\n\n isEnvLoaded = true;\n\n return loadEnv(options);\n}\n\n/**\n * 환경변수 로드 상태 리셋 (테스트용)\n */\nexport function resetEnvLoadState(): void\n{\n isEnvLoaded = false;\n}\n"]}
@@ -0,0 +1,157 @@
1
+ import { R as RouteDef, f as RouteBuilder } from './route-builder-DRW-MSCJ.js';
2
+ import { J as JsonSchema } from './types-5Rl11_Nb.js';
3
+ import { R as Router } from './router-C57y-vz1.js';
4
+ import { RouteEffect, HttpMethod } from './route/types.js';
5
+
6
+ /**
7
+ * How refusals name the surface they come from.
8
+ *
9
+ * A command surface is generic, but the ops surface predates it and its
10
+ * refusals already say "ops route" and point at `opsRoute`. The ops layer
11
+ * passes its own wording so an operator reading an ops refusal sees the same
12
+ * text as before; every other surface reads the neutral one.
13
+ */
14
+ interface SurfaceWording {
15
+ /** What a route on this surface is called: "ops route", "surface route". */
16
+ noun: string;
17
+ /** The builder that applies the namespace, as named in a refusal. */
18
+ builder: string;
19
+ /** The function that builds the surface, as named in a refusal. */
20
+ factory: string;
21
+ }
22
+
23
+ /**
24
+ * Capability module contract
25
+ *
26
+ * A package describes commands here, but an adopter application decides
27
+ * whether to mount the resulting module, and on which surface. Validation
28
+ * keeps an installed package from becoming a command surface merely by
29
+ * existing, and gives the surface enough metadata to enforce scopes and
30
+ * describe the module to a client from one declaration.
31
+ *
32
+ * A module's paths are written for one namespace — `/_ops/<id>/...` for an
33
+ * ops module — so it is validated against the namespace of the surface that
34
+ * mounts it. A module written for `/_ops` mounted on `/_app` is refused rather
35
+ * than registered at paths outside the surface that is supposed to guard them.
36
+ */
37
+
38
+ /** One vocabulary for module commands and an app's own: the route's `.effect()`. */
39
+ type SurfaceEffect = RouteEffect;
40
+ interface SurfaceModuleCommand<TRoute extends RouteDef<any> = RouteDef<any>> {
41
+ summary: string;
42
+ effect: SurfaceEffect;
43
+ scopes: readonly string[];
44
+ route: TRoute;
45
+ }
46
+ interface SurfaceModule<TCommands extends Record<string, SurfaceModuleCommand<any>> = Record<string, SurfaceModuleCommand<any>>> {
47
+ id: string;
48
+ source: string;
49
+ contractVersion: string;
50
+ summary: string;
51
+ commands: TCommands;
52
+ }
53
+ /**
54
+ * Validate and return a capability module written for a surface on
55
+ * `namespace` — every command under `<namespace>/<id>/`. The surface checks
56
+ * it again against its own namespace when it mounts it, so a module defined
57
+ * for one namespace and mounted on another is still refused.
58
+ */
59
+ declare function defineSurfaceModule<TCommands extends Record<string, SurfaceModuleCommand<any>>>(module: SurfaceModule<TCommands>, namespace: string): SurfaceModule<TCommands>;
60
+
61
+ /**
62
+ * Surface namespace and route builder
63
+ *
64
+ * A namespace is the path every command on a surface lives under — `/_ops`
65
+ * for operators, `/_app` or anything else an app chooses for another caller.
66
+ * It is refused at definition time unless it is a plain absolute path of
67
+ * URL-safe segments: no trailing slash, no dots, no percent signs, no
68
+ * parameters. Every check downstream — the prefix a route must carry, the
69
+ * module paths, the manifest URL, the CLI's own path checks — is a string
70
+ * comparison against it, and only a literal path keeps those comparisons
71
+ * honest.
72
+ *
73
+ * The route builder is `route` with the namespace applied, so a definition
74
+ * carries only the part of the path the app owns:
75
+ *
76
+ * ```ts
77
+ * const pageAdd = app.route.post('/pages') // POST /_app/pages
78
+ * .handler(async () => ({ ok: true }));
79
+ * ```
80
+ */
81
+
82
+ /** `route`, with a surface's namespace applied. */
83
+ interface SurfaceRouteBuilder {
84
+ get: (path: string) => RouteBuilder;
85
+ post: (path: string) => RouteBuilder;
86
+ put: (path: string) => RouteBuilder;
87
+ patch: (path: string) => RouteBuilder;
88
+ delete: (path: string) => RouteBuilder;
89
+ }
90
+
91
+ /**
92
+ * Surface Manifest
93
+ *
94
+ * The server's self-description of a command surface: every command an app
95
+ * exposes under the surface's namespace, with each command's input schemas as
96
+ * plain JSON Schema. A client fetches this from the running server, so command
97
+ * discovery needs neither the app's source nor a generated artifact on the
98
+ * caller's machine.
99
+ *
100
+ * The router is loaded and walked (the contract collector's approach) rather
101
+ * than parsed from source: route schemas built from imported values resolve,
102
+ * and TypeBox schemas serialize to JSON Schema by construction.
103
+ */
104
+
105
+ /** One explicitly mounted capability module, as a client sees it. */
106
+ interface SurfaceModuleDescriptor {
107
+ id: string;
108
+ source: string;
109
+ contractVersion: string;
110
+ summary: string;
111
+ }
112
+ /** One invokable command, as a client sees it. */
113
+ interface SurfaceCommand {
114
+ name: string;
115
+ method: HttpMethod;
116
+ path: string;
117
+ /** Present for commands contributed by an explicitly mounted module. */
118
+ module?: string;
119
+ /**
120
+ * A module command's from its declaration; an app command's from the
121
+ * route's `.summary()` and `.effect()`, present only when declared.
122
+ */
123
+ summary?: string;
124
+ effect?: SurfaceEffect;
125
+ /** Present for module commands only. */
126
+ scopes?: string[];
127
+ /** Input sections the route declares, each as JSON Schema. */
128
+ input: {
129
+ params?: JsonSchema;
130
+ query?: JsonSchema;
131
+ body?: JsonSchema;
132
+ };
133
+ }
134
+ /** What `GET <namespace>/_manifest` answers. */
135
+ interface SurfaceManifest {
136
+ manifestVersion: 1;
137
+ /** Additive module metadata. Omitted for an app-only v1 surface. */
138
+ modules?: SurfaceModuleDescriptor[];
139
+ commands: SurfaceCommand[];
140
+ }
141
+ /**
142
+ * Walk a routes record (nested routers included) and collect every RouteDef
143
+ * as a command, with the summary and effect its route declared. Validation of
144
+ * paths and of that metadata happens in the surface's `router()`, so nothing
145
+ * here is checked but the uniqueness of names.
146
+ */
147
+ declare function collectSurfaceCommands(routes: Record<string, RouteDef<any> | Router<any>>, wording?: SurfaceWording): SurfaceCommand[];
148
+
149
+ /**
150
+ * Thrown when a namespace, route, or module cannot be part of a command
151
+ * surface. `@spfn/core/ops` exports the same class as `OpsRouterError`.
152
+ */
153
+ declare class SurfaceError extends Error {
154
+ constructor(message: string);
155
+ }
156
+
157
+ export { type SurfaceEffect as S, type SurfaceModuleCommand as a, type SurfaceModule as b, type SurfaceRouteBuilder as c, type SurfaceCommand as d, type SurfaceManifest as e, type SurfaceModuleDescriptor as f, SurfaceError as g, collectSurfaceCommands as h, defineSurfaceModule as i };
@@ -103,6 +103,13 @@ type ErrorRegistryInput = SerializableErrorConstructor | SerializableErrorConstr
103
103
  */
104
104
  declare class ErrorRegistry {
105
105
  private errors;
106
+ /**
107
+ * The same classes keyed by the name their instances set for themselves.
108
+ * Consulted only when no class is registered under the constructor name, so
109
+ * an unminified registry resolves exactly as before and a minified one —
110
+ * whose constructor names are `Q`, `eA`, … — still resolves `__type`.
111
+ */
112
+ private instanceNames;
106
113
  constructor(initialErrors?: ErrorRegistryInput[]);
107
114
  /**
108
115
  * Append error class(es) to the registry
@@ -118,6 +125,11 @@ declare class ErrorRegistry {
118
125
  * @returns This registry for chaining
119
126
  */
120
127
  append(ErrorClasses: SerializableErrorConstructor[]): this;
128
+ /**
129
+ * Key `ErrorClass` by its constructor name and, when its instances set one of
130
+ * their own, by that name too.
131
+ */
132
+ private register;
121
133
  /**
122
134
  * Concatenate another ErrorRegistry into this one
123
135
  *
@@ -125,10 +137,15 @@ declare class ErrorRegistry {
125
137
  * @returns This registry for chaining
126
138
  */
127
139
  concat(registry: ErrorRegistry): this;
140
+ /**
141
+ * The class registered for `type`: by constructor name first, then by the
142
+ * name its instances carry.
143
+ */
144
+ private find;
128
145
  /**
129
146
  * Check if error type is registered
130
147
  *
131
- * @param name - Error class name
148
+ * @param name - Error class name, or the name its instances carry
132
149
  */
133
150
  has(name: string): boolean;
134
151
  /**