{"id":36967925,"url":"https://github.com/ikelaiah/dotenv-fp","last_synced_at":"2026-01-13T20:58:32.283Z","repository":{"id":328859639,"uuid":"1114002771","full_name":"ikelaiah/dotenv-fp","owner":"ikelaiah","description":"A feature-rich dotenv library for Free Pascal: load environment variables from .env files with type-safe getters, validation, interpolation, and zero memory management.","archived":false,"fork":false,"pushed_at":"2025-12-15T20:03:56.000Z","size":587,"stargazers_count":12,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-12-19T03:50:42.909Z","etag":null,"topics":["config","configuration","cross-platform","dotenv","env","environment-variables","fpc","freepascal","lazarus","library","object-pascal","pascal"],"latest_commit_sha":null,"homepage":"","language":"Pascal","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ikelaiah.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-12-10T19:00:19.000Z","updated_at":"2025-12-17T19:30:06.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/ikelaiah/dotenv-fp","commit_stats":null,"previous_names":["ikelaiah/dotenv-fp"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/ikelaiah/dotenv-fp","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ikelaiah%2Fdotenv-fp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ikelaiah%2Fdotenv-fp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ikelaiah%2Fdotenv-fp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ikelaiah%2Fdotenv-fp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ikelaiah","download_url":"https://codeload.github.com/ikelaiah/dotenv-fp/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ikelaiah%2Fdotenv-fp/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28399057,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-13T14:36:09.778Z","status":"ssl_error","status_checked_at":"2026-01-13T14:35:19.697Z","response_time":56,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["config","configuration","cross-platform","dotenv","env","environment-variables","fpc","freepascal","lazarus","library","object-pascal","pascal"],"created_at":"2026-01-13T20:58:31.443Z","updated_at":"2026-01-13T20:58:32.264Z","avatar_url":"https://github.com/ikelaiah.png","language":"Pascal","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 🌿 dotenv-fp\n\n\u003e **Load environment variables from `.env` files in Free Pascal — the easy way!**\n\nA feature-rich dotenv library for **Free Pascal 3.2.2+** inspired by [python-dotenv](https://github.com/theskumar/python-dotenv). Perfect for managing configuration in your Pascal applications without hardcoding sensitive data.\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-1E3A8A.svg)](https://opensource.org/licenses/MIT)\n[![Free Pascal](https://img.shields.io/badge/Free%20Pascal-3.2.2+-3B82F6.svg)](https://www.freepascal.org/)\n[![Lazarus](https://img.shields.io/badge/Lazarus-4.0+-60A5FA.svg)](https://www.lazarus-ide.org/)\n![Supports Windows](https://img.shields.io/badge/support-Windows-F59E0B?logo=Windows)\n![Supports Linux](https://img.shields.io/badge/support-Linux-F59E0B?logo=Linux)\n[![Version](https://img.shields.io/badge/version-1.1.0-8B5CF6.svg)](CHANGELOG.md)\n![No Dependencies](https://img.shields.io/badge/dependencies-none-10B981.svg)\n[![Documentation](https://img.shields.io/badge/Docs-Available-brightgreen.svg)](docs/)\n[![Status](https://img.shields.io/badge/Status-Stable-brightgreen.svg)]()\n\n## ✨ Features\n\n| Feature | Description |\n|---------|-------------|\n| 📁 **Load `.env` files** | Read configuration from `.env` files |\n| 🔗 **Variable interpolation** | Use `${VAR}` or `$VAR` syntax to reference other variables |\n| 📝 **Multi-line values** | Support for values spanning multiple lines |\n| 🎯 **Quoted values** | Single quotes, double quotes, or unquoted |\n| 💬 **Comments** | Use `#` for comments (line or inline) |\n| 🐚 **Shell compatible** | Supports `export` prefix for shell compatibility |\n| 🔒 **Type-safe getters** | `GetInt()`, `GetBool()`, `GetFloat()`, `GetArray()` |\n| ⚡ **Default values** | Fallback values when keys are missing |\n| ✅ **Validation** | Check for required variables before running |\n| 📚 **Multiple files** | Load `.env`, `.env.local`, `.env.production`, etc. |\n| 🌍 **Environment-aware** | Auto-load `.env.{environment}` files (v1.1.0+) |\n| 💾 **Save to file** | Generate `.env` files programmatically (v1.1.0+) |\n| 📋 **Generate examples** | Create `.env.example` for version control (v1.1.0+) |\n| 💬 **Interactive prompts** | `GetOrPrompt()` for first-run setup (v1.1.0+) |\n| 🏷️ **Key prefixing** | Add prefixes like `APP_` to all loaded keys |\n| 🧹 **Zero memory leaks** | Uses advanced records — no manual `Free` calls! |\n| 📦 **Zero dependencies** | Only standard FPC units |\n\n## 🚀 Quick Start\n\n### Installation\n\n1. Copy `src/DotEnv.pas` to your project  \n   *— or add the `src` folder to your unit search path*\n2. Add `DotEnv` to your `uses` clause\n\nThat's it! No package manager needed. 🎉\n\n### Your First `.env` File\n\nCreate a `.env` file in your project root:\n\n```bash\n# Database settings\nDATABASE_URL=postgresql://localhost/mydb\nDB_POOL_SIZE=10\n\n# Server configuration  \nPORT=3000\nDEBUG=true\n\n# Secrets (never commit these!)\nSECRET_KEY=\"super-secret-key-here\"\n```\n\n### Load It in Pascal\n\n```pascal\nprogram MyApp;\n\n{$mode objfpc}{$H+}{$J-}\n\nuses\n  DotEnv;\n\nvar\n  Env: TDotEnv;\nbegin\n  // Create and load .env file\n  Env := TDotEnv.Create;\n  Env.Load;  // Loads .env from current directory\n  \n  // Read values with type safety\n  WriteLn('Database: ', Env.Get('DATABASE_URL'));\n  WriteLn('Port: ', Env.GetInt('PORT', 3000));\n  WriteLn('Debug mode: ', Env.GetBool('DEBUG', False));\n  WriteLn('Pool size: ', Env.GetInt('DB_POOL_SIZE', 5));\n  \n  // No need to free - advanced records clean up automatically!\nend.\n```\n\n## 📖 `.env` File Format\n\n```bash\n# 💬 Comments start with #\n# Empty lines are ignored\n\n# Simple key-value pairs\nDATABASE_URL=postgresql://localhost/mydb\nPORT=3000\nDEBUG=true\n\n# 🎯 Quoted values (preserves spaces and special chars)\nSECRET_KEY=\"my-secret-key\"\nMESSAGE='Hello, World!'\nGREETING=\"Welcome to the app!\"\n\n# 🔗 Variable interpolation\nBASE_URL=https://api.example.com\nAPI_ENDPOINT=${BASE_URL}/v1/users\nFULL_URL=$BASE_URL/health\n\n# 📝 Multi-line values (use quotes)\nPRIVATE_KEY=\"-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEA...\n...more lines...\n-----END RSA PRIVATE KEY-----\"\n\n# 🐚 Shell-compatible export syntax\nexport SHELL_VAR=works_in_bash_too\n\n# 📋 Arrays (comma-separated, parsed with GetArray)\nALLOWED_HOSTS=localhost,127.0.0.1,example.com\nFEATURES=auth,logging,cache\n```\n\n## 📚 API Reference\n\n### Creating and Loading\n\n```pascal\nvar\n  Env: TDotEnv;\n  Options: TDotEnvOptions;\nbegin\n  // 🟢 Simple usage\n  Env := TDotEnv.Create;\n  Env.Load;                    // Load .env\n  Env.Load('.env.local');      // Load specific file\n  \n  // 🟡 With options\n  Options := TDotEnvOptions.Default;\n  Options.Override := True;    // Override existing env vars\n  Options.Verbose := True;     // Print debug info  \n  Options.Prefix := 'APP_';    // Prefix all keys with APP_\n  \n  Env := TDotEnv.CreateWithOptions(Options);\n  Env.Load;\n  \n  // 🔵 Load multiple files (later files override earlier ones)\n  Env.LoadMultiple(['.env', '.env.local', '.env.development']);\n\n  // 🟣 Load from string (great for testing!)\n  Env.LoadFromString('KEY=value' + LineEnding + 'OTHER=test');\n\n  // 🌍 NEW in v1.1.0: Environment-aware loading\n  Env.LoadForEnvironment('production');   // Loads .env + .env.production\n  Env.LoadForEnvironment();               // Auto-detects from APP_ENV or NODE_ENV\nend;\n```\n\n### Getting Values\n\n```pascal\n// 📝 Strings\nEnv.Get('KEY');                    // Returns '' if missing\nEnv.Get('KEY', 'default');         // Returns 'default' if missing\nEnv.GetRequired('KEY');            // Raises exception if missing\n\n// 🔢 Integers  \nEnv.GetInt('PORT');                // Returns 0 if missing/invalid\nEnv.GetInt('PORT', 3000);          // Returns 3000 if missing/invalid\nEnv.GetIntRequired('PORT');        // Raises exception if missing/invalid\n\n// ✅ Booleans (recognizes: true/false, yes/no, 1/0, on/off)\nEnv.GetBool('DEBUG');              // Returns False if missing\nEnv.GetBool('DEBUG', True);        // Returns True if missing\nEnv.GetBoolRequired('DEBUG');      // Raises exception if missing\n\n// 🔬 Floats\nEnv.GetFloat('RATE');              // Returns 0.0 if missing/invalid\nEnv.GetFloat('RATE', 0.5);         // Returns 0.5 if missing/invalid\nEnv.GetFloatRequired('RATE');      // Raises exception if missing/invalid\n\n// 📋 Arrays (splits comma-separated values)\nHosts := Env.GetArray('ALLOWED_HOSTS');        // Split by comma\nTags := Env.GetArray('TAGS', ';');             // Split by semicolon\n\n// 💬 NEW in v1.1.0: Interactive prompts (for setup scripts)\nDbUrl := Env.GetOrPrompt('DATABASE_URL',\n                        'Enter database URL',\n                        'postgres://localhost/mydb');\n// Prompts user if DATABASE_URL is missing, uses existing value if present\n```\n\n### Validation\n\n```pascal\nvar\n  Missing: TStringDynArray;\n  I: Integer;\nbegin\n  // ✅ Check if all required keys exist\n  if Env.Validate(['DATABASE_URL', 'SECRET_KEY', 'PORT']) then\n    WriteLn('All required configuration present!')\n  else\n  begin\n    // 📋 Get list of missing keys\n    Missing := Env.GetMissing(['DATABASE_URL', 'SECRET_KEY', 'PORT']);\n    WriteLn('Missing configuration:');\n    for I := 0 to High(Missing) do\n      WriteLn('  - ', Missing[I]);\n    Halt(1);\n  end;\nend;\n```\n\n### Utilities\n\n```pascal\n// 🔍 Check if key exists\nif Env.Has('OPTIONAL_FEATURE') then\n  EnableFeature;\n\n// 📋 Get all keys and values\nKeys := Env.Keys;           // TStringDynArray of all key names\nValues := Env.Values;       // TStringDynArray of all values  \nPairs := Env.AsArray;       // TDotEnvPairArray with Key/Value records\n\n// 🔢 Count loaded variables\nWriteLn('Loaded ', Env.Count, ' environment variables');\n\n// 🐛 Debug output (shows all loaded key=value pairs)\nWriteLn(Env.ToString);\n\n// 📁 See which files were loaded\nfor I := 0 to High(Env.LoadedFiles) do\n  WriteLn('Loaded: ', Env.LoadedFiles[I]);\n```\n\n### File Operations (v1.1.0+)\n\n```pascal\n// 💾 Save environment variables to a file\nEnv := TDotEnv.Create;\nEnv.SetToEnv('DATABASE_URL', 'postgres://localhost/mydb');\nEnv.SetToEnv('PORT', '3000');\nEnv.SetToEnv('DEBUG', 'true');\nEnv.Save('.env');  // Writes to .env file\n\n// 📋 Generate .env.example for version control\nEnv.Load('.env');\nEnv.GenerateExample('.env', '.env.example');\n// Creates .env.example with keys but empty values\n\n// 🌍 Environment-aware loading pattern\nEnv := TDotEnv.Create;\nEnv.LoadForEnvironment('development');   // Loads .env + .env.development\nEnv.LoadForEnvironment('production');    // Loads .env + .env.production\nEnv.LoadForEnvironment();                // Auto-detects from APP_ENV/NODE_ENV\n\n// 💬 Interactive setup script example\nEnv := TDotEnv.Create;\nEnv.Load('.env');  // Try to load existing config\nDbUrl := Env.GetOrPrompt('DATABASE_URL',\n                        'Enter database URL',\n                        'postgres://localhost/mydb');\nPort := Env.GetOrPrompt('PORT', 'Enter port', '3000');\nEnv.Save('.env');  // Save the configuration\nWriteLn('Configuration saved!');\n```\n\n### Global Helpers (Simple API)\n\nFor quick scripts or simple applications:\n\n```pascal\nuses DotEnv;\n\nbegin\n  DotEnvLoad;                              // Load .env\n  DotEnvLoad('.env.local');                // Load specific file\n  \n  WriteLn(DotEnvGet('DATABASE_URL'));      // Get value\n  WriteLn(DotEnvGet('PORT', '3000'));      // Get with default\n  \n  DotEnvSet('RUNTIME_VAR', 'value');       // Set at runtime\nend.\n```\n\n## 🔗 Variable Interpolation\n\nReference other variables using `${VAR}` or `$VAR` syntax:\n\n```bash\n# Define base values\nAPP_NAME=MyAwesomeApp\nAPP_VERSION=1.0.0\nBASE_URL=https://api.example.com\n\n# Reference them in other values\nAPP_TITLE=${APP_NAME} v${APP_VERSION}\nAPI_USERS=${BASE_URL}/users\nAPI_HEALTH=$BASE_URL/health\n\n# Also works with system environment variables!\nHOME_CONFIG=${HOME}/.myapp/config\n```\n\n**Resolution order:**\n1. Variables defined earlier in the same `.env` file\n2. System environment variables\n\n## ⚙️ Options\n\n| Option | Type | Default | Description |\n|--------|------|---------|-------------|\n| `Override` | `Boolean` | `False` | Override existing system environment variables |\n| `Interpolate` | `Boolean` | `True` | Enable `${VAR}` variable interpolation |\n| `Verbose` | `Boolean` | `False` | Print debug info while loading |\n| `Prefix` | `String` | `''` | Add prefix to all loaded key names |\n\n```pascal\nvar\n  Options: TDotEnvOptions;\nbegin\n  Options := TDotEnvOptions.Default;  // Start with defaults\n  Options.Override := True;\n  Options.Prefix := 'MYAPP_';\n  \n  Env := TDotEnv.CreateWithOptions(Options);\n  Env.Load;\n  \n  // KEY=value in .env becomes accessible as MYAPP_KEY\n  WriteLn(Env.Get('MYAPP_KEY'));\nend;\n```\n\n## ⚠️ Error Handling\n\n```pascal\nuses DotEnv;\n\nvar\n  Env: TDotEnv;\nbegin\n  Env := TDotEnv.Create;\n  Env.Load;\n  \n  // 🔴 Handle missing required keys\n  try\n    Env.GetRequired('MISSING_KEY');\n  except\n    on E: EDotEnvMissingKey do\n      WriteLn('Configuration error: ', E.Message);\n  end;\n  \n  // 🔴 Handle invalid type conversions\n  try\n    Env.GetIntRequired('NOT_A_NUMBER');\n  except\n    on E: EDotEnvParseError do\n      WriteLn('Parse error: ', E.Message);\n  end;\nend.\n```\n\n## 🆚 Comparison with Python dotenv\n\ndotenv-fp is inspired by [python-dotenv](https://github.com/theskumar/python-dotenv) and provides equivalent core functionality, plus additional features suited for statically-typed Pascal development:\n\n| Feature | python-dotenv | dotenv-fp |\n|---------|:-------------:|:---------:|\n| Load .env file | ✅ | ✅ |\n| Variable interpolation | ✅ | ✅ |\n| Multi-line values | ✅ | ✅ |\n| Quoted values | ✅ | ✅ |\n| Export prefix | ✅ | ✅ |\n| Comments | ✅ | ✅ |\n| Override mode | ✅ | ✅ |\n| **Type-safe getters** | — | ✅ |\n| **Built-in validation** | — | ✅ |\n| **Array parsing** | — | ✅ |\n| **Key prefixing** | — | ✅ |\n| **Automatic memory management** | N/A | ✅ |\n\n## 🧪 Running Tests\n\nThe library includes a comprehensive test suite using FPCUnit:\n\n```bash\ncd tests\nfpc TestRunner.pas\n./TestRunner -a --format=plain\n```\n\nExpected output:\n```\nTime:00.075 N:96 E:0 F:0 I:0\n  TTestDotEnv Time:00.075 N:96 E:0 F:0 I:0\n    00.000  Test01_BasicParsing_SimpleKeyValue\n    00.000  Test02_BasicParsing_TrimmedValue\n    ...\nNumber of run tests: 96\nNumber of errors:    0\nNumber of failures:  0\n```\n\n## 📁 Project Structure\n\n```\ndotenv-fp/\n├── src/\n│   └── DotEnv.pas          # Main library unit\n├── tests/\n│   ├── TestRunner.pas      # FPCUnit test runner\n│   └── DotEnv.Test.pas     # Test cases\n├── examples/\n│   ├── basic/              # Basic usage example\n│   └── advanced/           # Advanced features example\n├── docs/                   # Documentation\n├── .env.example            # Example .env file\n├── CHANGELOG.md\n└── README.md\n```\n\n## 💡 Tips for New Free Pascal Developers\n\n1. **Advanced Records**: This library uses advanced records (`{$modeswitch advancedrecords}`), which means you don't need to call `.Free` — memory is managed automatically!\n\n2. **Mode ObjFPC**: Make sure your program uses `{$mode objfpc}{$H+}{$J-}` for compatibility.\n\n3. **String Handling**: The `{$H+}` switch enables long strings (AnsiString) by default, which this library requires.\n\n4. **File Paths**: Use forward slashes `/` or the `PathDelim` constant for cross-platform compatibility.\n\n## 🤝 Contributing\n\nContributions are welcome! Feel free to:\n\n- 🐛 Report bugs\n- 💡 Suggest features  \n- 🔧 Submit pull requests\n\n## 📄 License\n\nMIT License — See [LICENSE](LICENSE) file for details.\n\n## 🙏 Acknowledgments\n\n- Inspired by [python-dotenv](https://github.com/theskumar/python-dotenv)\n- Built for the awesome Free Pascal community\n\n---\n\n**Happy coding! 🚀**\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fikelaiah%2Fdotenv-fp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fikelaiah%2Fdotenv-fp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fikelaiah%2Fdotenv-fp/lists"}