@smplkit/sdk 1.1.4 → 1.1.6
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/dist/{chunk-GLOLTIGH.js → chunk-RF6LYU4V.js} +6 -6
- package/dist/{chunk-GLOLTIGH.js.map → chunk-RF6LYU4V.js.map} +1 -1
- package/dist/index.cjs +84 -29
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -14
- package/dist/index.d.ts +14 -14
- package/dist/index.js +81 -26
- package/dist/index.js.map +1 -1
- package/dist/runtime-FT745HBO.js +7 -0
- package/package.json +1 -1
- package/dist/runtime-GALKHNKF.js +0 -7
- /package/dist/{runtime-GALKHNKF.js.map → runtime-FT745HBO.js.map} +0 -0
|
@@ -20,7 +20,7 @@ function resolveChain(chain, environment) {
|
|
|
20
20
|
let accumulated = {};
|
|
21
21
|
for (let i = chain.length - 1; i >= 0; i--) {
|
|
22
22
|
const config = chain[i];
|
|
23
|
-
const baseValues = config.
|
|
23
|
+
const baseValues = config.items ?? {};
|
|
24
24
|
const envEntry = (config.environments ?? {})[environment];
|
|
25
25
|
const envValues = envEntry !== null && envEntry !== void 0 && typeof envEntry === "object" && !Array.isArray(envEntry) ? envEntry.values ?? {} : {};
|
|
26
26
|
const configResolved = deepMerge(baseValues, envValues);
|
|
@@ -274,14 +274,14 @@ var ConfigRuntime = class {
|
|
|
274
274
|
const envEntry = chainEntry.environments[this._environment] !== void 0 && chainEntry.environments[this._environment] !== null ? chainEntry.environments[this._environment] : null;
|
|
275
275
|
const envValues = envEntry !== null && typeof envEntry === "object" ? envEntry.values ?? {} : null;
|
|
276
276
|
if (new_value === null || new_value === void 0) {
|
|
277
|
-
delete chainEntry.
|
|
277
|
+
delete chainEntry.items[key];
|
|
278
278
|
if (envValues) delete envValues[key];
|
|
279
279
|
} else if (envValues && key in envValues) {
|
|
280
280
|
envValues[key] = new_value;
|
|
281
|
-
} else if (key in chainEntry.
|
|
282
|
-
chainEntry.
|
|
281
|
+
} else if (key in chainEntry.items) {
|
|
282
|
+
chainEntry.items[key] = new_value;
|
|
283
283
|
} else {
|
|
284
|
-
chainEntry.
|
|
284
|
+
chainEntry.items[key] = new_value;
|
|
285
285
|
}
|
|
286
286
|
}
|
|
287
287
|
const oldCache = this._cache;
|
|
@@ -314,4 +314,4 @@ var ConfigRuntime = class {
|
|
|
314
314
|
export {
|
|
315
315
|
ConfigRuntime
|
|
316
316
|
};
|
|
317
|
-
//# sourceMappingURL=chunk-
|
|
317
|
+
//# sourceMappingURL=chunk-RF6LYU4V.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/config/runtime.ts","../src/config/resolve.ts"],"sourcesContent":["/**\n * ConfigRuntime — runtime-plane value resolution with WebSocket updates.\n *\n * Holds a fully resolved local cache of config values for a specific\n * environment. All value-access methods are synchronous (local reads);\n * only {@link refresh} and {@link close} are async.\n *\n * A background WebSocket connection is maintained for real-time updates.\n * If the WebSocket fails, the runtime operates in cache-only mode and\n * reconnects automatically with exponential backoff.\n */\n\nimport WebSocket from \"ws\";\nimport { resolveChain } from \"./resolve.js\";\nimport type { ChainConfig } from \"./resolve.js\";\nimport type { ConfigChangeEvent, ConfigStats, ConnectionStatus } from \"./runtime-types.js\";\n\n/** @internal */\ninterface ChangeListener {\n callback: (event: ConfigChangeEvent) => void;\n key: string | null;\n}\n\n/** @internal */\ninterface WsConfigChangedMessage {\n type: \"config_changed\";\n config_id: string;\n changes: Array<{\n key: string;\n old_value: unknown;\n new_value: unknown;\n }>;\n}\n\n/** @internal */\ninterface WsConfigDeletedMessage {\n type: \"config_deleted\";\n config_id: string;\n}\n\ntype WsMessage =\n | { type: \"subscribed\"; config_id: string; environment: string }\n | { type: \"error\"; message: string }\n | WsConfigChangedMessage\n | WsConfigDeletedMessage;\n\n/** @internal */\nconst BACKOFF_MS = [1000, 2000, 4000, 8000, 16000, 32000, 60000];\n\n/** @internal Options for constructing a ConfigRuntime. */\nexport interface ConfigRuntimeOptions {\n configKey: string;\n configId: string;\n environment: string;\n chain: ChainConfig[];\n apiKey: string;\n baseUrl: string;\n fetchChain: (() => Promise<ChainConfig[]>) | null;\n}\n\n/**\n * Runtime configuration handle for a specific environment.\n *\n * Obtained by calling {@link Config.connect}. All value-access methods\n * are synchronous and served entirely from a local in-process cache.\n * The cache is populated eagerly on construction and kept current via\n * a background WebSocket connection.\n */\nexport class ConfigRuntime {\n private _cache: Record<string, unknown>;\n private _chain: ChainConfig[];\n private _fetchCount: number;\n private _lastFetchAt: string | null;\n private _closed = false;\n private _wsStatus: ConnectionStatus = \"disconnected\";\n private _ws: InstanceType<typeof WebSocket> | null = null;\n private _reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n private _backoffIndex = 0;\n private _listeners: ChangeListener[] = [];\n\n private readonly _configId: string;\n private readonly _environment: string;\n private readonly _apiKey: string;\n private readonly _baseUrl: string;\n private readonly _fetchChain: (() => Promise<ChainConfig[]>) | null;\n\n /** @internal */\n constructor(options: ConfigRuntimeOptions) {\n this._configId = options.configId;\n this._environment = options.environment;\n this._apiKey = options.apiKey;\n this._baseUrl = options.baseUrl;\n this._fetchChain = options.fetchChain;\n this._chain = options.chain;\n this._cache = resolveChain(options.chain, options.environment);\n this._fetchCount = options.chain.length;\n this._lastFetchAt = new Date().toISOString();\n\n // Start WebSocket in background — non-blocking\n this._connectWebSocket();\n }\n\n // ---- Value access (synchronous, local cache) ----\n\n /**\n * Return the resolved value for `key`, or `defaultValue` if absent.\n *\n * @param key - The config key to look up.\n * @param defaultValue - Returned when the key is not present (default: null).\n */\n get(key: string, defaultValue: unknown = null): unknown {\n return key in this._cache ? this._cache[key] : defaultValue;\n }\n\n /**\n * Return the value as a string, or `defaultValue` if absent or not a string.\n */\n getString(key: string, defaultValue: string | null = null): string | null {\n const value = this._cache[key];\n return typeof value === \"string\" ? value : defaultValue;\n }\n\n /**\n * Return the value as a number, or `defaultValue` if absent or not a number.\n */\n getInt(key: string, defaultValue: number | null = null): number | null {\n const value = this._cache[key];\n return typeof value === \"number\" ? value : defaultValue;\n }\n\n /**\n * Return the value as a boolean, or `defaultValue` if absent or not a boolean.\n */\n getBool(key: string, defaultValue: boolean | null = null): boolean | null {\n const value = this._cache[key];\n return typeof value === \"boolean\" ? value : defaultValue;\n }\n\n /**\n * Return whether `key` is present in the resolved configuration.\n */\n exists(key: string): boolean {\n return key in this._cache;\n }\n\n /**\n * Return a shallow copy of the full resolved configuration.\n */\n getAll(): Record<string, unknown> {\n return { ...this._cache };\n }\n\n // ---- Change listeners ----\n\n /**\n * Register a listener that fires when a config value changes.\n *\n * @param callback - Called with a {@link ConfigChangeEvent} on each change.\n * @param options.key - If provided, the listener fires only for this key.\n * If omitted, the listener fires for all changes.\n */\n onChange(callback: (event: ConfigChangeEvent) => void, options?: { key?: string }): void {\n this._listeners.push({\n callback,\n key: options?.key ?? null,\n });\n }\n\n // ---- Diagnostics ----\n\n /**\n * Return diagnostic statistics for this runtime.\n */\n stats(): ConfigStats {\n return {\n fetchCount: this._fetchCount,\n lastFetchAt: this._lastFetchAt,\n };\n }\n\n /**\n * Return the current WebSocket connection status.\n */\n connectionStatus(): ConnectionStatus {\n return this._wsStatus;\n }\n\n // ---- Lifecycle ----\n\n /**\n * Force a manual refresh of the cached configuration.\n *\n * Re-fetches the full config chain via HTTP, re-resolves values, updates\n * the local cache, and fires listeners for any detected changes.\n *\n * @throws {Error} If no `fetchChain` function was provided on construction.\n */\n async refresh(): Promise<void> {\n if (!this._fetchChain) {\n throw new Error(\"No fetchChain function provided; cannot refresh.\");\n }\n\n const newChain = await this._fetchChain();\n const oldCache = this._cache;\n\n this._chain = newChain;\n this._cache = resolveChain(newChain, this._environment);\n this._fetchCount += newChain.length;\n this._lastFetchAt = new Date().toISOString();\n\n this._diffAndFire(oldCache, this._cache, \"manual\");\n }\n\n /**\n * Close the runtime connection.\n *\n * Shuts down the WebSocket and cancels any pending reconnect timer.\n * Safe to call multiple times.\n */\n async close(): Promise<void> {\n this._closed = true;\n this._wsStatus = \"disconnected\";\n\n if (this._reconnectTimer !== null) {\n clearTimeout(this._reconnectTimer);\n this._reconnectTimer = null;\n }\n\n if (this._ws !== null) {\n this._ws.close();\n this._ws = null;\n }\n }\n\n /**\n * Async dispose support for `await using` (TypeScript 5.2+).\n */\n async [Symbol.asyncDispose](): Promise<void> {\n await this.close();\n }\n\n // ---- WebSocket internals ----\n\n private _buildWsUrl(): string {\n let url = this._baseUrl;\n if (url.startsWith(\"https://\")) {\n url = \"wss://\" + url.slice(\"https://\".length);\n } else if (url.startsWith(\"http://\")) {\n url = \"ws://\" + url.slice(\"http://\".length);\n } else {\n url = \"wss://\" + url;\n }\n url = url.replace(/\\/$/, \"\");\n return `${url}/api/ws/v1/configs?api_key=${this._apiKey}`;\n }\n\n private _connectWebSocket(): void {\n if (this._closed) return;\n\n this._wsStatus = \"connecting\";\n const wsUrl = this._buildWsUrl();\n\n try {\n const ws = new WebSocket(wsUrl);\n this._ws = ws;\n\n ws.on(\"open\", () => {\n if (this._closed) {\n ws.close();\n return;\n }\n this._backoffIndex = 0;\n this._wsStatus = \"connected\";\n ws.send(\n JSON.stringify({\n type: \"subscribe\",\n config_id: this._configId,\n environment: this._environment,\n }),\n );\n });\n\n ws.on(\"message\", (data: WebSocket.RawData) => {\n try {\n const msg = JSON.parse(String(data)) as WsMessage;\n this._handleMessage(msg);\n } catch {\n // ignore unparseable messages\n }\n });\n\n ws.on(\"close\", () => {\n if (!this._closed) {\n this._wsStatus = \"disconnected\";\n this._scheduleReconnect();\n }\n });\n\n ws.on(\"error\", () => {\n // 'close' will fire after 'error'; reconnect is handled there\n });\n } catch {\n if (!this._closed) {\n this._scheduleReconnect();\n }\n }\n }\n\n private _scheduleReconnect(): void {\n if (this._closed) return;\n\n const delay = BACKOFF_MS[Math.min(this._backoffIndex, BACKOFF_MS.length - 1)];\n this._backoffIndex++;\n this._wsStatus = \"connecting\";\n\n this._reconnectTimer = setTimeout(() => {\n this._reconnectTimer = null;\n // On reconnect, resync the cache to pick up changes missed while offline\n if (this._fetchChain) {\n this._fetchChain()\n .then((newChain) => {\n const oldCache = this._cache;\n this._chain = newChain;\n this._cache = resolveChain(newChain, this._environment);\n this._fetchCount += newChain.length;\n this._lastFetchAt = new Date().toISOString();\n this._diffAndFire(oldCache, this._cache, \"manual\");\n })\n .catch(() => {\n // ignore fetch errors during reconnect\n })\n .finally(() => {\n this._connectWebSocket();\n });\n } else {\n this._connectWebSocket();\n }\n }, delay);\n }\n\n private _handleMessage(msg: WsMessage): void {\n if (msg.type === \"config_changed\") {\n this._applyChanges(msg.config_id, msg.changes);\n } else if (msg.type === \"config_deleted\") {\n this._closed = true;\n void this.close();\n }\n }\n\n private _applyChanges(\n configId: string,\n changes: Array<{ key: string; old_value: unknown; new_value: unknown }>,\n ): void {\n const chainEntry = this._chain.find((c) => c.id === configId);\n if (!chainEntry) return;\n\n for (const change of changes) {\n const { key, new_value } = change;\n\n // Get or create the environment entry\n const envEntry =\n chainEntry.environments[this._environment] !== undefined &&\n chainEntry.environments[this._environment] !== null\n ? (chainEntry.environments[this._environment] as Record<string, unknown>)\n : null;\n const envValues =\n envEntry !== null && typeof envEntry === \"object\"\n ? ((envEntry.values ?? {}) as Record<string, unknown>)\n : null;\n\n if (new_value === null || new_value === undefined) {\n // Deletion: remove from base values and env values\n delete chainEntry.values[key];\n if (envValues) delete envValues[key];\n } else if (envValues && key in envValues) {\n // Update existing env-specific override\n envValues[key] = new_value;\n } else if (key in chainEntry.values) {\n // Update existing base value\n chainEntry.values[key] = new_value;\n } else {\n // New key — put in base values\n chainEntry.values[key] = new_value;\n }\n }\n\n const oldCache = this._cache;\n this._cache = resolveChain(this._chain, this._environment);\n this._diffAndFire(oldCache, this._cache, \"websocket\");\n }\n\n private _diffAndFire(\n oldCache: Record<string, unknown>,\n newCache: Record<string, unknown>,\n source: \"websocket\" | \"poll\" | \"manual\",\n ): void {\n const allKeys = new Set([...Object.keys(oldCache), ...Object.keys(newCache)]);\n\n for (const key of allKeys) {\n const oldVal = key in oldCache ? oldCache[key] : null;\n const newVal = key in newCache ? newCache[key] : null;\n\n if (JSON.stringify(oldVal) !== JSON.stringify(newVal)) {\n const event: ConfigChangeEvent = { key, oldValue: oldVal, newValue: newVal, source };\n this._fireListeners(event);\n }\n }\n }\n\n private _fireListeners(event: ConfigChangeEvent): void {\n for (const listener of this._listeners) {\n if (listener.key === null || listener.key === event.key) {\n try {\n listener.callback(event);\n } catch {\n // ignore listener errors to prevent one bad listener from stopping others\n }\n }\n }\n }\n}\n","/**\n * Deep-merge resolution algorithm for config inheritance chains.\n *\n * Mirrors the Python SDK's `_resolver.py` (ADR-024 §2.5–2.6).\n */\n\n/** A single entry in a config inheritance chain (child-to-root ordering). */\nexport interface ChainConfig {\n /** Config UUID. */\n id: string;\n /** Base key-value pairs. */\n values: Record<string, unknown>;\n /**\n * Per-environment overrides.\n * Each entry is `{ values: { key: value, ... } }` — the server wraps\n * environment-specific values in a nested `values` key.\n */\n environments: Record<string, unknown>;\n}\n\n/**\n * Recursively merge two dicts, with `override` taking precedence.\n *\n * Nested dicts are merged recursively. Non-dict values (strings, numbers,\n * booleans, arrays, null) are replaced wholesale.\n */\nexport function deepMerge(\n base: Record<string, unknown>,\n override: Record<string, unknown>,\n): Record<string, unknown> {\n const result: Record<string, unknown> = { ...base };\n for (const [key, value] of Object.entries(override)) {\n if (\n key in result &&\n typeof result[key] === \"object\" &&\n result[key] !== null &&\n !Array.isArray(result[key]) &&\n typeof value === \"object\" &&\n value !== null &&\n !Array.isArray(value)\n ) {\n result[key] = deepMerge(\n result[key] as Record<string, unknown>,\n value as Record<string, unknown>,\n );\n } else {\n result[key] = value;\n }\n }\n return result;\n}\n\n/**\n * Resolve the full configuration for an environment given a config chain.\n *\n * Walks from root (last element) to child (first element), accumulating\n * values via deep merge so that child configs override parent configs.\n *\n * For each config in the chain, base `values` are merged with\n * environment-specific values (env wins), then that result is merged\n * on top of the accumulated parent result (child wins over parent).\n *\n * @param chain - Ordered list of config data from child (index 0) to root ancestor (last).\n * @param environment - The environment key to resolve for.\n */\nexport function resolveChain(chain: ChainConfig[], environment: string): Record<string, unknown> {\n let accumulated: Record<string, unknown> = {};\n\n // Walk from root to child (reverse order — chain is child-to-root)\n for (let i = chain.length - 1; i >= 0; i--) {\n const config = chain[i];\n const baseValues: Record<string, unknown> = config.values ?? {};\n\n // Environments are stored as { env_name: { values: { key: val } } }\n const envEntry = (config.environments ?? {})[environment];\n const envValues: Record<string, unknown> =\n envEntry !== null &&\n envEntry !== undefined &&\n typeof envEntry === \"object\" &&\n !Array.isArray(envEntry)\n ? (((envEntry as Record<string, unknown>).values ?? {}) as Record<string, unknown>)\n : {};\n\n // Merge environment overrides on top of base values (env wins)\n const configResolved = deepMerge(baseValues, envValues);\n\n // Merge this config's resolved values on top of accumulated parent values (child wins)\n accumulated = deepMerge(accumulated, configResolved);\n }\n\n return accumulated;\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/config/runtime.ts","../src/config/resolve.ts"],"sourcesContent":["/**\n * ConfigRuntime — runtime-plane value resolution with WebSocket updates.\n *\n * Holds a fully resolved local cache of config values for a specific\n * environment. All value-access methods are synchronous (local reads);\n * only {@link refresh} and {@link close} are async.\n *\n * A background WebSocket connection is maintained for real-time updates.\n * If the WebSocket fails, the runtime operates in cache-only mode and\n * reconnects automatically with exponential backoff.\n */\n\nimport WebSocket from \"ws\";\nimport { resolveChain } from \"./resolve.js\";\nimport type { ChainConfig } from \"./resolve.js\";\nimport type { ConfigChangeEvent, ConfigStats, ConnectionStatus } from \"./runtime-types.js\";\n\n/** @internal */\ninterface ChangeListener {\n callback: (event: ConfigChangeEvent) => void;\n key: string | null;\n}\n\n/** @internal */\ninterface WsConfigChangedMessage {\n type: \"config_changed\";\n config_id: string;\n changes: Array<{\n key: string;\n old_value: unknown;\n new_value: unknown;\n }>;\n}\n\n/** @internal */\ninterface WsConfigDeletedMessage {\n type: \"config_deleted\";\n config_id: string;\n}\n\ntype WsMessage =\n | { type: \"subscribed\"; config_id: string; environment: string }\n | { type: \"error\"; message: string }\n | WsConfigChangedMessage\n | WsConfigDeletedMessage;\n\n/** @internal */\nconst BACKOFF_MS = [1000, 2000, 4000, 8000, 16000, 32000, 60000];\n\n/** @internal Options for constructing a ConfigRuntime. */\nexport interface ConfigRuntimeOptions {\n configKey: string;\n configId: string;\n environment: string;\n chain: ChainConfig[];\n apiKey: string;\n baseUrl: string;\n fetchChain: (() => Promise<ChainConfig[]>) | null;\n}\n\n/**\n * Runtime configuration handle for a specific environment.\n *\n * Obtained by calling {@link Config.connect}. All value-access methods\n * are synchronous and served entirely from a local in-process cache.\n * The cache is populated eagerly on construction and kept current via\n * a background WebSocket connection.\n */\nexport class ConfigRuntime {\n private _cache: Record<string, unknown>;\n private _chain: ChainConfig[];\n private _fetchCount: number;\n private _lastFetchAt: string | null;\n private _closed = false;\n private _wsStatus: ConnectionStatus = \"disconnected\";\n private _ws: InstanceType<typeof WebSocket> | null = null;\n private _reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n private _backoffIndex = 0;\n private _listeners: ChangeListener[] = [];\n\n private readonly _configId: string;\n private readonly _environment: string;\n private readonly _apiKey: string;\n private readonly _baseUrl: string;\n private readonly _fetchChain: (() => Promise<ChainConfig[]>) | null;\n\n /** @internal */\n constructor(options: ConfigRuntimeOptions) {\n this._configId = options.configId;\n this._environment = options.environment;\n this._apiKey = options.apiKey;\n this._baseUrl = options.baseUrl;\n this._fetchChain = options.fetchChain;\n this._chain = options.chain;\n this._cache = resolveChain(options.chain, options.environment);\n this._fetchCount = options.chain.length;\n this._lastFetchAt = new Date().toISOString();\n\n // Start WebSocket in background — non-blocking\n this._connectWebSocket();\n }\n\n // ---- Value access (synchronous, local cache) ----\n\n /**\n * Return the resolved value for `key`, or `defaultValue` if absent.\n *\n * @param key - The config key to look up.\n * @param defaultValue - Returned when the key is not present (default: null).\n */\n get(key: string, defaultValue: unknown = null): unknown {\n return key in this._cache ? this._cache[key] : defaultValue;\n }\n\n /**\n * Return the value as a string, or `defaultValue` if absent or not a string.\n */\n getString(key: string, defaultValue: string | null = null): string | null {\n const value = this._cache[key];\n return typeof value === \"string\" ? value : defaultValue;\n }\n\n /**\n * Return the value as a number, or `defaultValue` if absent or not a number.\n */\n getInt(key: string, defaultValue: number | null = null): number | null {\n const value = this._cache[key];\n return typeof value === \"number\" ? value : defaultValue;\n }\n\n /**\n * Return the value as a boolean, or `defaultValue` if absent or not a boolean.\n */\n getBool(key: string, defaultValue: boolean | null = null): boolean | null {\n const value = this._cache[key];\n return typeof value === \"boolean\" ? value : defaultValue;\n }\n\n /**\n * Return whether `key` is present in the resolved configuration.\n */\n exists(key: string): boolean {\n return key in this._cache;\n }\n\n /**\n * Return a shallow copy of the full resolved configuration.\n */\n getAll(): Record<string, unknown> {\n return { ...this._cache };\n }\n\n // ---- Change listeners ----\n\n /**\n * Register a listener that fires when a config value changes.\n *\n * @param callback - Called with a {@link ConfigChangeEvent} on each change.\n * @param options.key - If provided, the listener fires only for this key.\n * If omitted, the listener fires for all changes.\n */\n onChange(callback: (event: ConfigChangeEvent) => void, options?: { key?: string }): void {\n this._listeners.push({\n callback,\n key: options?.key ?? null,\n });\n }\n\n // ---- Diagnostics ----\n\n /**\n * Return diagnostic statistics for this runtime.\n */\n stats(): ConfigStats {\n return {\n fetchCount: this._fetchCount,\n lastFetchAt: this._lastFetchAt,\n };\n }\n\n /**\n * Return the current WebSocket connection status.\n */\n connectionStatus(): ConnectionStatus {\n return this._wsStatus;\n }\n\n // ---- Lifecycle ----\n\n /**\n * Force a manual refresh of the cached configuration.\n *\n * Re-fetches the full config chain via HTTP, re-resolves values, updates\n * the local cache, and fires listeners for any detected changes.\n *\n * @throws {Error} If no `fetchChain` function was provided on construction.\n */\n async refresh(): Promise<void> {\n if (!this._fetchChain) {\n throw new Error(\"No fetchChain function provided; cannot refresh.\");\n }\n\n const newChain = await this._fetchChain();\n const oldCache = this._cache;\n\n this._chain = newChain;\n this._cache = resolveChain(newChain, this._environment);\n this._fetchCount += newChain.length;\n this._lastFetchAt = new Date().toISOString();\n\n this._diffAndFire(oldCache, this._cache, \"manual\");\n }\n\n /**\n * Close the runtime connection.\n *\n * Shuts down the WebSocket and cancels any pending reconnect timer.\n * Safe to call multiple times.\n */\n async close(): Promise<void> {\n this._closed = true;\n this._wsStatus = \"disconnected\";\n\n if (this._reconnectTimer !== null) {\n clearTimeout(this._reconnectTimer);\n this._reconnectTimer = null;\n }\n\n if (this._ws !== null) {\n this._ws.close();\n this._ws = null;\n }\n }\n\n /**\n * Async dispose support for `await using` (TypeScript 5.2+).\n */\n async [Symbol.asyncDispose](): Promise<void> {\n await this.close();\n }\n\n // ---- WebSocket internals ----\n\n private _buildWsUrl(): string {\n let url = this._baseUrl;\n if (url.startsWith(\"https://\")) {\n url = \"wss://\" + url.slice(\"https://\".length);\n } else if (url.startsWith(\"http://\")) {\n url = \"ws://\" + url.slice(\"http://\".length);\n } else {\n url = \"wss://\" + url;\n }\n url = url.replace(/\\/$/, \"\");\n return `${url}/api/ws/v1/configs?api_key=${this._apiKey}`;\n }\n\n private _connectWebSocket(): void {\n if (this._closed) return;\n\n this._wsStatus = \"connecting\";\n const wsUrl = this._buildWsUrl();\n\n try {\n const ws = new WebSocket(wsUrl);\n this._ws = ws;\n\n ws.on(\"open\", () => {\n if (this._closed) {\n ws.close();\n return;\n }\n this._backoffIndex = 0;\n this._wsStatus = \"connected\";\n ws.send(\n JSON.stringify({\n type: \"subscribe\",\n config_id: this._configId,\n environment: this._environment,\n }),\n );\n });\n\n ws.on(\"message\", (data: WebSocket.RawData) => {\n try {\n const msg = JSON.parse(String(data)) as WsMessage;\n this._handleMessage(msg);\n } catch {\n // ignore unparseable messages\n }\n });\n\n ws.on(\"close\", () => {\n if (!this._closed) {\n this._wsStatus = \"disconnected\";\n this._scheduleReconnect();\n }\n });\n\n ws.on(\"error\", () => {\n // 'close' will fire after 'error'; reconnect is handled there\n });\n } catch {\n if (!this._closed) {\n this._scheduleReconnect();\n }\n }\n }\n\n private _scheduleReconnect(): void {\n if (this._closed) return;\n\n const delay = BACKOFF_MS[Math.min(this._backoffIndex, BACKOFF_MS.length - 1)];\n this._backoffIndex++;\n this._wsStatus = \"connecting\";\n\n this._reconnectTimer = setTimeout(() => {\n this._reconnectTimer = null;\n // On reconnect, resync the cache to pick up changes missed while offline\n if (this._fetchChain) {\n this._fetchChain()\n .then((newChain) => {\n const oldCache = this._cache;\n this._chain = newChain;\n this._cache = resolveChain(newChain, this._environment);\n this._fetchCount += newChain.length;\n this._lastFetchAt = new Date().toISOString();\n this._diffAndFire(oldCache, this._cache, \"manual\");\n })\n .catch(() => {\n // ignore fetch errors during reconnect\n })\n .finally(() => {\n this._connectWebSocket();\n });\n } else {\n this._connectWebSocket();\n }\n }, delay);\n }\n\n private _handleMessage(msg: WsMessage): void {\n if (msg.type === \"config_changed\") {\n this._applyChanges(msg.config_id, msg.changes);\n } else if (msg.type === \"config_deleted\") {\n this._closed = true;\n void this.close();\n }\n }\n\n private _applyChanges(\n configId: string,\n changes: Array<{ key: string; old_value: unknown; new_value: unknown }>,\n ): void {\n const chainEntry = this._chain.find((c) => c.id === configId);\n if (!chainEntry) return;\n\n for (const change of changes) {\n const { key, new_value } = change;\n\n // Get or create the environment entry\n const envEntry =\n chainEntry.environments[this._environment] !== undefined &&\n chainEntry.environments[this._environment] !== null\n ? (chainEntry.environments[this._environment] as Record<string, unknown>)\n : null;\n const envValues =\n envEntry !== null && typeof envEntry === \"object\"\n ? ((envEntry.values ?? {}) as Record<string, unknown>)\n : null;\n\n if (new_value === null || new_value === undefined) {\n // Deletion: remove from base items and env values\n delete chainEntry.items[key];\n if (envValues) delete envValues[key];\n } else if (envValues && key in envValues) {\n // Update existing env-specific override\n envValues[key] = new_value;\n } else if (key in chainEntry.items) {\n // Update existing base value\n chainEntry.items[key] = new_value;\n } else {\n // New key — put in base items\n chainEntry.items[key] = new_value;\n }\n }\n\n const oldCache = this._cache;\n this._cache = resolveChain(this._chain, this._environment);\n this._diffAndFire(oldCache, this._cache, \"websocket\");\n }\n\n private _diffAndFire(\n oldCache: Record<string, unknown>,\n newCache: Record<string, unknown>,\n source: \"websocket\" | \"poll\" | \"manual\",\n ): void {\n const allKeys = new Set([...Object.keys(oldCache), ...Object.keys(newCache)]);\n\n for (const key of allKeys) {\n const oldVal = key in oldCache ? oldCache[key] : null;\n const newVal = key in newCache ? newCache[key] : null;\n\n if (JSON.stringify(oldVal) !== JSON.stringify(newVal)) {\n const event: ConfigChangeEvent = { key, oldValue: oldVal, newValue: newVal, source };\n this._fireListeners(event);\n }\n }\n }\n\n private _fireListeners(event: ConfigChangeEvent): void {\n for (const listener of this._listeners) {\n if (listener.key === null || listener.key === event.key) {\n try {\n listener.callback(event);\n } catch {\n // ignore listener errors to prevent one bad listener from stopping others\n }\n }\n }\n }\n}\n","/**\n * Deep-merge resolution algorithm for config inheritance chains.\n *\n * Mirrors the Python SDK's `_resolver.py` (ADR-024 §2.5–2.6).\n */\n\n/** A single entry in a config inheritance chain (child-to-root ordering). */\nexport interface ChainConfig {\n /** Config UUID. */\n id: string;\n /** Base key-value pairs (unwrapped from typed item definitions). */\n items: Record<string, unknown>;\n /**\n * Per-environment overrides.\n * Each entry is `{ values: { key: value, ... } }` — values are already\n * unwrapped from the server's `{ value: raw }` wrapper by the client layer.\n */\n environments: Record<string, unknown>;\n}\n\n/**\n * Recursively merge two dicts, with `override` taking precedence.\n *\n * Nested dicts are merged recursively. Non-dict values (strings, numbers,\n * booleans, arrays, null) are replaced wholesale.\n */\nexport function deepMerge(\n base: Record<string, unknown>,\n override: Record<string, unknown>,\n): Record<string, unknown> {\n const result: Record<string, unknown> = { ...base };\n for (const [key, value] of Object.entries(override)) {\n if (\n key in result &&\n typeof result[key] === \"object\" &&\n result[key] !== null &&\n !Array.isArray(result[key]) &&\n typeof value === \"object\" &&\n value !== null &&\n !Array.isArray(value)\n ) {\n result[key] = deepMerge(\n result[key] as Record<string, unknown>,\n value as Record<string, unknown>,\n );\n } else {\n result[key] = value;\n }\n }\n return result;\n}\n\n/**\n * Resolve the full configuration for an environment given a config chain.\n *\n * Walks from root (last element) to child (first element), accumulating\n * values via deep merge so that child configs override parent configs.\n *\n * For each config in the chain, base `values` are merged with\n * environment-specific values (env wins), then that result is merged\n * on top of the accumulated parent result (child wins over parent).\n *\n * @param chain - Ordered list of config data from child (index 0) to root ancestor (last).\n * @param environment - The environment key to resolve for.\n */\nexport function resolveChain(chain: ChainConfig[], environment: string): Record<string, unknown> {\n let accumulated: Record<string, unknown> = {};\n\n // Walk from root to child (reverse order — chain is child-to-root)\n for (let i = chain.length - 1; i >= 0; i--) {\n const config = chain[i];\n const baseValues: Record<string, unknown> = config.items ?? {};\n\n // Environments are stored as { env_name: { values: { key: val } } }\n const envEntry = (config.environments ?? {})[environment];\n const envValues: Record<string, unknown> =\n envEntry !== null &&\n envEntry !== undefined &&\n typeof envEntry === \"object\" &&\n !Array.isArray(envEntry)\n ? (((envEntry as Record<string, unknown>).values ?? {}) as Record<string, unknown>)\n : {};\n\n // Merge environment overrides on top of base values (env wins)\n const configResolved = deepMerge(baseValues, envValues);\n\n // Merge this config's resolved values on top of accumulated parent values (child wins)\n accumulated = deepMerge(accumulated, configResolved);\n }\n\n return accumulated;\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.cjs
CHANGED
|
@@ -49,7 +49,7 @@ function resolveChain(chain, environment) {
|
|
|
49
49
|
let accumulated = {};
|
|
50
50
|
for (let i = chain.length - 1; i >= 0; i--) {
|
|
51
51
|
const config = chain[i];
|
|
52
|
-
const baseValues = config.
|
|
52
|
+
const baseValues = config.items ?? {};
|
|
53
53
|
const envEntry = (config.environments ?? {})[environment];
|
|
54
54
|
const envValues = envEntry !== null && envEntry !== void 0 && typeof envEntry === "object" && !Array.isArray(envEntry) ? envEntry.values ?? {} : {};
|
|
55
55
|
const configResolved = deepMerge(baseValues, envValues);
|
|
@@ -318,14 +318,14 @@ var init_runtime = __esm({
|
|
|
318
318
|
const envEntry = chainEntry.environments[this._environment] !== void 0 && chainEntry.environments[this._environment] !== null ? chainEntry.environments[this._environment] : null;
|
|
319
319
|
const envValues = envEntry !== null && typeof envEntry === "object" ? envEntry.values ?? {} : null;
|
|
320
320
|
if (new_value === null || new_value === void 0) {
|
|
321
|
-
delete chainEntry.
|
|
321
|
+
delete chainEntry.items[key];
|
|
322
322
|
if (envValues) delete envValues[key];
|
|
323
323
|
} else if (envValues && key in envValues) {
|
|
324
324
|
envValues[key] = new_value;
|
|
325
|
-
} else if (key in chainEntry.
|
|
326
|
-
chainEntry.
|
|
325
|
+
} else if (key in chainEntry.items) {
|
|
326
|
+
chainEntry.items[key] = new_value;
|
|
327
327
|
} else {
|
|
328
|
-
chainEntry.
|
|
328
|
+
chainEntry.items[key] = new_value;
|
|
329
329
|
}
|
|
330
330
|
}
|
|
331
331
|
const oldCache = this._cache;
|
|
@@ -438,12 +438,12 @@ var Config = class {
|
|
|
438
438
|
description;
|
|
439
439
|
/** Parent config UUID, or null if this is a root config. */
|
|
440
440
|
parent;
|
|
441
|
-
/** Base key-value pairs. */
|
|
442
|
-
|
|
441
|
+
/** Base key-value pairs (unwrapped from typed item definitions). */
|
|
442
|
+
items;
|
|
443
443
|
/**
|
|
444
444
|
* Per-environment overrides.
|
|
445
|
-
* Stored as `{ env_name: { values: { key: value } } }`
|
|
446
|
-
* server's
|
|
445
|
+
* Stored as `{ env_name: { values: { key: value } } }` — values are
|
|
446
|
+
* unwrapped from the server's `{ value: raw }` wrapper.
|
|
447
447
|
*/
|
|
448
448
|
environments;
|
|
449
449
|
/** When the config was created, or null if unavailable. */
|
|
@@ -463,7 +463,7 @@ var Config = class {
|
|
|
463
463
|
this.name = fields.name;
|
|
464
464
|
this.description = fields.description;
|
|
465
465
|
this.parent = fields.parent;
|
|
466
|
-
this.
|
|
466
|
+
this.items = fields.items;
|
|
467
467
|
this.environments = fields.environments;
|
|
468
468
|
this.createdAt = fields.createdAt;
|
|
469
469
|
this.updatedAt = fields.updatedAt;
|
|
@@ -476,7 +476,7 @@ var Config = class {
|
|
|
476
476
|
*
|
|
477
477
|
* @param options.name - New display name.
|
|
478
478
|
* @param options.description - New description (pass empty string to clear).
|
|
479
|
-
* @param options.
|
|
479
|
+
* @param options.items - New base values (replaces entirely).
|
|
480
480
|
* @param options.environments - New environments dict (replaces entirely).
|
|
481
481
|
*/
|
|
482
482
|
async update(options) {
|
|
@@ -486,12 +486,12 @@ var Config = class {
|
|
|
486
486
|
key: this.key,
|
|
487
487
|
description: options.description !== void 0 ? options.description : this.description,
|
|
488
488
|
parent: this.parent,
|
|
489
|
-
|
|
489
|
+
items: options.items ?? this.items,
|
|
490
490
|
environments: options.environments ?? this.environments
|
|
491
491
|
});
|
|
492
492
|
this.name = updated.name;
|
|
493
493
|
this.description = updated.description;
|
|
494
|
-
this.
|
|
494
|
+
this.items = updated.items;
|
|
495
495
|
this.environments = updated.environments;
|
|
496
496
|
this.updatedAt = updated.updatedAt;
|
|
497
497
|
}
|
|
@@ -500,19 +500,19 @@ var Config = class {
|
|
|
500
500
|
*
|
|
501
501
|
* When `environment` is provided, replaces that environment's `values`
|
|
502
502
|
* sub-dict (other environments are preserved). When omitted, replaces
|
|
503
|
-
* the base `
|
|
503
|
+
* the base `items`.
|
|
504
504
|
*
|
|
505
505
|
* @param values - The complete set of values to set.
|
|
506
506
|
* @param environment - Target environment, or omit for base values.
|
|
507
507
|
*/
|
|
508
508
|
async setValues(values, environment) {
|
|
509
|
-
let
|
|
509
|
+
let newItems;
|
|
510
510
|
let newEnvs;
|
|
511
511
|
if (environment === void 0) {
|
|
512
|
-
|
|
512
|
+
newItems = values;
|
|
513
513
|
newEnvs = this.environments;
|
|
514
514
|
} else {
|
|
515
|
-
|
|
515
|
+
newItems = this.items;
|
|
516
516
|
const existingEntry = typeof this.environments[environment] === "object" && this.environments[environment] !== null ? { ...this.environments[environment] } : {};
|
|
517
517
|
existingEntry.values = values;
|
|
518
518
|
newEnvs = { ...this.environments, [environment]: existingEntry };
|
|
@@ -523,10 +523,10 @@ var Config = class {
|
|
|
523
523
|
key: this.key,
|
|
524
524
|
description: this.description,
|
|
525
525
|
parent: this.parent,
|
|
526
|
-
|
|
526
|
+
items: newItems,
|
|
527
527
|
environments: newEnvs
|
|
528
528
|
});
|
|
529
|
-
this.
|
|
529
|
+
this.items = updated.items;
|
|
530
530
|
this.environments = updated.environments;
|
|
531
531
|
this.updatedAt = updated.updatedAt;
|
|
532
532
|
}
|
|
@@ -541,7 +541,7 @@ var Config = class {
|
|
|
541
541
|
*/
|
|
542
542
|
async setValue(key, value, environment) {
|
|
543
543
|
if (environment === void 0) {
|
|
544
|
-
const merged = { ...this.
|
|
544
|
+
const merged = { ...this.items, [key]: value };
|
|
545
545
|
await this.setValues(merged);
|
|
546
546
|
} else {
|
|
547
547
|
const envEntry = typeof this.environments[environment] === "object" && this.environments[environment] !== null ? this.environments[environment] : {};
|
|
@@ -596,13 +596,13 @@ var Config = class {
|
|
|
596
596
|
* @internal
|
|
597
597
|
*/
|
|
598
598
|
async _buildChain(_timeout) {
|
|
599
|
-
const chain = [{ id: this.id,
|
|
599
|
+
const chain = [{ id: this.id, items: this.items, environments: this.environments }];
|
|
600
600
|
let parentId = this.parent;
|
|
601
601
|
while (parentId !== null) {
|
|
602
602
|
const parentConfig = await this._client.get({ id: parentId });
|
|
603
603
|
chain.push({
|
|
604
604
|
id: parentConfig.id,
|
|
605
|
-
|
|
605
|
+
items: parentConfig.items,
|
|
606
606
|
environments: parentConfig.environments
|
|
607
607
|
});
|
|
608
608
|
parentId = parentConfig.parent;
|
|
@@ -616,6 +616,30 @@ var Config = class {
|
|
|
616
616
|
|
|
617
617
|
// src/config/client.ts
|
|
618
618
|
var BASE_URL = "https://config.smplkit.com";
|
|
619
|
+
function extractItemValues(items) {
|
|
620
|
+
if (!items) return {};
|
|
621
|
+
const result = {};
|
|
622
|
+
for (const [key, item] of Object.entries(items)) {
|
|
623
|
+
result[key] = item && typeof item === "object" && "value" in item ? item.value : item;
|
|
624
|
+
}
|
|
625
|
+
return result;
|
|
626
|
+
}
|
|
627
|
+
function extractEnvironments(environments) {
|
|
628
|
+
if (!environments) return {};
|
|
629
|
+
const result = {};
|
|
630
|
+
for (const [envName, envEntry] of Object.entries(environments)) {
|
|
631
|
+
if (envEntry && typeof envEntry === "object" && envEntry.values) {
|
|
632
|
+
const unwrapped = {};
|
|
633
|
+
for (const [key, item] of Object.entries(envEntry.values)) {
|
|
634
|
+
unwrapped[key] = item && typeof item === "object" && "value" in item ? item.value : item;
|
|
635
|
+
}
|
|
636
|
+
result[envName] = { values: unwrapped };
|
|
637
|
+
} else {
|
|
638
|
+
result[envName] = envEntry;
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
return result;
|
|
642
|
+
}
|
|
619
643
|
function resourceToConfig(resource, client) {
|
|
620
644
|
const attrs = resource.attributes;
|
|
621
645
|
return new Config(client, {
|
|
@@ -624,8 +648,10 @@ function resourceToConfig(resource, client) {
|
|
|
624
648
|
name: attrs.name,
|
|
625
649
|
description: attrs.description ?? null,
|
|
626
650
|
parent: attrs.parent ?? null,
|
|
627
|
-
|
|
628
|
-
environments:
|
|
651
|
+
items: extractItemValues(attrs.items),
|
|
652
|
+
environments: extractEnvironments(
|
|
653
|
+
attrs.environments
|
|
654
|
+
),
|
|
629
655
|
createdAt: attrs.created_at ? new Date(attrs.created_at) : null,
|
|
630
656
|
updatedAt: attrs.updated_at ? new Date(attrs.updated_at) : null
|
|
631
657
|
});
|
|
@@ -654,6 +680,35 @@ function wrapFetchError(err) {
|
|
|
654
680
|
`Request failed: ${err instanceof Error ? err.message : String(err)}`
|
|
655
681
|
);
|
|
656
682
|
}
|
|
683
|
+
function wrapItemValues(values) {
|
|
684
|
+
if (!values) return null;
|
|
685
|
+
const result = {};
|
|
686
|
+
for (const [key, val] of Object.entries(values)) {
|
|
687
|
+
result[key] = { value: val };
|
|
688
|
+
}
|
|
689
|
+
return result;
|
|
690
|
+
}
|
|
691
|
+
function wrapEnvironments(environments) {
|
|
692
|
+
if (!environments) return null;
|
|
693
|
+
const result = {};
|
|
694
|
+
for (const [envName, envEntry] of Object.entries(environments)) {
|
|
695
|
+
if (envEntry && typeof envEntry === "object" && !Array.isArray(envEntry)) {
|
|
696
|
+
const entry = envEntry;
|
|
697
|
+
if (entry.values && typeof entry.values === "object" && !Array.isArray(entry.values)) {
|
|
698
|
+
const wrapped = {};
|
|
699
|
+
for (const [key, val] of Object.entries(entry.values)) {
|
|
700
|
+
wrapped[key] = { value: val };
|
|
701
|
+
}
|
|
702
|
+
result[envName] = { ...entry, values: wrapped };
|
|
703
|
+
} else {
|
|
704
|
+
result[envName] = envEntry;
|
|
705
|
+
}
|
|
706
|
+
} else {
|
|
707
|
+
result[envName] = envEntry;
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
return result;
|
|
711
|
+
}
|
|
657
712
|
function buildRequestBody(options) {
|
|
658
713
|
const attrs = {
|
|
659
714
|
name: options.name
|
|
@@ -661,10 +716,10 @@ function buildRequestBody(options) {
|
|
|
661
716
|
if (options.key !== void 0) attrs.key = options.key;
|
|
662
717
|
if (options.description !== void 0) attrs.description = options.description;
|
|
663
718
|
if (options.parent !== void 0) attrs.parent = options.parent;
|
|
664
|
-
if (options.
|
|
665
|
-
attrs.
|
|
719
|
+
if (options.items !== void 0)
|
|
720
|
+
attrs.items = wrapItemValues(options.items);
|
|
666
721
|
if (options.environments !== void 0)
|
|
667
|
-
attrs.environments = options.environments;
|
|
722
|
+
attrs.environments = wrapEnvironments(options.environments);
|
|
668
723
|
return {
|
|
669
724
|
data: {
|
|
670
725
|
id: options.id ?? null,
|
|
@@ -747,7 +802,7 @@ var ConfigClient = class {
|
|
|
747
802
|
key: options.key,
|
|
748
803
|
description: options.description,
|
|
749
804
|
parent: options.parent,
|
|
750
|
-
|
|
805
|
+
items: options.items
|
|
751
806
|
});
|
|
752
807
|
let data;
|
|
753
808
|
try {
|
|
@@ -790,7 +845,7 @@ var ConfigClient = class {
|
|
|
790
845
|
key: payload.key,
|
|
791
846
|
description: payload.description,
|
|
792
847
|
parent: payload.parent,
|
|
793
|
-
|
|
848
|
+
items: payload.items,
|
|
794
849
|
environments: payload.environments
|
|
795
850
|
});
|
|
796
851
|
let data;
|