🚀 Socket Launch Week Day 5:Introducing Repository Access Permissions and Custom Roles.Learn more
Sign In

@fluojs/config

Package Overview
Dependencies
Maintainers
1
Versions
12
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@fluojs/config - npm Package Compare versions

Comparing version
1.0.0-beta.3
to
1.0.0-beta.4
+6
-0
dist/clone.d.ts

@@ -0,2 +1,8 @@

/**
* Clone config dictionary.
*
* @param value The value.
* @returns The clone config dictionary result.
*/
export declare function cloneConfigDictionary<T>(value: T): T;
//# sourceMappingURL=clone.d.ts.map
+1
-1

@@ -1,1 +0,1 @@

{"version":3,"file":"clone.d.ts","sourceRoot":"","sources":["../src/clone.ts"],"names":[],"mappings":"AAEA,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAMpD"}
{"version":3,"file":"clone.d.ts","sourceRoot":"","sources":["../src/clone.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAMpD"}
import { fallbackClone } from '@fluojs/core/internal';
/**
* Clone config dictionary.
*
* @param value The value.
* @returns The clone config dictionary result.
*/
export function cloneConfigDictionary(value) {

@@ -3,0 +10,0 @@ try {

@@ -5,3 +5,3 @@ import type { ConfigDictionary, ConfigLoadOptions, ConfigReloader } from './types.js';

*
* @param options Configuration loading options, including optional watch mode and validation hooks.
* @param options Configuration loading options, including optional watch mode and a synchronous Standard Schema validator.
* @returns A reloader that exposes the current snapshot, manual reload, subscriptions, and cleanup.

@@ -29,3 +29,3 @@ * @throws {FluoError} When the initial config load or validation fails.

*
* @param options Configuration loading options for source precedence, parsing, and validation.
* @param options Configuration loading options for source precedence, parsing, and synchronous schema validation.
* @returns A detached normalized configuration dictionary for the current load.

@@ -32,0 +32,0 @@ * @throws {FluoError} When validation throws or the config cannot be normalized.

@@ -1,1 +0,1 @@

{"version":3,"file":"load.d.ts","sourceRoot":"","sources":["../src/load.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EACV,gBAAgB,EAChB,iBAAiB,EAEjB,cAAc,EAIf,MAAM,YAAY,CAAC;AAqQpB;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,iBAAiB,GAAG,cAAc,CA8B/E;AAED;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,gBAAgB,CAEvE"}
{"version":3,"file":"load.d.ts","sourceRoot":"","sources":["../src/load.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EACV,gBAAgB,EAChB,iBAAiB,EAEjB,cAAc,EAKf,MAAM,YAAY,CAAC;AA0XpB;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,iBAAiB,GAAG,cAAc,CA8B/E;AAED;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,gBAAgB,CAEvE"}

@@ -35,3 +35,12 @@ import { existsSync, readFileSync, watch } from 'node:fs';

}
function rejectLegacyValidateOption(options) {
if ('validate' in options) {
throw new FluoError('Invalid configuration.', {
code: 'INVALID_CONFIG',
cause: new Error('The legacy `validate` option was removed. Use `schema` with a synchronous Standard Schema validator instead.')
});
}
}
function normalizeLoadOptions(options) {
rejectLegacyValidateOption(options);
const cwd = options.cwd ?? process.cwd();

@@ -49,3 +58,3 @@ const envFile = options.envFilePath ?? options.envFile ?? join(cwd, '.env');

safeProcessEnv,
validate: options.validate
schema: options.schema
};

@@ -92,10 +101,72 @@ }

}
function isPromiseLike(value) {
return (typeof value === 'object' || typeof value === 'function') && value !== null && 'then' in value && typeof value.then === 'function';
}
function isConfigSchemaIssue(value) {
return typeof value === 'object' && value !== null && 'message' in value && typeof value.message === 'string';
}
function isConfigSchemaFailureResult(value) {
if (typeof value !== 'object' || value === null || !('issues' in value)) {
return false;
}
return Array.isArray(value.issues) && value.issues.every(isConfigSchemaIssue);
}
function isConfigSchemaSuccessResult(value) {
return typeof value === 'object' && value !== null && 'value' in value && isPlainObject(value.value);
}
function isConfigSchemaPathKeySegment(value) {
if (typeof value !== 'object' || value === null || !('key' in value)) {
return false;
}
return typeof value.key === 'string' || typeof value.key === 'number' || typeof value.key === 'symbol';
}
function formatConfigSchemaPathSegment(segment) {
if (typeof segment === 'string' || typeof segment === 'number') {
return String(segment);
}
if (isConfigSchemaPathKeySegment(segment)) {
return String(segment.key);
}
return undefined;
}
function formatConfigSchemaIssue(issue) {
const path = issue.path?.map(formatConfigSchemaPathSegment).filter(segment => segment !== undefined).join('.');
return path && path.length > 0 ? `${path}: ${issue.message}` : issue.message;
}
function createInvalidConfigError(cause, issues) {
return new FluoError('Invalid configuration.', {
code: 'INVALID_CONFIG',
cause,
meta: issues ? {
issues: issues.map(formatConfigSchemaIssue)
} : undefined
});
}
function isInvalidConfigError(error) {
return error instanceof FluoError && error.code === 'INVALID_CONFIG';
}
function readConfigSchemaResult(result) {
if (isConfigSchemaFailureResult(result)) {
throw createInvalidConfigError(new Error('Standard Schema config validation failed.'), result.issues);
}
if (!isConfigSchemaSuccessResult(result)) {
throw createInvalidConfigError(new Error('Standard Schema config validator returned a malformed result.'));
}
return result.value;
}
function validateConfig(options, merged) {
if (!options.schema) {
return merged;
}
try {
return options.validate ? options.validate(merged) : merged;
const result = options.schema['~standard'].validate(merged);
if (isPromiseLike(result)) {
throw new Error('Config schemas must validate synchronously. Async Standard Schema validation is not supported by the synchronous config API.');
}
return readConfigSchemaResult(result);
} catch (error) {
throw new FluoError('Invalid configuration.', {
code: 'INVALID_CONFIG',
cause: error
});
if (isInvalidConfigError(error)) {
throw error;
}
throw createInvalidConfigError(error);
}

@@ -186,3 +257,3 @@ }

*
* @param options Configuration loading options, including optional watch mode and validation hooks.
* @param options Configuration loading options, including optional watch mode and a synchronous Standard Schema validator.
* @returns A reloader that exposes the current snapshot, manual reload, subscriptions, and cleanup.

@@ -239,3 +310,3 @@ * @throws {FluoError} When the initial config load or validation fails.

*
* @param options Configuration loading options for source precedence, parsing, and validation.
* @param options Configuration loading options for source precedence, parsing, and synchronous schema validation.
* @returns A detached normalized configuration dictionary for the current load.

@@ -242,0 +313,0 @@ * @throws {FluoError} When validation throws or the config cannot be normalized.

@@ -0,1 +1,2 @@

import type { StandardSchemaV1 } from '@standard-schema/spec';
/**

@@ -6,2 +7,9 @@ * Plain JSON-like object used as the normalized configuration snapshot shape.

/**
* Standard Schema v1-compatible config validator accepted by `@fluojs/config` loaders.
*
* @typeParam Input Raw merged config shape consumed by the schema validator.
* @typeParam Output Normalized config shape produced by the schema validator.
*/
export type ConfigSchema<Input = unknown, Output extends ConfigDictionary = ConfigDictionary> = StandardSchemaV1<Input, Output>;
/**
* Nested dot-path key helper.

@@ -24,3 +32,3 @@ * Produces "a" | "a.b" | "a.b.c" keys from a Record type.

processEnv?: NodeJS.ProcessEnv;
validate?: (raw: ConfigDictionary) => ConfigDictionary;
schema?: ConfigSchema;
defaults?: ConfigDictionary;

@@ -27,0 +35,0 @@ /** Supply a custom file parser (e.g. for YAML or TOML). Receives raw file content,

@@ -1,1 +0,1 @@

{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEvD;;;GAGG;AACH,MAAM,MAAM,QAAQ,CAAC,CAAC,EAAE,MAAM,SAAS,MAAM,GAAG,EAAE,IAAI,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACnF;KACG,CAAC,IAAI,MAAM,CAAC,GAAG,MAAM,GAClB,GAAG,MAAM,GAAG,CAAC,EAAE,GACf,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,MAAM,GAAG,CAAC,GAAG,CAAC;CACrC,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,GACnB,KAAK,CAAC;AAEV;;GAEG;AACH,MAAM,MAAM,QAAQ,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,IAAI,CAAC,SAAS,MAAM,CAAC,GACzD,CAAC,CAAC,CAAC,CAAC,GACJ,CAAC,SAAS,GAAG,MAAM,IAAI,IAAI,MAAM,IAAI,EAAE,GACrC,IAAI,SAAS,MAAM,CAAC,GAClB,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,GACvB,KAAK,GACP,KAAK,CAAC;AAEZ;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IAC/B,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,gBAAgB,KAAK,gBAAgB,CAAC;IACvD,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IAC5B;uEACmE;IACnE,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpD,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAkB,SAAQ,mBAAmB;IAC5D,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;CACrC;AAED;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEpD;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,QAAQ,EAAE,gBAAgB,EAAE,MAAM,EAAE,kBAAkB,KAAK,IAAI,CAAC;AAEpG;;GAEG;AACH,MAAM,MAAM,yBAAyB,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,kBAAkB,KAAK,IAAI,CAAC;AAE7F;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,WAAW,IAAI,IAAI,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,OAAO,IAAI,gBAAgB,CAAC;IAC5B,MAAM,IAAI,gBAAgB,CAAC;IAC3B,SAAS,CAAC,QAAQ,EAAE,oBAAoB,GAAG,wBAAwB,CAAC;IACpE,cAAc,CAAC,QAAQ,EAAE,yBAAyB,GAAG,wBAAwB,CAAC;IAC9E,KAAK,IAAI,IAAI,CAAC;CACf"}
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAE9D;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEvD;;;;;GAKG;AACH,MAAM,MAAM,YAAY,CAAC,KAAK,GAAG,OAAO,EAAE,MAAM,SAAS,gBAAgB,GAAG,gBAAgB,IAAI,gBAAgB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;AAEhI;;;GAGG;AACH,MAAM,MAAM,QAAQ,CAAC,CAAC,EAAE,MAAM,SAAS,MAAM,GAAG,EAAE,IAAI,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACnF;KACG,CAAC,IAAI,MAAM,CAAC,GAAG,MAAM,GAClB,GAAG,MAAM,GAAG,CAAC,EAAE,GACf,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,MAAM,GAAG,CAAC,GAAG,CAAC;CACrC,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,GACnB,KAAK,CAAC;AAEV;;GAEG;AACH,MAAM,MAAM,QAAQ,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,IAAI,CAAC,SAAS,MAAM,CAAC,GACzD,CAAC,CAAC,CAAC,CAAC,GACJ,CAAC,SAAS,GAAG,MAAM,IAAI,IAAI,MAAM,IAAI,EAAE,GACrC,IAAI,SAAS,MAAM,CAAC,GAClB,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,GACvB,KAAK,GACP,KAAK,CAAC;AAEZ;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IAC/B,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IAC5B;uEACmE;IACnE,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpD,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAkB,SAAQ,mBAAmB;IAC5D,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;CACrC;AAED;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEpD;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,QAAQ,EAAE,gBAAgB,EAAE,MAAM,EAAE,kBAAkB,KAAK,IAAI,CAAC;AAEpG;;GAEG;AACH,MAAM,MAAM,yBAAyB,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,kBAAkB,KAAK,IAAI,CAAC;AAE7F;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,WAAW,IAAI,IAAI,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,OAAO,IAAI,gBAAgB,CAAC;IAC5B,MAAM,IAAI,gBAAgB,CAAC;IAC3B,SAAS,CAAC,QAAQ,EAAE,oBAAoB,GAAG,wBAAwB,CAAC;IACpE,cAAc,CAAC,QAAQ,EAAE,yBAAyB,GAAG,wBAAwB,CAAC;IAC9E,KAAK,IAAI,IAAI,CAAC;CACf"}

@@ -11,3 +11,3 @@ {

],
"version": "1.0.0-beta.3",
"version": "1.0.0-beta.4",
"private": false,

@@ -39,2 +39,3 @@ "license": "MIT",

"dependencies": {
"@standard-schema/spec": "^1.1.0",
"dotenv": "^16.0.0",

@@ -41,0 +42,0 @@ "dotenv-expand": "^11.0.0",

@@ -35,3 +35,9 @@ # @fluojs/config

import { Module } from '@fluojs/core';
import { z } from 'zod';
const EnvSchema = z.object({
DATABASE_URL: z.string().url(),
PORT: z.coerce.number().default(3000),
});
@Module({

@@ -45,6 +51,3 @@ imports: [

defaults: { PORT: '3000' },
validate: (config) => {
if (!config.DATABASE_URL) throw new Error('DATABASE_URL이 필요합니다');
return config;
},
schema: EnvSchema,
}),

@@ -83,4 +86,6 @@ ],

`validate` 함수는 모든 소스가 합쳐진 뒤 실행되며, 에러를 던지면 부트스트랩이 즉시 중단됩니다.
`schema` 옵션은 Zod, Valibot, ArkType 같은 동기식 [Standard Schema](https://standardschema.dev/schema) 호환 validator를 받습니다. 스키마는 모든 소스가 합쳐진 뒤 실행되고, 검증된 `value`가 최종 config snapshot이 됩니다. schema issue가 보고되면 bootstrap/load/reload는 `INVALID_CONFIG`로 실패합니다.
`@fluojs/config`의 load와 reload API는 동기식입니다. 비동기 Standard Schema 결과는 `INVALID_CONFIG`로 거부되므로 config 검증에는 동기 스키마를 사용하세요.
### 런타임 접근과 리로드 비용 모델

@@ -109,3 +114,3 @@

- `@fluojs/runtime`: 부트스트랩 중 `loadConfig()`를 호출합니다.
- `@fluojs/validation`: `validate` 함수 안에서 스키마 기반 검증을 조합할 수 있습니다.
- Standard Schema validator: Zod, Valibot, ArkType 및 호환 schema 라이브러리를 `schema` 옵션으로 전달할 수 있습니다.

@@ -112,0 +117,0 @@ ## 예제 소스

@@ -38,3 +38,9 @@ # @fluojs/config

import { ConfigModule } from '@fluojs/config';
import { z } from 'zod';
const EnvSchema = z.object({
DATABASE_URL: z.string().url(),
PORT: z.coerce.number().default(3000),
});
@Module({

@@ -48,6 +54,3 @@ imports: [

defaults: { PORT: '3000' },
validate: (config) => {
if (!config.DATABASE_URL) throw new Error('DATABASE_URL is required');
return config;
},
schema: EnvSchema,
}),

@@ -88,4 +91,6 @@ ],

### Validation
The `validate` function runs after all sources are merged but before the application starts. If it throws, the application bootstrap fails immediately.
The `schema` option accepts a synchronous [Standard Schema](https://standardschema.dev/schema)-compatible validator such as Zod, Valibot, or ArkType. The schema runs after all sources are merged but before the application starts. Its validated `value` becomes the final config snapshot, and reported issues fail bootstrap/load/reload with `INVALID_CONFIG`.
`@fluojs/config` keeps loading and reload APIs synchronous. Async Standard Schema results are rejected with `INVALID_CONFIG`; use a synchronous schema for config validation.
### Runtime Access and Reload Cost Model

@@ -113,3 +118,3 @@ `ConfigService.get('a.b.c')` resolves dot-path keys by walking each path segment, so lookup cost is proportional to path depth. When `get()`, `getOrThrow()`, or `snapshot()` returns an object-like value, the returned value is a detached clone; clone cost is proportional to the returned subtree size so caller mutations cannot affect the active config snapshot.

- **`@fluojs/runtime`**: Calls `loadConfig` internally during application bootstrap.
- **`@fluojs/validation`**: Can be used within the `validate` function for schema-based validation.
- **Standard Schema validators**: Zod, Valibot, ArkType, and other compatible schema libraries can be passed through the `schema` option.

@@ -116,0 +121,0 @@ ## Example Sources