diff --git a/.github/workflows/bun-test.yml b/.github/workflows/bun-test.yml new file mode 100644 index 0000000..f2221b9 --- /dev/null +++ b/.github/workflows/bun-test.yml @@ -0,0 +1,19 @@ +name: Bun Tests + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - uses: oven-sh/setup-bun@v2 + with: + bun-version: latest + - run: bun install + - run: bun test diff --git a/.gitignore b/.gitignore index ad67955..5f37029 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,10 @@ target # MSVC Windows builds of rustc generate these, which store debugging information *.pdb +# Bun +node_modules/ +bun.lockb + # Generated by cargo mutants # Contains mutation testing data **/mutants.out*/ diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..fcf307b --- /dev/null +++ b/.npmignore @@ -0,0 +1,19 @@ +# Rust files +src/*.rs +Cargo.toml +Cargo.lock +target/ +rustfmt.toml + +# CI/Config +.github/ +docs/ +CHANGELOG.md +.tool-versions +.release-please-manifest.json +release-please-config.json +renovate.json + +# LLM stuff +.roo/ +AGENTS.md diff --git a/CHANGELOG.md b/CHANGELOG.md index c579347..7fbbca5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,41 +7,41 @@ * add collection and session sqlite configurations ([#18](https://github.com/dimensionalpocket/dps-config/issues/18)) ([8b719ad](https://github.com/dimensionalpocket/dps-config/commit/8b719add5c44a612106595fdc125646eeb38d7ac)) -## [0.5.0](https://github.com/dimensionalpocket/dps-config-rs/compare/0.4.0...0.5.0) (2025-11-26) +## [0.5.0](https://github.com/dimensionalpocket/dps-config/compare/0.4.0...0.5.0) (2025-11-26) ### Features -* change auth_api_session_ttl_seconds type from u64 to u32 ([#13](https://github.com/dimensionalpocket/dps-config-rs/issues/13)) ([18e3960](https://github.com/dimensionalpocket/dps-config-rs/commit/18e396023c94805a12036b763b094c5cd2e8e043)) +* change auth_api_session_ttl_seconds type from u64 to u32 ([#13](https://github.com/dimensionalpocket/dps-config/issues/13)) ([18e3960](https://github.com/dimensionalpocket/dps-config/commit/18e396023c94805a12036b763b094c5cd2e8e043)) -## [0.4.0](https://github.com/dimensionalpocket/dps-config-rs/compare/0.3.0...0.4.0) (2025-11-25) +## [0.4.0](https://github.com/dimensionalpocket/dps-config/compare/0.3.0...0.4.0) (2025-11-25) ### ⚠ BREAKING CHANGES -* remove api_subdomain and introduce api_path configuration ([#11](https://github.com/dimensionalpocket/dps-config-rs/issues/11)) +* remove api_subdomain and introduce api_path configuration ([#11](https://github.com/dimensionalpocket/dps-config/issues/11)) ### Features -* remove api_subdomain and introduce api_path configuration ([#11](https://github.com/dimensionalpocket/dps-config-rs/issues/11)) ([091b0f5](https://github.com/dimensionalpocket/dps-config-rs/commit/091b0f5e33456f8021f9dbfdba1617e79275b1cb)) +* remove api_subdomain and introduce api_path configuration ([#11](https://github.com/dimensionalpocket/dps-config/issues/11)) ([091b0f5](https://github.com/dimensionalpocket/dps-config/commit/091b0f5e33456f8021f9dbfdba1617e79275b1cb)) -## [0.3.0](https://github.com/dimensionalpocket/dps-config-rs/compare/0.2.0...0.3.0) (2025-11-15) +## [0.3.0](https://github.com/dimensionalpocket/dps-config/compare/0.2.0...0.3.0) (2025-11-15) ### Features -* new properties ([#8](https://github.com/dimensionalpocket/dps-config-rs/issues/8)) ([51256f7](https://github.com/dimensionalpocket/dps-config-rs/commit/51256f72f8d9d3c1805e21a5e058d586a8edec7e)) +* new properties ([#8](https://github.com/dimensionalpocket/dps-config/issues/8)) ([51256f7](https://github.com/dimensionalpocket/dps-config/commit/51256f72f8d9d3c1805e21a5e058d586a8edec7e)) -## [0.2.0](https://github.com/dimensionalpocket/dps-config-rs/compare/0.1.0...0.2.0) (2025-11-14) +## [0.2.0](https://github.com/dimensionalpocket/dps-config/compare/0.1.0...0.2.0) (2025-11-14) ### Features -* add auth_api_session_ttl_seconds ([#4](https://github.com/dimensionalpocket/dps-config-rs/issues/4)) ([1a7104a](https://github.com/dimensionalpocket/dps-config-rs/commit/1a7104a4600529d1685146e13fa2b726b4faec55)) +* add auth_api_session_ttl_seconds ([#4](https://github.com/dimensionalpocket/dps-config/issues/4)) ([1a7104a](https://github.com/dimensionalpocket/dps-config/commit/1a7104a4600529d1685146e13fa2b726b4faec55)) -## [0.1.0](https://github.com/dimensionalpocket/dps-config-rs/compare/0.0.1...0.1.0) (2025-11-13) +## [0.1.0](https://github.com/dimensionalpocket/dps-config/compare/0.0.1...0.1.0) (2025-11-13) ### Features -* initial release ([6aeccb0](https://github.com/dimensionalpocket/dps-config-rs/commit/6aeccb0fdb7486b5fe53425591dc9a151dbd8a03)) +* initial release ([6aeccb0](https://github.com/dimensionalpocket/dps-config/commit/6aeccb0fdb7486b5fe53425591dc9a151dbd8a03)) diff --git a/Cargo.toml b/Cargo.toml index 4945d0a..5f23e3d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,15 @@ edition = "2021" authors = ["Dimensional Pocket"] license = "MIT" description = "Configuration management for the DPS ecosystem" -repository = "https://github.com/dimensionalpocket/dps-config-rs" +repository = "https://github.com/dimensionalpocket/dps-config" +exclude = [ + "src/*.ts", + "package.json", + "bun.lockb", + "node_modules/", + ".github/", + ".npmignore" +] [lib] name = "dps_config" diff --git a/README.md b/README.md index b7955b3..f9c80dc 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # @dimensionalpocket/dps-config -[![Rust Tests](https://github.com/dimensionalpocket/dps-config-rs/actions/workflows/test.yml/badge.svg)](https://github.com/dimensionalpocket/dps-config-rs/actions/workflows/test.yml) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) +[![Rust Tests](https://github.com/dimensionalpocket/dps-config/actions/workflows/test.yml/badge.svg)](https://github.com/dimensionalpocket/dps-config/actions/workflows/test.yml) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) Configuration management for the [DPS ecosystem](https://github.com/dimensionalpocket/dps-readme). @@ -18,22 +18,30 @@ Key principles: ## Installation +### Rust + Add it to `Cargo.toml`: ```toml [dependencies] -dps-config = { git = "https://github.com/dimensionalpocket/dps-config-rs" } +dps-config = { git = "https://github.com/dimensionalpocket/dps-config" } ``` Or add via cargo: ```bash -cargo add --git https://github.com/dimensionalpocket/dps-config-rs dps-config +cargo add --git https://github.com/dimensionalpocket/dps-config dps-config +``` + +### Bun + +```bash +bun add @dimensionalpocket/dps-config ``` ## Quick Start -Basic usage example: +### Rust ```rust use dps_config::DpsConfig; @@ -55,6 +63,29 @@ fn main() { } ``` +### Bun / TypeScript + +```typescript +import { DpsConfig } from "@dimensionalpocket/dps-config"; + +const config = new DpsConfig(); + +// defaults +const domain = config.getDomain(); +const apiPath = config.getApiPath(); + +// overrides +config.setDomain("example.com"); +config.setApiPath("v1"); +config.setDevelopmentMode(true); + +const authApiUrl = config.getAuthApiUrl(); +console.log(`Auth API URL: ${authApiUrl}`); + +// Vite support (loads environment variables with VITE_ prefix) +const viteConfig = new DpsConfig("VITE_"); +``` + ## Configuration Properties The following properties are provided. Properties load from environment variables when present. @@ -164,10 +195,13 @@ mod tests { ## Project Structure ``` -dps-config-rs/ -├── src/ # main library code with DpsConfig +dps-config/ +├── src/ +│ ├── index.ts # Bun / TypeScript implementation +│ └── lib.rs # Rust implementation ├── docs/ # documentation (LLM instructions, plans, drafts, etc.) -├── Cargo.toml +├── Cargo.toml # Rust package manifest +├── package.json # Bun package manifest └── README.md ``` diff --git a/docs/llm/plans/2025/11/13-implementation-plan.md b/docs/llm/plans/2025/11/13-implementation-plan.md index 3a2cb82..ce4cd56 100644 --- a/docs/llm/plans/2025/11/13-implementation-plan.md +++ b/docs/llm/plans/2025/11/13-implementation-plan.md @@ -1,4 +1,4 @@ -# Implementation Plan for dps-config-rs +# Implementation Plan for dps-config ## Overview @@ -53,7 +53,7 @@ edition = "2021" authors = ["Dimensional Pocket"] license = "MIT" description = "Configuration management for the DPS ecosystem" -repository = "https://github.com/dimensionalpocket/dps-config-rs" +repository = "https://github.com/dimensionalpocket/dps-config" keywords = ["config", "configuration", "dps"] categories = ["config"] diff --git a/docs/llm/plans/2025/11/13-readme-plan.md b/docs/llm/plans/2025/11/13-readme-plan.md index 68e4d25..df38291 100644 --- a/docs/llm/plans/2025/11/13-readme-plan.md +++ b/docs/llm/plans/2025/11/13-readme-plan.md @@ -1,8 +1,8 @@ -# README Plan for dps-config-rs +# README Plan for dps-config ## Overview -This plan outlines the structure and content for the comprehensive README.md file for the `dps-config-rs` repository. The README will serve as the primary documentation for the DpsConfig struct, a configuration management component used across the DPS ecosystem. +This plan outlines the structure and content for the comprehensive README.md file for the `dps-config` repository. The README will serve as the primary documentation for the DpsConfig struct, a configuration management component used across the DPS ecosystem. For more information about the DPS ecosystem, see the [DPS README](https://github.com/dimensionalpocket/dps-readme). @@ -17,13 +17,13 @@ For more information about the DPS ecosystem, see the [DPS README](https://githu ### 1. Header Section **Content:** -- Project title: `# dps-config-rs` +- Project title: `# dps-config` - Brief one-line description - Badges (if applicable): build status, crates.io version, license, etc. **Example:** ```markdown -# dps-config-rs +# dps-config Configuration management for the DPS ecosystem @@ -35,7 +35,7 @@ Configuration management for the DPS ecosystem ### 2. Overview **Content:** -- What is `dps-config-rs` +- What is `dps-config` - Its role in the DPS ecosystem - Key design principles (no validation, optional values, default values in getters) @@ -43,7 +43,7 @@ Configuration management for the DPS ecosystem ```markdown ## Overview -`dps-config-rs` provides the `DpsConfig` struct, a centralized configuration management +`dps-config` provides the `DpsConfig` struct, a centralized configuration management solution for Rust-based components in the DPS ecosystem. It handles configuration for multiple services including: @@ -76,13 +76,13 @@ Add this to your `Cargo.toml`: ```toml [dependencies] -dps-config = { git = "https://github.com/dimensionalpocket/dps-config-rs" } +dps-config = { git = "https://github.com/dimensionalpocket/dps-config" } ``` Or using cargo: ```bash -cargo add --git https://github.com/dimensionalpocket/dps-config-rs dps-config +cargo add --git https://github.com/dimensionalpocket/dps-config dps-config ``` ``` @@ -401,7 +401,7 @@ mise exec -- cargo test ### Project Structure ``` -dps-config-rs/ +dps-config/ ├── src/ │ └── lib.rs # Main library code with DpsConfig struct ├── docs/ # Documentation (LLM instructions, plans, drafts, etc.) diff --git a/package.json b/package.json new file mode 100644 index 0000000..1ff2466 --- /dev/null +++ b/package.json @@ -0,0 +1,21 @@ +{ + "name": "@dimensionalpocket/dps-config", + "version": "0.6.0", + "description": "Configuration management for the DPS ecosystem", + "main": "src/index.ts", + "type": "module", + "scripts": { + "test": "bun test" + }, + "keywords": [ + "config", + "dps", + "typescript" + ], + "author": "Dimensional Pocket", + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/dimensionalpocket/dps-config.git" + } +} diff --git a/release-please-config.json b/release-please-config.json index b18f8bb..564dc47 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -3,7 +3,14 @@ ".": { "release-type": "rust", "include-v-in-tag": false, - "include-component-in-tag": false + "include-component-in-tag": false, + "extra-files": [ + { + "type": "json", + "path": "package.json", + "jsonpath": "$.version" + } + ] } } } diff --git a/src/index.test.ts b/src/index.test.ts new file mode 100644 index 0000000..8ee3748 --- /dev/null +++ b/src/index.test.ts @@ -0,0 +1,130 @@ +import { describe, it, expect, afterEach } from "bun:test"; +import { DpsConfig } from "./index"; + +describe("DpsConfig", () => { + afterEach(() => { + // Clean up environment variables after each test + const keysToRemove = Object.keys(process.env).filter(key => key.startsWith("DPS_") || key.startsWith("VITE_DPS_")); + for (const key of keysToRemove) { + delete process.env[key]; + } + }); + + it("should have correct default values", () => { + const config = new DpsConfig(); + expect(config.getDomain()).toBe("dps.localhost"); + expect(config.getApiPath()).toBe("api"); + expect(config.getDevelopmentMode()).toBe(false); + expect(config.getAuthApiSubdomain()).toBe("auth"); + expect(config.getAuthApiProtocol()).toBe("https"); + expect(config.getAuthApiInsecureCookie()).toBe(false); + expect(config.getAuthApiSqliteMainFilePath()).toBe("data/main-development.db"); + expect(config.getAuthApiSqliteMainPoolSize()).toBe(1); + expect(config.getAuthApiSqliteCollectionFilePath()).toBe("data/collection-development.db"); + expect(config.getAuthApiSqliteCollectionPoolSize()).toBe(1); + expect(config.getAuthApiSqliteSessionFilePath()).toBe("data/session-development.db"); + expect(config.getAuthApiSqliteSessionPoolSize()).toBe(1); + expect(config.getAuthApiPort()).toBeUndefined(); + expect(config.getAuthApiSessionSecret()).toBeUndefined(); + expect(config.getAuthApiSessionSecretBytes()).toBeUndefined(); + }); + + it("should work with setters", () => { + const config = new DpsConfig(); + config.setDomain("example.com"); + config.setApiPath("v1"); + config.setDevelopmentMode(true); + config.setAuthApiPort(3000); + config.setAuthApiSessionSecret("s3cr3t"); + + expect(config.getDomain()).toBe("example.com"); + expect(config.getApiPath()).toBe("v1"); + expect(config.getDevelopmentMode()).toBe(true); + expect(config.getAuthApiPort()).toBe(3000); + expect(config.getAuthApiSessionSecret()).toBe("s3cr3t"); + }); + + it("should build auth API URL without port", () => { + const config = new DpsConfig(); + config.setAuthApiProtocol("https"); + config.setAuthApiSubdomain("auth"); + config.setDomain("dps.localhost"); + expect(config.getAuthApiUrl()).toBe("https://auth.dps.localhost/api"); + }); + + it("should build auth API URL with port", () => { + const config = new DpsConfig(); + config.setAuthApiProtocol("http"); + config.setAuthApiPort(3000); + config.setAuthApiSubdomain("auth"); + config.setDomain("dps.localhost"); + expect(config.getAuthApiUrl()).toBe("http://auth.dps.localhost:3000/api"); + }); + + it("should follow README example", () => { + const config = new DpsConfig(); + config.setDomain("test.local"); + config.setAuthApiProtocol("http"); + config.setAuthApiPort(8080); + expect(config.getAuthApiUrl()).toBe("http://auth.test.local:8080/api"); + }); + + it("should load insecure cookie setting from env", () => { + const c1 = new DpsConfig(); + expect(c1.getAuthApiInsecureCookie()).toBe(false); + c1.setAuthApiInsecureCookie(true); + expect(c1.getAuthApiInsecureCookie()).toBe(true); + + process.env.DPS_AUTH_API_INSECURE_COOKIE = "Y"; + const c2 = new DpsConfig(); + expect(c2.getAuthApiInsecureCookie()).toBe(true); + }); + + it("should load SQLite pool sizes from env", () => { + process.env.DPS_AUTH_API_SQLITE_MAIN_POOL_SIZE = "8"; + process.env.DPS_AUTH_API_SQLITE_COLLECTION_POOL_SIZE = "4"; + process.env.DPS_AUTH_API_SQLITE_SESSION_POOL_SIZE = "2"; + + const config = new DpsConfig(); + expect(config.getAuthApiSqliteMainPoolSize()).toBe(8); + expect(config.getAuthApiSqliteCollectionPoolSize()).toBe(4); + expect(config.getAuthApiSqliteSessionPoolSize()).toBe(2); + }); + + it("should load SQLite file paths from env", () => { + process.env.DPS_AUTH_API_SQLITE_MAIN_FILE_PATH = "data/test-main.db"; + const config = new DpsConfig(); + expect(config.getAuthApiSqliteMainFilePath()).toBe("data/test-main.db"); + }); + + it("should return session secret bytes", () => { + const config = new DpsConfig(); + config.setAuthApiSessionSecret("my-secret-key"); + const bytes = config.getAuthApiSessionSecretBytes(); + expect(bytes).toBeDefined(); + expect(new TextDecoder().decode(bytes)).toBe("my-secret-key"); + }); + + it("should load session TTL from env", () => { + process.env.DPS_AUTH_API_SESSION_TTL_SECONDS = "1800"; + const config = new DpsConfig(); + expect(config.getAuthApiSessionTtlSeconds()).toBe(1800); + }); + + it("should load API path from env", () => { + process.env.DPS_API_PATH = "api/v2"; + const config = new DpsConfig(); + expect(config.getApiPath()).toBe("api/v2"); + }); + + it("should support envPrefix", () => { + process.env.VITE_DPS_DOMAIN = "vite.local"; + process.env.DPS_DOMAIN = "standard.local"; + + const config = new DpsConfig("VITE_"); + expect(config.getDomain()).toBe("vite.local"); + + const standardConfig = new DpsConfig(); + expect(standardConfig.getDomain()).toBe("standard.local"); + }); +}); diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..e764e64 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,223 @@ +/** + * Configuration management for the DPS ecosystem. + * + * This class provides a lightweight configuration container mirroring the Rust implementation. + * It focuses on optional values, sensible defaults, and environment variable loading. + */ +export class DpsConfig { + private domain?: string; + private apiPath?: string; + private developmentMode?: boolean; + + private authApiSubdomain?: string; + private authApiPort?: number; + private authApiProtocol?: string; + private authApiInsecureCookie?: boolean; + private authApiSqliteMainFilePath?: string; + private authApiSqliteMainPoolSize?: number; + private authApiSqliteCollectionFilePath?: string; + private authApiSqliteCollectionPoolSize?: number; + private authApiSqliteSessionFilePath?: string; + private authApiSqliteSessionPoolSize?: number; + private authApiSessionSecret?: string; + private authApiSessionTtlSeconds?: number; + + /** + * Create a new DpsConfig instance, loading values from environment variables when present. + * + * @param envPrefix Optional prefix for environment variables (e.g. "VITE_" for Vite support). + */ + constructor(envPrefix: string = "") { + this.domain = this.loadEnvString(envPrefix, "DPS_DOMAIN"); + this.apiPath = this.loadEnvString(envPrefix, "DPS_API_PATH"); + this.developmentMode = this.loadEnvBool(envPrefix, "DPS_DEVELOPMENT_MODE"); + + this.authApiSubdomain = this.loadEnvString(envPrefix, "DPS_AUTH_API_SUBDOMAIN"); + this.authApiPort = this.loadEnvNumber(envPrefix, "DPS_AUTH_API_PORT"); + this.authApiProtocol = this.loadEnvString(envPrefix, "DPS_AUTH_API_PROTOCOL"); + this.authApiInsecureCookie = this.loadEnvBool(envPrefix, "DPS_AUTH_API_INSECURE_COOKIE"); + this.authApiSqliteMainFilePath = this.loadEnvString(envPrefix, "DPS_AUTH_API_SQLITE_MAIN_FILE_PATH"); + this.authApiSqliteMainPoolSize = this.loadEnvNumber(envPrefix, "DPS_AUTH_API_SQLITE_MAIN_POOL_SIZE"); + this.authApiSqliteCollectionFilePath = this.loadEnvString(envPrefix, "DPS_AUTH_API_SQLITE_COLLECTION_FILE_PATH"); + this.authApiSqliteCollectionPoolSize = this.loadEnvNumber(envPrefix, "DPS_AUTH_API_SQLITE_COLLECTION_POOL_SIZE"); + this.authApiSqliteSessionFilePath = this.loadEnvString(envPrefix, "DPS_AUTH_API_SQLITE_SESSION_FILE_PATH"); + this.authApiSqliteSessionPoolSize = this.loadEnvNumber(envPrefix, "DPS_AUTH_API_SQLITE_SESSION_POOL_SIZE"); + this.authApiSessionSecret = this.loadEnvString(envPrefix, "DPS_AUTH_API_SESSION_SECRET"); + this.authApiSessionTtlSeconds = this.loadEnvNumber(envPrefix, "DPS_AUTH_API_SESSION_TTL_SECONDS"); + } + + // -------------------- + // Global getters/setters + // -------------------- + + getDomain(): string { + return this.domain ?? "dps.localhost"; + } + + setDomain(value: string) { + this.domain = value; + } + + getApiPath(): string { + return this.apiPath ?? "api"; + } + + setApiPath(value: string) { + this.apiPath = value; + } + + getDevelopmentMode(): boolean { + return this.developmentMode ?? false; + } + + setDevelopmentMode(value: boolean) { + this.developmentMode = value; + } + + // -------------------- + // DpsAuthApi getters/setters + // -------------------- + + getAuthApiSubdomain(): string { + return this.authApiSubdomain ?? "auth"; + } + + setAuthApiSubdomain(value: string) { + this.authApiSubdomain = value; + } + + getAuthApiPort(): number | undefined { + return this.authApiPort; + } + + setAuthApiPort(value: number | undefined) { + this.authApiPort = value; + } + + getAuthApiProtocol(): string { + return this.authApiProtocol ?? "https"; + } + + setAuthApiProtocol(value: string) { + this.authApiProtocol = value; + } + + getAuthApiInsecureCookie(): boolean { + return this.authApiInsecureCookie ?? false; + } + + setAuthApiInsecureCookie(value: boolean) { + this.authApiInsecureCookie = value; + } + + getAuthApiSqliteMainFilePath(): string { + return this.authApiSqliteMainFilePath ?? "data/main-development.db"; + } + + setAuthApiSqliteMainFilePath(value: string) { + this.authApiSqliteMainFilePath = value; + } + + getAuthApiSqliteMainPoolSize(): number { + return this.authApiSqliteMainPoolSize ?? 1; + } + + setAuthApiSqliteMainPoolSize(value: number | undefined) { + this.authApiSqliteMainPoolSize = value; + } + + getAuthApiSqliteCollectionFilePath(): string { + return this.authApiSqliteCollectionFilePath ?? "data/collection-development.db"; + } + + setAuthApiSqliteCollectionFilePath(value: string) { + this.authApiSqliteCollectionFilePath = value; + } + + getAuthApiSqliteCollectionPoolSize(): number { + return this.authApiSqliteCollectionPoolSize ?? 1; + } + + setAuthApiSqliteCollectionPoolSize(value: number | undefined) { + this.authApiSqliteCollectionPoolSize = value; + } + + getAuthApiSqliteSessionFilePath(): string { + return this.authApiSqliteSessionFilePath ?? "data/session-development.db"; + } + + setAuthApiSqliteSessionFilePath(value: string) { + this.authApiSqliteSessionFilePath = value; + } + + getAuthApiSqliteSessionPoolSize(): number { + return this.authApiSqliteSessionPoolSize ?? 1; + } + + setAuthApiSqliteSessionPoolSize(value: number | undefined) { + this.authApiSqliteSessionPoolSize = value; + } + + getAuthApiSessionSecret(): string | undefined { + return this.authApiSessionSecret; + } + + setAuthApiSessionSecret(value: string | undefined) { + this.authApiSessionSecret = value; + } + + /** + * Returns the auth API session secret as bytes (Uint8Array), if configured. + */ + getAuthApiSessionSecretBytes(): Uint8Array | undefined { + if (this.authApiSessionSecret === undefined) { + return undefined; + } + return new TextEncoder().encode(this.authApiSessionSecret); + } + + getAuthApiSessionTtlSeconds(): number { + return this.authApiSessionTtlSeconds ?? 1209600; + } + + setAuthApiSessionTtlSeconds(value: number | undefined) { + this.authApiSessionTtlSeconds = value; + } + + // -------------------- + // Computed getters + // -------------------- + + getAuthApiUrl(): string { + const protocol = this.getAuthApiProtocol(); + const authSub = this.getAuthApiSubdomain(); + const domain = this.getDomain(); + const apiPath = this.getApiPath(); + const port = this.getAuthApiPort(); + const portString = port ? `:${port}` : ""; + return `${protocol}://${authSub}.${domain}${portString}/${apiPath}`; + } + + // -------------------- + // Helper methods + // -------------------- + + private loadEnvString(prefix: string, key: string): string | undefined { + const value = process.env[prefix + key]; + return (value && value.length > 0) ? value : undefined; + } + + private loadEnvBool(prefix: string, key: string): boolean | undefined { + const value = process.env[prefix + key]; + return value === "Y" ? true : (value === undefined ? undefined : false); + } + + private loadEnvNumber(prefix: string, key: string): number | undefined { + const value = process.env[prefix + key]; + if (value === undefined || value.length === 0) { + return undefined; + } + const parsed = parseInt(value, 10); + return isNaN(parsed) ? undefined : parsed; + } +}