npm package: @linplatform/lin-configuration
Description: Dynamic remote configuration loader for Node.js applications.
Author: LIN Platform
Latest version: 1.0.0

# @linplatform/lin-configuration

Dynamic remote configuration loader for Node.js applications. It fetches a remote JSON configuration and maps each value into `process.env`, inspired by `.NET IConfigurationProvider`.

## Features

- Native `fetch` on Node.js 18+
- TypeScript-first API
- Optional in-memory cache
- Optional auto-refresh
- Optional debug logs
- Response validation and robust error handling
- `overrideExisting` support
- Optional key prefix
- Optional key transform hook
- Optional validation hook per entry
- Dual package export for ESM and CommonJS

## Installation

```bash
npm install @linplatform/lin-configuration
```

## Quick Start

### ESM / TypeScript

```ts
import { loadRemoteConfig } from "@linplatform/lin-configuration";

await loadRemoteConfig({
  apiKey: "MI_API_KEY",
  overrideExisting: true,
});

console.log(process.env.MI_VARIABLE);
```

### CommonJS

```js
const { loadRemoteConfig } = require("@linplatform/lin-configuration");

async function bootstrap() {
  await loadRemoteConfig({
    apiKey: "MI_API_KEY",
    overrideExisting: true,
  });

  console.log(process.env.MI_VARIABLE);
}

bootstrap();
```

## API

### `loadRemoteConfig(options)`

Loads configuration from the remote API and assigns the valid entries to `process.env`.

```ts
import { loadRemoteConfig } from "@linplatform/lin-configuration";

await loadRemoteConfig({
  apiKey: "MI_API_KEY",
  url: "https://api.linplatform.com/configuration/api/configuration",
  overrideExisting: false,
  useCache: true,
  cacheTtlMs: 300000,
  refreshIntervalMs: 600000,
  debug: false,
  prefix: "APP_",
  transformKey: (key) => key.toUpperCase(),
  validateEntry: ({ key, value }) => key.length > 0 && value.length > 0,
});
```

### Options

| Option | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `apiKey` | `string` | Yes | - | API key sent in the `key` header. |
| `url` | `string` | No | `https://api.linplatform.com/configuration/api/configuration` | Remote endpoint to fetch configuration from. |
| `overrideExisting` | `boolean` | No | `false` | When `true`, existing `process.env` values are overwritten. |
| `useCache` | `boolean` | No | `true` | Enables in-memory response cache. |
| `cacheTtlMs` | `number` | No | `300000` | Cache lifetime in milliseconds. Use `0` to keep cache forever during process lifetime. |
| `refreshIntervalMs` | `number` | No | `undefined` | Enables periodic reload using `setInterval`. |
| `debug` | `boolean` | No | `false` | Emits debug logs through `console.debug`. |
| `prefix` | `string` | No | `""` | Prefix applied to every environment variable name. |
| `transformKey` | `(key: string) => string` | No | - | Transforms each remote key before assignment. |
| `validateEntry` | `(entry) => boolean \| void \| Promise<boolean \| void>` | No | - | Called before assigning each entry. Return `false` to skip the key. |

## Response Shape

Expected remote response:

```json
{
  "KEY1": "value1",
  "KEY2": "value2"
}
```

Behavior:

- `null` and `undefined` values are ignored
- Non-string values throw an error
- Invalid JSON throws an error
- Non-2xx responses throw an error

## Examples

### Prefix and key normalization

```ts
import { loadRemoteConfig } from "@linplatform/lin-configuration";

await loadRemoteConfig({
  apiKey: "MI_API_KEY",
  prefix: "APP_",
  transformKey: (key) => key.toUpperCase(),
});

console.log(process.env.APP_DATABASE_URL);
```

### Skip existing environment variables

```ts
process.env.PORT = "3000";

await loadRemoteConfig({
  apiKey: "MI_API_KEY",
  overrideExisting: false,
});

console.log(process.env.PORT); // keeps "3000"
```

### Auto-refresh every 5 minutes

```ts
import {
  loadRemoteConfig,
  stopRemoteConfigAutoRefresh,
} from "@linplatform/lin-configuration";

await loadRemoteConfig({
  apiKey: "MI_API_KEY",
  refreshIntervalMs: 5 * 60 * 1000,
  overrideExisting: true,
});

// Later, if needed:
stopRemoteConfigAutoRefresh({ apiKey: "MI_API_KEY" });
```

### Custom validation hook

```ts
await loadRemoteConfig({
  apiKey: "MI_API_KEY",
  validateEntry: ({ key, value }) => {
    if (!key.startsWith("APP_")) {
      return false;
    }

    return value.trim().length > 0;
  },
});
```

## Additional Utilities

### `clearRemoteConfigCache()`

Clears the in-memory cache.

### `stopRemoteConfigAutoRefresh(options?)`

Stops one refresh timer or all refresh timers.

```ts
import { stopRemoteConfigAutoRefresh } from "@linplatform/lin-configuration";

stopRemoteConfigAutoRefresh({ apiKey: "MI_API_KEY" });
stopRemoteConfigAutoRefresh();
```

### `getRemoteConfigCacheSnapshot()`

Returns a read-only snapshot of cached entries.

## Error Handling

The package throws `RemoteConfigError` for:

- Missing or invalid `apiKey`
- Invalid `url`
- HTTP request failures
- Non-success HTTP status codes
- Invalid JSON
- Invalid response shape
- Invalid refresh or cache options

Example:

```ts
import { loadRemoteConfig, RemoteConfigError } from "@linplatform/lin-configuration";

try {
  await loadRemoteConfig({ apiKey: "MI_API_KEY" });
} catch (error) {
  if (error instanceof RemoteConfigError) {
    console.error(error.message);
  }
}
```

## Build

```bash
npm run build
```

The build generates:

- `dist/esm`
- `dist/cjs`
- `dist/types`

## License

MIT