@fonderie/config 2.0.0 → 3.0.0

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.
package/README.md CHANGED
@@ -13,10 +13,10 @@ npm install @fonderie/config
13
13
 
14
14
  ```ts
15
15
  import { FonderieApp, defineConfig } from '@fonderie/core';
16
- import { RemoteConfigModule } from '@fonderie/config';
16
+ import { ConfigModule } from '@fonderie/config';
17
17
 
18
18
  const app = await new FonderieApp(defineConfig({}))
19
- .register(new RemoteConfigModule())
19
+ .register(new ConfigModule())
20
20
  .boot();
21
21
  ```
22
22
 
@@ -7,12 +7,12 @@
7
7
  Subpath exports: `@fonderie/config/types`, `@fonderie/config/middleware`, `@fonderie/config/migrations`
8
8
 
9
9
  ```ts
10
- new RemoteConfigModule(store: IStoreAdapter, options?: IRemoteConfigOptions): RemoteConfigModule
10
+ new ConfigModule(store: IStoreAdapter, options?: IConfigOptions): ConfigModule
11
11
  .name: "@fonderie/config"
12
12
  .manager: RemoteConfigManager
13
13
  .install(app: IFonderieApp): Promise<void>
14
14
 
15
- new RemoteConfigManager(store: IStoreAdapter, options?: IRemoteConfigOptions): RemoteConfigManager
15
+ new RemoteConfigManager(store: IStoreAdapter, options?: IConfigOptions): RemoteConfigManager
16
16
  .boot(): Promise<void>
17
17
  .stop(): void
18
18
  .get<T>(key: string, fallback: T): T
@@ -48,7 +48,7 @@ interface IConfigSnapshot {
48
48
  fetchedAt: Date;
49
49
  }
50
50
 
51
- interface IRemoteConfigOptions {
51
+ interface IConfigOptions {
52
52
  ttl?: number;
53
53
  environment?: string;
54
54
  table?: string;
@@ -1,7 +1,7 @@
1
1
  import { Middleware } from '@fonderie/core';
2
2
  import { IStoreAdapter } from '@fonderie/store';
3
3
 
4
- interface IRemoteConfigOptions {
4
+ interface IConfigOptions {
5
5
  ttl?: number;
6
6
  environment?: string;
7
7
  table?: string;
@@ -14,7 +14,7 @@ declare class RemoteConfigManager {
14
14
  private environment;
15
15
  private ttl;
16
16
  private table;
17
- constructor(store: IStoreAdapter, options?: IRemoteConfigOptions);
17
+ constructor(store: IStoreAdapter, options?: IConfigOptions);
18
18
  boot(): Promise<void>;
19
19
  stop(): void;
20
20
  get<T>(key: string, fallback: T): T;
@@ -29,4 +29,4 @@ declare function getConfig(ctx: {
29
29
  meta: Record<string, unknown>;
30
30
  }, key: string, fallback?: unknown): unknown;
31
31
 
32
- export { CONFIG_MANAGER_KEY as C, type IRemoteConfigOptions as I, RemoteConfigManager as R, configContextMiddleware as c, getConfig as g };
32
+ export { CONFIG_MANAGER_KEY as C, type IConfigOptions as I, RemoteConfigManager as R, configContextMiddleware as c, getConfig as g };
@@ -1,7 +1,7 @@
1
1
  import { Middleware } from '@fonderie/core';
2
2
  import { IStoreAdapter } from '@fonderie/store';
3
3
 
4
- interface IRemoteConfigOptions {
4
+ interface IConfigOptions {
5
5
  ttl?: number;
6
6
  environment?: string;
7
7
  table?: string;
@@ -14,7 +14,7 @@ declare class RemoteConfigManager {
14
14
  private environment;
15
15
  private ttl;
16
16
  private table;
17
- constructor(store: IStoreAdapter, options?: IRemoteConfigOptions);
17
+ constructor(store: IStoreAdapter, options?: IConfigOptions);
18
18
  boot(): Promise<void>;
19
19
  stop(): void;
20
20
  get<T>(key: string, fallback: T): T;
@@ -29,4 +29,4 @@ declare function getConfig(ctx: {
29
29
  meta: Record<string, unknown>;
30
30
  }, key: string, fallback?: unknown): unknown;
31
31
 
32
- export { CONFIG_MANAGER_KEY as C, type IRemoteConfigOptions as I, RemoteConfigManager as R, configContextMiddleware as c, getConfig as g };
32
+ export { CONFIG_MANAGER_KEY as C, type IConfigOptions as I, RemoteConfigManager as R, configContextMiddleware as c, getConfig as g };
package/dist/index.cjs CHANGED
@@ -21,8 +21,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
23
  CONFIG_MANAGER_KEY: () => CONFIG_MANAGER_KEY,
24
+ ConfigModule: () => ConfigModule,
24
25
  RemoteConfigManager: () => RemoteConfigManager,
25
- RemoteConfigModule: () => RemoteConfigModule,
26
26
  configContextMiddleware: () => configContextMiddleware,
27
27
  deleteConfigEntry: () => deleteConfigEntry,
28
28
  getConfig: () => getConfig,
@@ -132,7 +132,7 @@ function getConfig(ctx, key, fallback = null) {
132
132
  }
133
133
 
134
134
  // src/module.ts
135
- var RemoteConfigModule = class {
135
+ var ConfigModule = class {
136
136
  name = "@fonderie/config";
137
137
  manager;
138
138
  constructor(store, options = {}) {
@@ -197,8 +197,8 @@ async function deleteConfigEntry(key, environment, store) {
197
197
  // Annotate the CommonJS export names for ESM import in node:
198
198
  0 && (module.exports = {
199
199
  CONFIG_MANAGER_KEY,
200
+ ConfigModule,
200
201
  RemoteConfigManager,
201
- RemoteConfigModule,
202
202
  configContextMiddleware,
203
203
  deleteConfigEntry,
204
204
  getConfig,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/manager.ts","../src/middlewares/config-context.ts","../src/module.ts","../src/services/config.ts"],"sourcesContent":["// ── Public API ───────────────────────────────────────────────────\nexport { RemoteConfigModule } from './module';\nexport { RemoteConfigManager, CONFIG_MANAGER_KEY } from './manager';\nexport { configContextMiddleware, getConfig } from './middlewares/config-context';\nexport {\n\tlistConfigEntries,\n\tgetConfigEntry,\n\tsetConfigEntry,\n\tdeleteConfigEntry,\n} from './services/config';\n\nexport type { IConfigEntry, IConfigSnapshot } from './types';\nexport type { IRemoteConfigOptions } from './config';\n","import type { IStoreAdapter } from '@fonderie/store';\n\nimport type { IRemoteConfigOptions } from './config';\nimport type { IConfigSnapshot } from './types';\n\nconst FONDERIE_CONFIG_KEY = 'fonderie.config.snapshot';\n\nexport class RemoteConfigManager {\n\tprivate snapshot: IConfigSnapshot | null = null;\n\tprivate interval: ReturnType<typeof setInterval> | null = null;\n\tprivate environment: string;\n\tprivate ttl: number;\n\tprivate table: string;\n\n\tconstructor(\n\t\tprivate store: IStoreAdapter,\n\t\toptions: IRemoteConfigOptions = {},\n\t) {\n\t\tthis.ttl = options.ttl ?? 30_000;\n\t\tthis.environment = options.environment ?? process.env['NODE_ENV'] ?? 'development';\n\t\tthis.table = options.table ?? 'fonderie_config';\n\t}\n\n\tasync boot(): Promise<void> {\n\t\tawait this.refresh();\n\t\tthis.interval = setInterval(() => {\n\t\t\tthis.refresh().catch((err) => console.error('[config] refresh error:', err));\n\t\t}, this.ttl);\n\t}\n\n\tstop(): void {\n\t\tif (this.interval) {\n\t\t\tclearInterval(this.interval);\n\t\t\tthis.interval = null;\n\t\t}\n\t}\n\n\t// Get a single value with a typed fallback\n\tget<T>(key: string, fallback: T): T {\n\t\tif (!this.snapshot) {\n\t\t\treturn fallback;\n\t\t}\n\n\t\tconst value = this.snapshot.entries[key];\n\t\treturn value !== undefined ? (value as T) : fallback;\n\t}\n\n\t// Get all entries as a flat record\n\tall(): Record<string, unknown> {\n\t\treturn this.snapshot?.entries ?? {};\n\t}\n\n\tasync refresh(): Promise<void> {\n\t\ttry {\n\t\t\tconst rows = await this.store.query<{\n\t\t\t\tkey: string;\n\t\t\t\tvalue: string;\n\t\t\t\tenvironment: string;\n\t\t\t}>(\n\t\t\t\t`SELECT key, value, environment\n\t\t\t\t FROM ${this.table}\n\t\t\t\t WHERE (environment = $1 OR environment = 'all')\n\t\t\t\t\tAND active = true`,\n\t\t\t\t[this.environment],\n\t\t\t);\n\n\t\t\tconst entries: Record<string, unknown> = {};\n\n\t\t\t// First pass — load 'all' entries as base\n\t\t\tfor (const row of rows) {\n\t\t\t\tif (row.environment === 'all') {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tentries[row.key] = JSON.parse(row.value);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\tentries[row.key] = row.value;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Second pass — environment-specific entries override 'all'\n\t\t\tfor (const row of rows) {\n\t\t\t\tif (row.environment !== 'all') {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tentries[row.key] = JSON.parse(row.value);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\tentries[row.key] = row.value;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.snapshot = {\n\t\t\t\tentries,\n\t\t\t\tfetchedAt: new Date(),\n\t\t\t};\n\t\t} catch (err) {\n\t\t\tconsole.error('[config] failed to refresh:', err);\n\t\t}\n\t}\n\n\tisStale(): boolean {\n\t\tif (!this.snapshot) {\n\t\t\treturn true;\n\t\t}\n\n\t\treturn Date.now() - this.snapshot.fetchedAt.getTime() > this.ttl;\n\t}\n}\n\n// Well-known config keys — extend as needed\nexport const CONFIG_MANAGER_KEY = FONDERIE_CONFIG_KEY;\n","import type { Middleware } from '@fonderie/core';\n\nimport type { RemoteConfigManager } from '../manager';\n\nimport { CONFIG_MANAGER_KEY } from '../manager';\n\n// Injects the config manager into ctx.meta\n// so handlers can read config values per-request\n\nexport function configContextMiddleware(manager: RemoteConfigManager): Middleware {\n\treturn async (ctx, next) => {\n\t\tctx.meta[CONFIG_MANAGER_KEY] = manager;\n\t\treturn next();\n\t};\n}\n\n// Helper to read config from ctx inside a handler\nexport function getConfig(\n\tctx: { meta: Record<string, unknown> },\n\tkey: string,\n\tfallback: unknown = null,\n): unknown {\n\tconst manager = ctx.meta[CONFIG_MANAGER_KEY] as RemoteConfigManager | undefined;\n\tif (!manager) {\n\t\treturn fallback;\n\t}\n\n\treturn manager.get(key, fallback);\n}\n","import type { IFonderieModule, IFonderieApp } from '@fonderie/core';\nimport type { IStoreAdapter } from '@fonderie/store';\n\nimport type { IRemoteConfigOptions } from './config';\n\nimport { RemoteConfigManager, CONFIG_MANAGER_KEY } from './manager';\nimport { configContextMiddleware } from './middlewares/config-context';\n\nexport class RemoteConfigModule implements IFonderieModule {\n\treadonly name = '@fonderie/config';\n\treadonly manager: RemoteConfigManager;\n\n\tconstructor(store: IStoreAdapter, options: IRemoteConfigOptions = {}) {\n\t\tthis.manager = new RemoteConfigManager(store, options);\n\t}\n\n\tasync install(app: IFonderieApp): Promise<void> {\n\t\tawait this.manager.boot();\n\t\tapp.use(configContextMiddleware(this.manager));\n\t}\n}\n","import type { IStoreAdapter } from '@fonderie/store';\n\nimport type { IConfigEntry } from '../types';\n\nconst SELECT_ENTRY = `\n\tSELECT\n\t\tkey,\n\t\tvalue,\n\t\tenvironment,\n\t\tdescription,\n\t\tactive,\n\t\tupdated_at AS \"updatedAt\"\n\tFROM fonderie_config`;\n\nexport async function listConfigEntries(\n\tenvironment: string | null,\n\tstore: IStoreAdapter,\n): Promise<IConfigEntry[]> {\n\treturn environment\n\t\t? store.query<IConfigEntry>(\n\t\t\t\t`${SELECT_ENTRY} WHERE (environment = $1 OR environment = 'all') AND active = true ORDER BY key`,\n\t\t\t\t[environment],\n\t\t\t)\n\t\t: store.query<IConfigEntry>(`${SELECT_ENTRY} ORDER BY environment, key`);\n}\n\nexport async function getConfigEntry(\n\tkey: string,\n\tenvironment: string,\n\tstore: IStoreAdapter,\n): Promise<IConfigEntry | null> {\n\tconst [row] = await store.query<IConfigEntry>(\n\t\t`${SELECT_ENTRY} WHERE key = $1 AND environment = $2`,\n\t\t[key, environment],\n\t);\n\treturn row ?? null;\n}\n\nexport async function setConfigEntry(\n\topts: {\n\t\tkey: string;\n\t\tvalue: unknown;\n\t\tenvironment?: string;\n\t\tdescription?: string;\n\t\tactive?: boolean;\n\t},\n\tstore: IStoreAdapter,\n): Promise<IConfigEntry> {\n\tconst env = opts.environment ?? 'all';\n\tconst raw = typeof opts.value === 'string' ? opts.value : JSON.stringify(opts.value);\n\tconst active = opts.active ?? true;\n\n\tconst [row] = await store.query<IConfigEntry>(\n\t\t`INSERT INTO fonderie_config (key, value, environment, description, active)\n\t\tVALUES ($1, $2, $3, $4, $5)\n\t\tON CONFLICT (key, environment) DO UPDATE SET\n\t\t\tvalue = $2,\n\t\t\tdescription = COALESCE($4, fonderie_config.description),\n\t\t\tactive = $5,\n\t\t\tupdated_at = now()\n\t\tRETURNING\n\t\t\tkey, value, environment, description, active,\n\t\t\tupdated_at AS \"updatedAt\"`,\n\t\t[opts.key, raw, env, opts.description ?? null, active],\n\t);\n\tif (!row) throw new Error('Failed to upsert config entry');\n\treturn row;\n}\n\nexport async function deleteConfigEntry(\n\tkey: string,\n\tenvironment: string,\n\tstore: IStoreAdapter,\n): Promise<boolean> {\n\tconst rows = await store.query<{ key: string }>(\n\t\t`DELETE FROM fonderie_config WHERE key = $1 AND environment = $2 RETURNING key`,\n\t\t[key, environment],\n\t);\n\treturn rows.length > 0;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/manager.ts","../src/middlewares/config-context.ts","../src/module.ts","../src/services/config.ts"],"sourcesContent":["// ── Public API ───────────────────────────────────────────────────\nexport { ConfigModule } from './module';\nexport { RemoteConfigManager, CONFIG_MANAGER_KEY } from './manager';\nexport { configContextMiddleware, getConfig } from './middlewares/config-context';\nexport {\n\tlistConfigEntries,\n\tgetConfigEntry,\n\tsetConfigEntry,\n\tdeleteConfigEntry,\n} from './services/config';\n\nexport type { IConfigEntry, IConfigSnapshot } from './types';\nexport type { IConfigOptions } from './config';\n","import type { IStoreAdapter } from '@fonderie/store';\n\nimport type { IConfigOptions } from './config';\nimport type { IConfigSnapshot } from './types';\n\nconst FONDERIE_CONFIG_KEY = 'fonderie.config.snapshot';\n\nexport class RemoteConfigManager {\n\tprivate snapshot: IConfigSnapshot | null = null;\n\tprivate interval: ReturnType<typeof setInterval> | null = null;\n\tprivate environment: string;\n\tprivate ttl: number;\n\tprivate table: string;\n\n\tconstructor(\n\t\tprivate store: IStoreAdapter,\n\t\toptions: IConfigOptions = {},\n\t) {\n\t\tthis.ttl = options.ttl ?? 30_000;\n\t\tthis.environment = options.environment ?? process.env['NODE_ENV'] ?? 'development';\n\t\tthis.table = options.table ?? 'fonderie_config';\n\t}\n\n\tasync boot(): Promise<void> {\n\t\tawait this.refresh();\n\t\tthis.interval = setInterval(() => {\n\t\t\tthis.refresh().catch((err) => console.error('[config] refresh error:', err));\n\t\t}, this.ttl);\n\t}\n\n\tstop(): void {\n\t\tif (this.interval) {\n\t\t\tclearInterval(this.interval);\n\t\t\tthis.interval = null;\n\t\t}\n\t}\n\n\t// Get a single value with a typed fallback\n\tget<T>(key: string, fallback: T): T {\n\t\tif (!this.snapshot) {\n\t\t\treturn fallback;\n\t\t}\n\n\t\tconst value = this.snapshot.entries[key];\n\t\treturn value !== undefined ? (value as T) : fallback;\n\t}\n\n\t// Get all entries as a flat record\n\tall(): Record<string, unknown> {\n\t\treturn this.snapshot?.entries ?? {};\n\t}\n\n\tasync refresh(): Promise<void> {\n\t\ttry {\n\t\t\tconst rows = await this.store.query<{\n\t\t\t\tkey: string;\n\t\t\t\tvalue: string;\n\t\t\t\tenvironment: string;\n\t\t\t}>(\n\t\t\t\t`SELECT key, value, environment\n\t\t\t\t FROM ${this.table}\n\t\t\t\t WHERE (environment = $1 OR environment = 'all')\n\t\t\t\t\tAND active = true`,\n\t\t\t\t[this.environment],\n\t\t\t);\n\n\t\t\tconst entries: Record<string, unknown> = {};\n\n\t\t\t// First pass — load 'all' entries as base\n\t\t\tfor (const row of rows) {\n\t\t\t\tif (row.environment === 'all') {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tentries[row.key] = JSON.parse(row.value);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\tentries[row.key] = row.value;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Second pass — environment-specific entries override 'all'\n\t\t\tfor (const row of rows) {\n\t\t\t\tif (row.environment !== 'all') {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tentries[row.key] = JSON.parse(row.value);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\tentries[row.key] = row.value;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.snapshot = {\n\t\t\t\tentries,\n\t\t\t\tfetchedAt: new Date(),\n\t\t\t};\n\t\t} catch (err) {\n\t\t\tconsole.error('[config] failed to refresh:', err);\n\t\t}\n\t}\n\n\tisStale(): boolean {\n\t\tif (!this.snapshot) {\n\t\t\treturn true;\n\t\t}\n\n\t\treturn Date.now() - this.snapshot.fetchedAt.getTime() > this.ttl;\n\t}\n}\n\n// Well-known config keys — extend as needed\nexport const CONFIG_MANAGER_KEY = FONDERIE_CONFIG_KEY;\n","import type { Middleware } from '@fonderie/core';\n\nimport type { RemoteConfigManager } from '../manager';\n\nimport { CONFIG_MANAGER_KEY } from '../manager';\n\n// Injects the config manager into ctx.meta\n// so handlers can read config values per-request\n\nexport function configContextMiddleware(manager: RemoteConfigManager): Middleware {\n\treturn async (ctx, next) => {\n\t\tctx.meta[CONFIG_MANAGER_KEY] = manager;\n\t\treturn next();\n\t};\n}\n\n// Helper to read config from ctx inside a handler\nexport function getConfig(\n\tctx: { meta: Record<string, unknown> },\n\tkey: string,\n\tfallback: unknown = null,\n): unknown {\n\tconst manager = ctx.meta[CONFIG_MANAGER_KEY] as RemoteConfigManager | undefined;\n\tif (!manager) {\n\t\treturn fallback;\n\t}\n\n\treturn manager.get(key, fallback);\n}\n","import type { IFonderieModule, IFonderieApp } from '@fonderie/core';\nimport type { IStoreAdapter } from '@fonderie/store';\n\nimport type { IConfigOptions } from './config';\n\nimport { RemoteConfigManager, CONFIG_MANAGER_KEY } from './manager';\nimport { configContextMiddleware } from './middlewares/config-context';\n\nexport class ConfigModule implements IFonderieModule {\n\treadonly name = '@fonderie/config';\n\treadonly manager: RemoteConfigManager;\n\n\tconstructor(store: IStoreAdapter, options: IConfigOptions = {}) {\n\t\tthis.manager = new RemoteConfigManager(store, options);\n\t}\n\n\tasync install(app: IFonderieApp): Promise<void> {\n\t\tawait this.manager.boot();\n\t\tapp.use(configContextMiddleware(this.manager));\n\t}\n}\n","import type { IStoreAdapter } from '@fonderie/store';\n\nimport type { IConfigEntry } from '../types';\n\nconst SELECT_ENTRY = `\n\tSELECT\n\t\tkey,\n\t\tvalue,\n\t\tenvironment,\n\t\tdescription,\n\t\tactive,\n\t\tupdated_at AS \"updatedAt\"\n\tFROM fonderie_config`;\n\nexport async function listConfigEntries(\n\tenvironment: string | null,\n\tstore: IStoreAdapter,\n): Promise<IConfigEntry[]> {\n\treturn environment\n\t\t? store.query<IConfigEntry>(\n\t\t\t\t`${SELECT_ENTRY} WHERE (environment = $1 OR environment = 'all') AND active = true ORDER BY key`,\n\t\t\t\t[environment],\n\t\t\t)\n\t\t: store.query<IConfigEntry>(`${SELECT_ENTRY} ORDER BY environment, key`);\n}\n\nexport async function getConfigEntry(\n\tkey: string,\n\tenvironment: string,\n\tstore: IStoreAdapter,\n): Promise<IConfigEntry | null> {\n\tconst [row] = await store.query<IConfigEntry>(\n\t\t`${SELECT_ENTRY} WHERE key = $1 AND environment = $2`,\n\t\t[key, environment],\n\t);\n\treturn row ?? null;\n}\n\nexport async function setConfigEntry(\n\topts: {\n\t\tkey: string;\n\t\tvalue: unknown;\n\t\tenvironment?: string;\n\t\tdescription?: string;\n\t\tactive?: boolean;\n\t},\n\tstore: IStoreAdapter,\n): Promise<IConfigEntry> {\n\tconst env = opts.environment ?? 'all';\n\tconst raw = typeof opts.value === 'string' ? opts.value : JSON.stringify(opts.value);\n\tconst active = opts.active ?? true;\n\n\tconst [row] = await store.query<IConfigEntry>(\n\t\t`INSERT INTO fonderie_config (key, value, environment, description, active)\n\t\tVALUES ($1, $2, $3, $4, $5)\n\t\tON CONFLICT (key, environment) DO UPDATE SET\n\t\t\tvalue = $2,\n\t\t\tdescription = COALESCE($4, fonderie_config.description),\n\t\t\tactive = $5,\n\t\t\tupdated_at = now()\n\t\tRETURNING\n\t\t\tkey, value, environment, description, active,\n\t\t\tupdated_at AS \"updatedAt\"`,\n\t\t[opts.key, raw, env, opts.description ?? null, active],\n\t);\n\tif (!row) throw new Error('Failed to upsert config entry');\n\treturn row;\n}\n\nexport async function deleteConfigEntry(\n\tkey: string,\n\tenvironment: string,\n\tstore: IStoreAdapter,\n): Promise<boolean> {\n\tconst rows = await store.query<{ key: string }>(\n\t\t`DELETE FROM fonderie_config WHERE key = $1 AND environment = $2 RETURNING key`,\n\t\t[key, environment],\n\t);\n\treturn rows.length > 0;\n}\n"],"mappings":"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","names":[]}
package/dist/index.d.cts CHANGED
@@ -1,14 +1,14 @@
1
1
  import { IFonderieModule, IFonderieApp } from '@fonderie/core';
2
2
  import { IStoreAdapter } from '@fonderie/store';
3
- import { R as RemoteConfigManager, I as IRemoteConfigOptions } from './index-v2hLmm2m.cjs';
4
- export { C as CONFIG_MANAGER_KEY, c as configContextMiddleware, g as getConfig } from './index-v2hLmm2m.cjs';
3
+ import { R as RemoteConfigManager, I as IConfigOptions } from './index-B2jOiL09.cjs';
4
+ export { C as CONFIG_MANAGER_KEY, c as configContextMiddleware, g as getConfig } from './index-B2jOiL09.cjs';
5
5
  import { IConfigEntry } from './types.cjs';
6
6
  export { IConfigSnapshot } from './types.cjs';
7
7
 
8
- declare class RemoteConfigModule implements IFonderieModule {
8
+ declare class ConfigModule implements IFonderieModule {
9
9
  readonly name = "@fonderie/config";
10
10
  readonly manager: RemoteConfigManager;
11
- constructor(store: IStoreAdapter, options?: IRemoteConfigOptions);
11
+ constructor(store: IStoreAdapter, options?: IConfigOptions);
12
12
  install(app: IFonderieApp): Promise<void>;
13
13
  }
14
14
 
@@ -23,4 +23,4 @@ declare function setConfigEntry(opts: {
23
23
  }, store: IStoreAdapter): Promise<IConfigEntry>;
24
24
  declare function deleteConfigEntry(key: string, environment: string, store: IStoreAdapter): Promise<boolean>;
25
25
 
26
- export { IConfigEntry, IRemoteConfigOptions, RemoteConfigManager, RemoteConfigModule, deleteConfigEntry, getConfigEntry, listConfigEntries, setConfigEntry };
26
+ export { ConfigModule, IConfigEntry, IConfigOptions, RemoteConfigManager, deleteConfigEntry, getConfigEntry, listConfigEntries, setConfigEntry };
package/dist/index.d.ts CHANGED
@@ -1,14 +1,14 @@
1
1
  import { IFonderieModule, IFonderieApp } from '@fonderie/core';
2
2
  import { IStoreAdapter } from '@fonderie/store';
3
- import { R as RemoteConfigManager, I as IRemoteConfigOptions } from './index-v2hLmm2m.js';
4
- export { C as CONFIG_MANAGER_KEY, c as configContextMiddleware, g as getConfig } from './index-v2hLmm2m.js';
3
+ import { R as RemoteConfigManager, I as IConfigOptions } from './index-B2jOiL09.js';
4
+ export { C as CONFIG_MANAGER_KEY, c as configContextMiddleware, g as getConfig } from './index-B2jOiL09.js';
5
5
  import { IConfigEntry } from './types.js';
6
6
  export { IConfigSnapshot } from './types.js';
7
7
 
8
- declare class RemoteConfigModule implements IFonderieModule {
8
+ declare class ConfigModule implements IFonderieModule {
9
9
  readonly name = "@fonderie/config";
10
10
  readonly manager: RemoteConfigManager;
11
- constructor(store: IStoreAdapter, options?: IRemoteConfigOptions);
11
+ constructor(store: IStoreAdapter, options?: IConfigOptions);
12
12
  install(app: IFonderieApp): Promise<void>;
13
13
  }
14
14
 
@@ -23,4 +23,4 @@ declare function setConfigEntry(opts: {
23
23
  }, store: IStoreAdapter): Promise<IConfigEntry>;
24
24
  declare function deleteConfigEntry(key: string, environment: string, store: IStoreAdapter): Promise<boolean>;
25
25
 
26
- export { IConfigEntry, IRemoteConfigOptions, RemoteConfigManager, RemoteConfigModule, deleteConfigEntry, getConfigEntry, listConfigEntries, setConfigEntry };
26
+ export { ConfigModule, IConfigEntry, IConfigOptions, RemoteConfigManager, deleteConfigEntry, getConfigEntry, listConfigEntries, setConfigEntry };
package/dist/index.js CHANGED
@@ -98,7 +98,7 @@ function getConfig(ctx, key, fallback = null) {
98
98
  }
99
99
 
100
100
  // src/module.ts
101
- var RemoteConfigModule = class {
101
+ var ConfigModule = class {
102
102
  name = "@fonderie/config";
103
103
  manager;
104
104
  constructor(store, options = {}) {
@@ -162,8 +162,8 @@ async function deleteConfigEntry(key, environment, store) {
162
162
  }
163
163
  export {
164
164
  CONFIG_MANAGER_KEY,
165
+ ConfigModule,
165
166
  RemoteConfigManager,
166
- RemoteConfigModule,
167
167
  configContextMiddleware,
168
168
  deleteConfigEntry,
169
169
  getConfig,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/manager.ts","../src/middlewares/config-context.ts","../src/module.ts","../src/services/config.ts"],"sourcesContent":["import type { IStoreAdapter } from '@fonderie/store';\n\nimport type { IRemoteConfigOptions } from './config';\nimport type { IConfigSnapshot } from './types';\n\nconst FONDERIE_CONFIG_KEY = 'fonderie.config.snapshot';\n\nexport class RemoteConfigManager {\n\tprivate snapshot: IConfigSnapshot | null = null;\n\tprivate interval: ReturnType<typeof setInterval> | null = null;\n\tprivate environment: string;\n\tprivate ttl: number;\n\tprivate table: string;\n\n\tconstructor(\n\t\tprivate store: IStoreAdapter,\n\t\toptions: IRemoteConfigOptions = {},\n\t) {\n\t\tthis.ttl = options.ttl ?? 30_000;\n\t\tthis.environment = options.environment ?? process.env['NODE_ENV'] ?? 'development';\n\t\tthis.table = options.table ?? 'fonderie_config';\n\t}\n\n\tasync boot(): Promise<void> {\n\t\tawait this.refresh();\n\t\tthis.interval = setInterval(() => {\n\t\t\tthis.refresh().catch((err) => console.error('[config] refresh error:', err));\n\t\t}, this.ttl);\n\t}\n\n\tstop(): void {\n\t\tif (this.interval) {\n\t\t\tclearInterval(this.interval);\n\t\t\tthis.interval = null;\n\t\t}\n\t}\n\n\t// Get a single value with a typed fallback\n\tget<T>(key: string, fallback: T): T {\n\t\tif (!this.snapshot) {\n\t\t\treturn fallback;\n\t\t}\n\n\t\tconst value = this.snapshot.entries[key];\n\t\treturn value !== undefined ? (value as T) : fallback;\n\t}\n\n\t// Get all entries as a flat record\n\tall(): Record<string, unknown> {\n\t\treturn this.snapshot?.entries ?? {};\n\t}\n\n\tasync refresh(): Promise<void> {\n\t\ttry {\n\t\t\tconst rows = await this.store.query<{\n\t\t\t\tkey: string;\n\t\t\t\tvalue: string;\n\t\t\t\tenvironment: string;\n\t\t\t}>(\n\t\t\t\t`SELECT key, value, environment\n\t\t\t\t FROM ${this.table}\n\t\t\t\t WHERE (environment = $1 OR environment = 'all')\n\t\t\t\t\tAND active = true`,\n\t\t\t\t[this.environment],\n\t\t\t);\n\n\t\t\tconst entries: Record<string, unknown> = {};\n\n\t\t\t// First pass — load 'all' entries as base\n\t\t\tfor (const row of rows) {\n\t\t\t\tif (row.environment === 'all') {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tentries[row.key] = JSON.parse(row.value);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\tentries[row.key] = row.value;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Second pass — environment-specific entries override 'all'\n\t\t\tfor (const row of rows) {\n\t\t\t\tif (row.environment !== 'all') {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tentries[row.key] = JSON.parse(row.value);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\tentries[row.key] = row.value;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.snapshot = {\n\t\t\t\tentries,\n\t\t\t\tfetchedAt: new Date(),\n\t\t\t};\n\t\t} catch (err) {\n\t\t\tconsole.error('[config] failed to refresh:', err);\n\t\t}\n\t}\n\n\tisStale(): boolean {\n\t\tif (!this.snapshot) {\n\t\t\treturn true;\n\t\t}\n\n\t\treturn Date.now() - this.snapshot.fetchedAt.getTime() > this.ttl;\n\t}\n}\n\n// Well-known config keys — extend as needed\nexport const CONFIG_MANAGER_KEY = FONDERIE_CONFIG_KEY;\n","import type { Middleware } from '@fonderie/core';\n\nimport type { RemoteConfigManager } from '../manager';\n\nimport { CONFIG_MANAGER_KEY } from '../manager';\n\n// Injects the config manager into ctx.meta\n// so handlers can read config values per-request\n\nexport function configContextMiddleware(manager: RemoteConfigManager): Middleware {\n\treturn async (ctx, next) => {\n\t\tctx.meta[CONFIG_MANAGER_KEY] = manager;\n\t\treturn next();\n\t};\n}\n\n// Helper to read config from ctx inside a handler\nexport function getConfig(\n\tctx: { meta: Record<string, unknown> },\n\tkey: string,\n\tfallback: unknown = null,\n): unknown {\n\tconst manager = ctx.meta[CONFIG_MANAGER_KEY] as RemoteConfigManager | undefined;\n\tif (!manager) {\n\t\treturn fallback;\n\t}\n\n\treturn manager.get(key, fallback);\n}\n","import type { IFonderieModule, IFonderieApp } from '@fonderie/core';\nimport type { IStoreAdapter } from '@fonderie/store';\n\nimport type { IRemoteConfigOptions } from './config';\n\nimport { RemoteConfigManager, CONFIG_MANAGER_KEY } from './manager';\nimport { configContextMiddleware } from './middlewares/config-context';\n\nexport class RemoteConfigModule implements IFonderieModule {\n\treadonly name = '@fonderie/config';\n\treadonly manager: RemoteConfigManager;\n\n\tconstructor(store: IStoreAdapter, options: IRemoteConfigOptions = {}) {\n\t\tthis.manager = new RemoteConfigManager(store, options);\n\t}\n\n\tasync install(app: IFonderieApp): Promise<void> {\n\t\tawait this.manager.boot();\n\t\tapp.use(configContextMiddleware(this.manager));\n\t}\n}\n","import type { IStoreAdapter } from '@fonderie/store';\n\nimport type { IConfigEntry } from '../types';\n\nconst SELECT_ENTRY = `\n\tSELECT\n\t\tkey,\n\t\tvalue,\n\t\tenvironment,\n\t\tdescription,\n\t\tactive,\n\t\tupdated_at AS \"updatedAt\"\n\tFROM fonderie_config`;\n\nexport async function listConfigEntries(\n\tenvironment: string | null,\n\tstore: IStoreAdapter,\n): Promise<IConfigEntry[]> {\n\treturn environment\n\t\t? store.query<IConfigEntry>(\n\t\t\t\t`${SELECT_ENTRY} WHERE (environment = $1 OR environment = 'all') AND active = true ORDER BY key`,\n\t\t\t\t[environment],\n\t\t\t)\n\t\t: store.query<IConfigEntry>(`${SELECT_ENTRY} ORDER BY environment, key`);\n}\n\nexport async function getConfigEntry(\n\tkey: string,\n\tenvironment: string,\n\tstore: IStoreAdapter,\n): Promise<IConfigEntry | null> {\n\tconst [row] = await store.query<IConfigEntry>(\n\t\t`${SELECT_ENTRY} WHERE key = $1 AND environment = $2`,\n\t\t[key, environment],\n\t);\n\treturn row ?? null;\n}\n\nexport async function setConfigEntry(\n\topts: {\n\t\tkey: string;\n\t\tvalue: unknown;\n\t\tenvironment?: string;\n\t\tdescription?: string;\n\t\tactive?: boolean;\n\t},\n\tstore: IStoreAdapter,\n): Promise<IConfigEntry> {\n\tconst env = opts.environment ?? 'all';\n\tconst raw = typeof opts.value === 'string' ? opts.value : JSON.stringify(opts.value);\n\tconst active = opts.active ?? true;\n\n\tconst [row] = await store.query<IConfigEntry>(\n\t\t`INSERT INTO fonderie_config (key, value, environment, description, active)\n\t\tVALUES ($1, $2, $3, $4, $5)\n\t\tON CONFLICT (key, environment) DO UPDATE SET\n\t\t\tvalue = $2,\n\t\t\tdescription = COALESCE($4, fonderie_config.description),\n\t\t\tactive = $5,\n\t\t\tupdated_at = now()\n\t\tRETURNING\n\t\t\tkey, value, environment, description, active,\n\t\t\tupdated_at AS \"updatedAt\"`,\n\t\t[opts.key, raw, env, opts.description ?? null, active],\n\t);\n\tif (!row) throw new Error('Failed to upsert config entry');\n\treturn row;\n}\n\nexport async function deleteConfigEntry(\n\tkey: string,\n\tenvironment: string,\n\tstore: IStoreAdapter,\n): Promise<boolean> {\n\tconst rows = await store.query<{ key: string }>(\n\t\t`DELETE FROM fonderie_config WHERE key = $1 AND environment = $2 RETURNING key`,\n\t\t[key, environment],\n\t);\n\treturn rows.length > 0;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/manager.ts","../src/middlewares/config-context.ts","../src/module.ts","../src/services/config.ts"],"sourcesContent":["import type { IStoreAdapter } from '@fonderie/store';\n\nimport type { IConfigOptions } from './config';\nimport type { IConfigSnapshot } from './types';\n\nconst FONDERIE_CONFIG_KEY = 'fonderie.config.snapshot';\n\nexport class RemoteConfigManager {\n\tprivate snapshot: IConfigSnapshot | null = null;\n\tprivate interval: ReturnType<typeof setInterval> | null = null;\n\tprivate environment: string;\n\tprivate ttl: number;\n\tprivate table: string;\n\n\tconstructor(\n\t\tprivate store: IStoreAdapter,\n\t\toptions: IConfigOptions = {},\n\t) {\n\t\tthis.ttl = options.ttl ?? 30_000;\n\t\tthis.environment = options.environment ?? process.env['NODE_ENV'] ?? 'development';\n\t\tthis.table = options.table ?? 'fonderie_config';\n\t}\n\n\tasync boot(): Promise<void> {\n\t\tawait this.refresh();\n\t\tthis.interval = setInterval(() => {\n\t\t\tthis.refresh().catch((err) => console.error('[config] refresh error:', err));\n\t\t}, this.ttl);\n\t}\n\n\tstop(): void {\n\t\tif (this.interval) {\n\t\t\tclearInterval(this.interval);\n\t\t\tthis.interval = null;\n\t\t}\n\t}\n\n\t// Get a single value with a typed fallback\n\tget<T>(key: string, fallback: T): T {\n\t\tif (!this.snapshot) {\n\t\t\treturn fallback;\n\t\t}\n\n\t\tconst value = this.snapshot.entries[key];\n\t\treturn value !== undefined ? (value as T) : fallback;\n\t}\n\n\t// Get all entries as a flat record\n\tall(): Record<string, unknown> {\n\t\treturn this.snapshot?.entries ?? {};\n\t}\n\n\tasync refresh(): Promise<void> {\n\t\ttry {\n\t\t\tconst rows = await this.store.query<{\n\t\t\t\tkey: string;\n\t\t\t\tvalue: string;\n\t\t\t\tenvironment: string;\n\t\t\t}>(\n\t\t\t\t`SELECT key, value, environment\n\t\t\t\t FROM ${this.table}\n\t\t\t\t WHERE (environment = $1 OR environment = 'all')\n\t\t\t\t\tAND active = true`,\n\t\t\t\t[this.environment],\n\t\t\t);\n\n\t\t\tconst entries: Record<string, unknown> = {};\n\n\t\t\t// First pass — load 'all' entries as base\n\t\t\tfor (const row of rows) {\n\t\t\t\tif (row.environment === 'all') {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tentries[row.key] = JSON.parse(row.value);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\tentries[row.key] = row.value;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Second pass — environment-specific entries override 'all'\n\t\t\tfor (const row of rows) {\n\t\t\t\tif (row.environment !== 'all') {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tentries[row.key] = JSON.parse(row.value);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\tentries[row.key] = row.value;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.snapshot = {\n\t\t\t\tentries,\n\t\t\t\tfetchedAt: new Date(),\n\t\t\t};\n\t\t} catch (err) {\n\t\t\tconsole.error('[config] failed to refresh:', err);\n\t\t}\n\t}\n\n\tisStale(): boolean {\n\t\tif (!this.snapshot) {\n\t\t\treturn true;\n\t\t}\n\n\t\treturn Date.now() - this.snapshot.fetchedAt.getTime() > this.ttl;\n\t}\n}\n\n// Well-known config keys — extend as needed\nexport const CONFIG_MANAGER_KEY = FONDERIE_CONFIG_KEY;\n","import type { Middleware } from '@fonderie/core';\n\nimport type { RemoteConfigManager } from '../manager';\n\nimport { CONFIG_MANAGER_KEY } from '../manager';\n\n// Injects the config manager into ctx.meta\n// so handlers can read config values per-request\n\nexport function configContextMiddleware(manager: RemoteConfigManager): Middleware {\n\treturn async (ctx, next) => {\n\t\tctx.meta[CONFIG_MANAGER_KEY] = manager;\n\t\treturn next();\n\t};\n}\n\n// Helper to read config from ctx inside a handler\nexport function getConfig(\n\tctx: { meta: Record<string, unknown> },\n\tkey: string,\n\tfallback: unknown = null,\n): unknown {\n\tconst manager = ctx.meta[CONFIG_MANAGER_KEY] as RemoteConfigManager | undefined;\n\tif (!manager) {\n\t\treturn fallback;\n\t}\n\n\treturn manager.get(key, fallback);\n}\n","import type { IFonderieModule, IFonderieApp } from '@fonderie/core';\nimport type { IStoreAdapter } from '@fonderie/store';\n\nimport type { IConfigOptions } from './config';\n\nimport { RemoteConfigManager, CONFIG_MANAGER_KEY } from './manager';\nimport { configContextMiddleware } from './middlewares/config-context';\n\nexport class ConfigModule implements IFonderieModule {\n\treadonly name = '@fonderie/config';\n\treadonly manager: RemoteConfigManager;\n\n\tconstructor(store: IStoreAdapter, options: IConfigOptions = {}) {\n\t\tthis.manager = new RemoteConfigManager(store, options);\n\t}\n\n\tasync install(app: IFonderieApp): Promise<void> {\n\t\tawait this.manager.boot();\n\t\tapp.use(configContextMiddleware(this.manager));\n\t}\n}\n","import type { IStoreAdapter } from '@fonderie/store';\n\nimport type { IConfigEntry } from '../types';\n\nconst SELECT_ENTRY = `\n\tSELECT\n\t\tkey,\n\t\tvalue,\n\t\tenvironment,\n\t\tdescription,\n\t\tactive,\n\t\tupdated_at AS \"updatedAt\"\n\tFROM fonderie_config`;\n\nexport async function listConfigEntries(\n\tenvironment: string | null,\n\tstore: IStoreAdapter,\n): Promise<IConfigEntry[]> {\n\treturn environment\n\t\t? store.query<IConfigEntry>(\n\t\t\t\t`${SELECT_ENTRY} WHERE (environment = $1 OR environment = 'all') AND active = true ORDER BY key`,\n\t\t\t\t[environment],\n\t\t\t)\n\t\t: store.query<IConfigEntry>(`${SELECT_ENTRY} ORDER BY environment, key`);\n}\n\nexport async function getConfigEntry(\n\tkey: string,\n\tenvironment: string,\n\tstore: IStoreAdapter,\n): Promise<IConfigEntry | null> {\n\tconst [row] = await store.query<IConfigEntry>(\n\t\t`${SELECT_ENTRY} WHERE key = $1 AND environment = $2`,\n\t\t[key, environment],\n\t);\n\treturn row ?? null;\n}\n\nexport async function setConfigEntry(\n\topts: {\n\t\tkey: string;\n\t\tvalue: unknown;\n\t\tenvironment?: string;\n\t\tdescription?: string;\n\t\tactive?: boolean;\n\t},\n\tstore: IStoreAdapter,\n): Promise<IConfigEntry> {\n\tconst env = opts.environment ?? 'all';\n\tconst raw = typeof opts.value === 'string' ? opts.value : JSON.stringify(opts.value);\n\tconst active = opts.active ?? true;\n\n\tconst [row] = await store.query<IConfigEntry>(\n\t\t`INSERT INTO fonderie_config (key, value, environment, description, active)\n\t\tVALUES ($1, $2, $3, $4, $5)\n\t\tON CONFLICT (key, environment) DO UPDATE SET\n\t\t\tvalue = $2,\n\t\t\tdescription = COALESCE($4, fonderie_config.description),\n\t\t\tactive = $5,\n\t\t\tupdated_at = now()\n\t\tRETURNING\n\t\t\tkey, value, environment, description, active,\n\t\t\tupdated_at AS \"updatedAt\"`,\n\t\t[opts.key, raw, env, opts.description ?? null, active],\n\t);\n\tif (!row) throw new Error('Failed to upsert config entry');\n\treturn row;\n}\n\nexport async function deleteConfigEntry(\n\tkey: string,\n\tenvironment: string,\n\tstore: IStoreAdapter,\n): Promise<boolean> {\n\tconst rows = await store.query<{ key: string }>(\n\t\t`DELETE FROM fonderie_config WHERE key = $1 AND environment = $2 RETURNING key`,\n\t\t[key, environment],\n\t);\n\treturn rows.length > 0;\n}\n"],"mappings":"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","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/middlewares/index.ts","../../src/manager.ts","../../src/middlewares/config-context.ts"],"sourcesContent":["export { configContextMiddleware, getConfig } from './config-context';\n","import type { IStoreAdapter } from '@fonderie/store';\n\nimport type { IRemoteConfigOptions } from './config';\nimport type { IConfigSnapshot } from './types';\n\nconst FONDERIE_CONFIG_KEY = 'fonderie.config.snapshot';\n\nexport class RemoteConfigManager {\n\tprivate snapshot: IConfigSnapshot | null = null;\n\tprivate interval: ReturnType<typeof setInterval> | null = null;\n\tprivate environment: string;\n\tprivate ttl: number;\n\tprivate table: string;\n\n\tconstructor(\n\t\tprivate store: IStoreAdapter,\n\t\toptions: IRemoteConfigOptions = {},\n\t) {\n\t\tthis.ttl = options.ttl ?? 30_000;\n\t\tthis.environment = options.environment ?? process.env['NODE_ENV'] ?? 'development';\n\t\tthis.table = options.table ?? 'fonderie_config';\n\t}\n\n\tasync boot(): Promise<void> {\n\t\tawait this.refresh();\n\t\tthis.interval = setInterval(() => {\n\t\t\tthis.refresh().catch((err) => console.error('[config] refresh error:', err));\n\t\t}, this.ttl);\n\t}\n\n\tstop(): void {\n\t\tif (this.interval) {\n\t\t\tclearInterval(this.interval);\n\t\t\tthis.interval = null;\n\t\t}\n\t}\n\n\t// Get a single value with a typed fallback\n\tget<T>(key: string, fallback: T): T {\n\t\tif (!this.snapshot) {\n\t\t\treturn fallback;\n\t\t}\n\n\t\tconst value = this.snapshot.entries[key];\n\t\treturn value !== undefined ? (value as T) : fallback;\n\t}\n\n\t// Get all entries as a flat record\n\tall(): Record<string, unknown> {\n\t\treturn this.snapshot?.entries ?? {};\n\t}\n\n\tasync refresh(): Promise<void> {\n\t\ttry {\n\t\t\tconst rows = await this.store.query<{\n\t\t\t\tkey: string;\n\t\t\t\tvalue: string;\n\t\t\t\tenvironment: string;\n\t\t\t}>(\n\t\t\t\t`SELECT key, value, environment\n\t\t\t\t FROM ${this.table}\n\t\t\t\t WHERE (environment = $1 OR environment = 'all')\n\t\t\t\t\tAND active = true`,\n\t\t\t\t[this.environment],\n\t\t\t);\n\n\t\t\tconst entries: Record<string, unknown> = {};\n\n\t\t\t// First pass — load 'all' entries as base\n\t\t\tfor (const row of rows) {\n\t\t\t\tif (row.environment === 'all') {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tentries[row.key] = JSON.parse(row.value);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\tentries[row.key] = row.value;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Second pass — environment-specific entries override 'all'\n\t\t\tfor (const row of rows) {\n\t\t\t\tif (row.environment !== 'all') {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tentries[row.key] = JSON.parse(row.value);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\tentries[row.key] = row.value;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.snapshot = {\n\t\t\t\tentries,\n\t\t\t\tfetchedAt: new Date(),\n\t\t\t};\n\t\t} catch (err) {\n\t\t\tconsole.error('[config] failed to refresh:', err);\n\t\t}\n\t}\n\n\tisStale(): boolean {\n\t\tif (!this.snapshot) {\n\t\t\treturn true;\n\t\t}\n\n\t\treturn Date.now() - this.snapshot.fetchedAt.getTime() > this.ttl;\n\t}\n}\n\n// Well-known config keys — extend as needed\nexport const CONFIG_MANAGER_KEY = FONDERIE_CONFIG_KEY;\n","import type { Middleware } from '@fonderie/core';\n\nimport type { RemoteConfigManager } from '../manager';\n\nimport { CONFIG_MANAGER_KEY } from '../manager';\n\n// Injects the config manager into ctx.meta\n// so handlers can read config values per-request\n\nexport function configContextMiddleware(manager: RemoteConfigManager): Middleware {\n\treturn async (ctx, next) => {\n\t\tctx.meta[CONFIG_MANAGER_KEY] = manager;\n\t\treturn next();\n\t};\n}\n\n// Helper to read config from ctx inside a handler\nexport function getConfig(\n\tctx: { meta: Record<string, unknown> },\n\tkey: string,\n\tfallback: unknown = null,\n): unknown {\n\tconst manager = ctx.meta[CONFIG_MANAGER_KEY] as RemoteConfigManager | undefined;\n\tif (!manager) {\n\t\treturn fallback;\n\t}\n\n\treturn manager.get(key, fallback);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACKA,IAAM,sBAAsB;AAwGrB,IAAM,qBAAqB;;;ACpG3B,SAAS,wBAAwB,SAA0C;AACjF,SAAO,OAAO,KAAK,SAAS;AAC3B,QAAI,KAAK,kBAAkB,IAAI;AAC/B,WAAO,KAAK;AAAA,EACb;AACD;AAGO,SAAS,UACf,KACA,KACA,WAAoB,MACV;AACV,QAAM,UAAU,IAAI,KAAK,kBAAkB;AAC3C,MAAI,CAAC,SAAS;AACb,WAAO;AAAA,EACR;AAEA,SAAO,QAAQ,IAAI,KAAK,QAAQ;AACjC;","names":[]}
1
+ {"version":3,"sources":["../../src/middlewares/index.ts","../../src/manager.ts","../../src/middlewares/config-context.ts"],"sourcesContent":["export { configContextMiddleware, getConfig } from './config-context';\n","import type { IStoreAdapter } from '@fonderie/store';\n\nimport type { IConfigOptions } from './config';\nimport type { IConfigSnapshot } from './types';\n\nconst FONDERIE_CONFIG_KEY = 'fonderie.config.snapshot';\n\nexport class RemoteConfigManager {\n\tprivate snapshot: IConfigSnapshot | null = null;\n\tprivate interval: ReturnType<typeof setInterval> | null = null;\n\tprivate environment: string;\n\tprivate ttl: number;\n\tprivate table: string;\n\n\tconstructor(\n\t\tprivate store: IStoreAdapter,\n\t\toptions: IConfigOptions = {},\n\t) {\n\t\tthis.ttl = options.ttl ?? 30_000;\n\t\tthis.environment = options.environment ?? process.env['NODE_ENV'] ?? 'development';\n\t\tthis.table = options.table ?? 'fonderie_config';\n\t}\n\n\tasync boot(): Promise<void> {\n\t\tawait this.refresh();\n\t\tthis.interval = setInterval(() => {\n\t\t\tthis.refresh().catch((err) => console.error('[config] refresh error:', err));\n\t\t}, this.ttl);\n\t}\n\n\tstop(): void {\n\t\tif (this.interval) {\n\t\t\tclearInterval(this.interval);\n\t\t\tthis.interval = null;\n\t\t}\n\t}\n\n\t// Get a single value with a typed fallback\n\tget<T>(key: string, fallback: T): T {\n\t\tif (!this.snapshot) {\n\t\t\treturn fallback;\n\t\t}\n\n\t\tconst value = this.snapshot.entries[key];\n\t\treturn value !== undefined ? (value as T) : fallback;\n\t}\n\n\t// Get all entries as a flat record\n\tall(): Record<string, unknown> {\n\t\treturn this.snapshot?.entries ?? {};\n\t}\n\n\tasync refresh(): Promise<void> {\n\t\ttry {\n\t\t\tconst rows = await this.store.query<{\n\t\t\t\tkey: string;\n\t\t\t\tvalue: string;\n\t\t\t\tenvironment: string;\n\t\t\t}>(\n\t\t\t\t`SELECT key, value, environment\n\t\t\t\t FROM ${this.table}\n\t\t\t\t WHERE (environment = $1 OR environment = 'all')\n\t\t\t\t\tAND active = true`,\n\t\t\t\t[this.environment],\n\t\t\t);\n\n\t\t\tconst entries: Record<string, unknown> = {};\n\n\t\t\t// First pass — load 'all' entries as base\n\t\t\tfor (const row of rows) {\n\t\t\t\tif (row.environment === 'all') {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tentries[row.key] = JSON.parse(row.value);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\tentries[row.key] = row.value;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Second pass — environment-specific entries override 'all'\n\t\t\tfor (const row of rows) {\n\t\t\t\tif (row.environment !== 'all') {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tentries[row.key] = JSON.parse(row.value);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\tentries[row.key] = row.value;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.snapshot = {\n\t\t\t\tentries,\n\t\t\t\tfetchedAt: new Date(),\n\t\t\t};\n\t\t} catch (err) {\n\t\t\tconsole.error('[config] failed to refresh:', err);\n\t\t}\n\t}\n\n\tisStale(): boolean {\n\t\tif (!this.snapshot) {\n\t\t\treturn true;\n\t\t}\n\n\t\treturn Date.now() - this.snapshot.fetchedAt.getTime() > this.ttl;\n\t}\n}\n\n// Well-known config keys — extend as needed\nexport const CONFIG_MANAGER_KEY = FONDERIE_CONFIG_KEY;\n","import type { Middleware } from '@fonderie/core';\n\nimport type { RemoteConfigManager } from '../manager';\n\nimport { CONFIG_MANAGER_KEY } from '../manager';\n\n// Injects the config manager into ctx.meta\n// so handlers can read config values per-request\n\nexport function configContextMiddleware(manager: RemoteConfigManager): Middleware {\n\treturn async (ctx, next) => {\n\t\tctx.meta[CONFIG_MANAGER_KEY] = manager;\n\t\treturn next();\n\t};\n}\n\n// Helper to read config from ctx inside a handler\nexport function getConfig(\n\tctx: { meta: Record<string, unknown> },\n\tkey: string,\n\tfallback: unknown = null,\n): unknown {\n\tconst manager = ctx.meta[CONFIG_MANAGER_KEY] as RemoteConfigManager | undefined;\n\tif (!manager) {\n\t\treturn fallback;\n\t}\n\n\treturn manager.get(key, fallback);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACKA,IAAM,sBAAsB;AAwGrB,IAAM,qBAAqB;;;ACpG3B,SAAS,wBAAwB,SAA0C;AACjF,SAAO,OAAO,KAAK,SAAS;AAC3B,QAAI,KAAK,kBAAkB,IAAI;AAC/B,WAAO,KAAK;AAAA,EACb;AACD;AAGO,SAAS,UACf,KACA,KACA,WAAoB,MACV;AACV,QAAM,UAAU,IAAI,KAAK,kBAAkB;AAC3C,MAAI,CAAC,SAAS;AACb,WAAO;AAAA,EACR;AAEA,SAAO,QAAQ,IAAI,KAAK,QAAQ;AACjC;","names":[]}
@@ -1,3 +1,3 @@
1
- export { c as configContextMiddleware, g as getConfig } from '../index-v2hLmm2m.cjs';
1
+ export { c as configContextMiddleware, g as getConfig } from '../index-B2jOiL09.cjs';
2
2
  import '@fonderie/core';
3
3
  import '@fonderie/store';
@@ -1,3 +1,3 @@
1
- export { c as configContextMiddleware, g as getConfig } from '../index-v2hLmm2m.js';
1
+ export { c as configContextMiddleware, g as getConfig } from '../index-B2jOiL09.js';
2
2
  import '@fonderie/core';
3
3
  import '@fonderie/store';
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/manager.ts","../../src/middlewares/config-context.ts"],"sourcesContent":["import type { IStoreAdapter } from '@fonderie/store';\n\nimport type { IRemoteConfigOptions } from './config';\nimport type { IConfigSnapshot } from './types';\n\nconst FONDERIE_CONFIG_KEY = 'fonderie.config.snapshot';\n\nexport class RemoteConfigManager {\n\tprivate snapshot: IConfigSnapshot | null = null;\n\tprivate interval: ReturnType<typeof setInterval> | null = null;\n\tprivate environment: string;\n\tprivate ttl: number;\n\tprivate table: string;\n\n\tconstructor(\n\t\tprivate store: IStoreAdapter,\n\t\toptions: IRemoteConfigOptions = {},\n\t) {\n\t\tthis.ttl = options.ttl ?? 30_000;\n\t\tthis.environment = options.environment ?? process.env['NODE_ENV'] ?? 'development';\n\t\tthis.table = options.table ?? 'fonderie_config';\n\t}\n\n\tasync boot(): Promise<void> {\n\t\tawait this.refresh();\n\t\tthis.interval = setInterval(() => {\n\t\t\tthis.refresh().catch((err) => console.error('[config] refresh error:', err));\n\t\t}, this.ttl);\n\t}\n\n\tstop(): void {\n\t\tif (this.interval) {\n\t\t\tclearInterval(this.interval);\n\t\t\tthis.interval = null;\n\t\t}\n\t}\n\n\t// Get a single value with a typed fallback\n\tget<T>(key: string, fallback: T): T {\n\t\tif (!this.snapshot) {\n\t\t\treturn fallback;\n\t\t}\n\n\t\tconst value = this.snapshot.entries[key];\n\t\treturn value !== undefined ? (value as T) : fallback;\n\t}\n\n\t// Get all entries as a flat record\n\tall(): Record<string, unknown> {\n\t\treturn this.snapshot?.entries ?? {};\n\t}\n\n\tasync refresh(): Promise<void> {\n\t\ttry {\n\t\t\tconst rows = await this.store.query<{\n\t\t\t\tkey: string;\n\t\t\t\tvalue: string;\n\t\t\t\tenvironment: string;\n\t\t\t}>(\n\t\t\t\t`SELECT key, value, environment\n\t\t\t\t FROM ${this.table}\n\t\t\t\t WHERE (environment = $1 OR environment = 'all')\n\t\t\t\t\tAND active = true`,\n\t\t\t\t[this.environment],\n\t\t\t);\n\n\t\t\tconst entries: Record<string, unknown> = {};\n\n\t\t\t// First pass — load 'all' entries as base\n\t\t\tfor (const row of rows) {\n\t\t\t\tif (row.environment === 'all') {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tentries[row.key] = JSON.parse(row.value);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\tentries[row.key] = row.value;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Second pass — environment-specific entries override 'all'\n\t\t\tfor (const row of rows) {\n\t\t\t\tif (row.environment !== 'all') {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tentries[row.key] = JSON.parse(row.value);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\tentries[row.key] = row.value;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.snapshot = {\n\t\t\t\tentries,\n\t\t\t\tfetchedAt: new Date(),\n\t\t\t};\n\t\t} catch (err) {\n\t\t\tconsole.error('[config] failed to refresh:', err);\n\t\t}\n\t}\n\n\tisStale(): boolean {\n\t\tif (!this.snapshot) {\n\t\t\treturn true;\n\t\t}\n\n\t\treturn Date.now() - this.snapshot.fetchedAt.getTime() > this.ttl;\n\t}\n}\n\n// Well-known config keys — extend as needed\nexport const CONFIG_MANAGER_KEY = FONDERIE_CONFIG_KEY;\n","import type { Middleware } from '@fonderie/core';\n\nimport type { RemoteConfigManager } from '../manager';\n\nimport { CONFIG_MANAGER_KEY } from '../manager';\n\n// Injects the config manager into ctx.meta\n// so handlers can read config values per-request\n\nexport function configContextMiddleware(manager: RemoteConfigManager): Middleware {\n\treturn async (ctx, next) => {\n\t\tctx.meta[CONFIG_MANAGER_KEY] = manager;\n\t\treturn next();\n\t};\n}\n\n// Helper to read config from ctx inside a handler\nexport function getConfig(\n\tctx: { meta: Record<string, unknown> },\n\tkey: string,\n\tfallback: unknown = null,\n): unknown {\n\tconst manager = ctx.meta[CONFIG_MANAGER_KEY] as RemoteConfigManager | undefined;\n\tif (!manager) {\n\t\treturn fallback;\n\t}\n\n\treturn manager.get(key, fallback);\n}\n"],"mappings":";AAKA,IAAM,sBAAsB;AAwGrB,IAAM,qBAAqB;;;ACpG3B,SAAS,wBAAwB,SAA0C;AACjF,SAAO,OAAO,KAAK,SAAS;AAC3B,QAAI,KAAK,kBAAkB,IAAI;AAC/B,WAAO,KAAK;AAAA,EACb;AACD;AAGO,SAAS,UACf,KACA,KACA,WAAoB,MACV;AACV,QAAM,UAAU,IAAI,KAAK,kBAAkB;AAC3C,MAAI,CAAC,SAAS;AACb,WAAO;AAAA,EACR;AAEA,SAAO,QAAQ,IAAI,KAAK,QAAQ;AACjC;","names":[]}
1
+ {"version":3,"sources":["../../src/manager.ts","../../src/middlewares/config-context.ts"],"sourcesContent":["import type { IStoreAdapter } from '@fonderie/store';\n\nimport type { IConfigOptions } from './config';\nimport type { IConfigSnapshot } from './types';\n\nconst FONDERIE_CONFIG_KEY = 'fonderie.config.snapshot';\n\nexport class RemoteConfigManager {\n\tprivate snapshot: IConfigSnapshot | null = null;\n\tprivate interval: ReturnType<typeof setInterval> | null = null;\n\tprivate environment: string;\n\tprivate ttl: number;\n\tprivate table: string;\n\n\tconstructor(\n\t\tprivate store: IStoreAdapter,\n\t\toptions: IConfigOptions = {},\n\t) {\n\t\tthis.ttl = options.ttl ?? 30_000;\n\t\tthis.environment = options.environment ?? process.env['NODE_ENV'] ?? 'development';\n\t\tthis.table = options.table ?? 'fonderie_config';\n\t}\n\n\tasync boot(): Promise<void> {\n\t\tawait this.refresh();\n\t\tthis.interval = setInterval(() => {\n\t\t\tthis.refresh().catch((err) => console.error('[config] refresh error:', err));\n\t\t}, this.ttl);\n\t}\n\n\tstop(): void {\n\t\tif (this.interval) {\n\t\t\tclearInterval(this.interval);\n\t\t\tthis.interval = null;\n\t\t}\n\t}\n\n\t// Get a single value with a typed fallback\n\tget<T>(key: string, fallback: T): T {\n\t\tif (!this.snapshot) {\n\t\t\treturn fallback;\n\t\t}\n\n\t\tconst value = this.snapshot.entries[key];\n\t\treturn value !== undefined ? (value as T) : fallback;\n\t}\n\n\t// Get all entries as a flat record\n\tall(): Record<string, unknown> {\n\t\treturn this.snapshot?.entries ?? {};\n\t}\n\n\tasync refresh(): Promise<void> {\n\t\ttry {\n\t\t\tconst rows = await this.store.query<{\n\t\t\t\tkey: string;\n\t\t\t\tvalue: string;\n\t\t\t\tenvironment: string;\n\t\t\t}>(\n\t\t\t\t`SELECT key, value, environment\n\t\t\t\t FROM ${this.table}\n\t\t\t\t WHERE (environment = $1 OR environment = 'all')\n\t\t\t\t\tAND active = true`,\n\t\t\t\t[this.environment],\n\t\t\t);\n\n\t\t\tconst entries: Record<string, unknown> = {};\n\n\t\t\t// First pass — load 'all' entries as base\n\t\t\tfor (const row of rows) {\n\t\t\t\tif (row.environment === 'all') {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tentries[row.key] = JSON.parse(row.value);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\tentries[row.key] = row.value;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Second pass — environment-specific entries override 'all'\n\t\t\tfor (const row of rows) {\n\t\t\t\tif (row.environment !== 'all') {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tentries[row.key] = JSON.parse(row.value);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\tentries[row.key] = row.value;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.snapshot = {\n\t\t\t\tentries,\n\t\t\t\tfetchedAt: new Date(),\n\t\t\t};\n\t\t} catch (err) {\n\t\t\tconsole.error('[config] failed to refresh:', err);\n\t\t}\n\t}\n\n\tisStale(): boolean {\n\t\tif (!this.snapshot) {\n\t\t\treturn true;\n\t\t}\n\n\t\treturn Date.now() - this.snapshot.fetchedAt.getTime() > this.ttl;\n\t}\n}\n\n// Well-known config keys — extend as needed\nexport const CONFIG_MANAGER_KEY = FONDERIE_CONFIG_KEY;\n","import type { Middleware } from '@fonderie/core';\n\nimport type { RemoteConfigManager } from '../manager';\n\nimport { CONFIG_MANAGER_KEY } from '../manager';\n\n// Injects the config manager into ctx.meta\n// so handlers can read config values per-request\n\nexport function configContextMiddleware(manager: RemoteConfigManager): Middleware {\n\treturn async (ctx, next) => {\n\t\tctx.meta[CONFIG_MANAGER_KEY] = manager;\n\t\treturn next();\n\t};\n}\n\n// Helper to read config from ctx inside a handler\nexport function getConfig(\n\tctx: { meta: Record<string, unknown> },\n\tkey: string,\n\tfallback: unknown = null,\n): unknown {\n\tconst manager = ctx.meta[CONFIG_MANAGER_KEY] as RemoteConfigManager | undefined;\n\tif (!manager) {\n\t\treturn fallback;\n\t}\n\n\treturn manager.get(key, fallback);\n}\n"],"mappings":";AAKA,IAAM,sBAAsB;AAwGrB,IAAM,qBAAqB;;;ACpG3B,SAAS,wBAAwB,SAA0C;AACjF,SAAO,OAAO,KAAK,SAAS;AAC3B,QAAI,KAAK,kBAAkB,IAAI;AAC/B,WAAO,KAAK;AAAA,EACb;AACD;AAGO,SAAS,UACf,KACA,KACA,WAAoB,MACV;AACV,QAAM,UAAU,IAAI,KAAK,kBAAkB;AAC3C,MAAI,CAAC,SAAS;AACb,WAAO;AAAA,EACR;AAEA,SAAO,QAAQ,IAAI,KAAK,QAAQ;AACjC;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fonderie/config",
3
- "version": "2.0.0",
3
+ "version": "3.0.0",
4
4
  "description": "DB-backed feature flags and remote config — per-environment overrides, TTL-based hot reload, and a typed getConfig helper usable inside any middleware.",
5
5
  "keywords": [
6
6
  "fonderie-js",
@@ -49,7 +49,7 @@
49
49
  "check": "biome check --write src"
50
50
  },
51
51
  "peerDependencies": {
52
- "@fonderie/core": "^0.2.0",
52
+ "@fonderie/core": "^0.3.0",
53
53
  "@fonderie/store": "^0.1.1"
54
54
  },
55
55
  "devDependencies": {