@smplkit/sdk 1.1.9 → 1.2.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/dist/{chunk-RF6LYU4V.js → chunk-2VYY5OMH.js} +36 -112
- package/dist/chunk-2VYY5OMH.js.map +1 -0
- package/dist/index.cjs +1329 -113
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +442 -30
- package/dist/index.d.ts +442 -30
- package/dist/index.js +1282 -4
- package/dist/index.js.map +1 -1
- package/dist/runtime-MIIY5ZNG.js +7 -0
- package/package.json +2 -1
- package/dist/chunk-RF6LYU4V.js.map +0 -1
- package/dist/runtime-FT745HBO.js +0 -7
- /package/dist/{runtime-FT745HBO.js.map → runtime-MIIY5ZNG.js.map} +0 -0
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
// src/config/runtime.ts
|
|
2
|
-
import WebSocket from "ws";
|
|
3
|
-
|
|
4
1
|
// src/config/resolve.ts
|
|
5
2
|
function deepMerge(base, override) {
|
|
6
3
|
const result = { ...base };
|
|
@@ -30,35 +27,29 @@ function resolveChain(chain, environment) {
|
|
|
30
27
|
}
|
|
31
28
|
|
|
32
29
|
// src/config/runtime.ts
|
|
33
|
-
var BACKOFF_MS = [1e3, 2e3, 4e3, 8e3, 16e3, 32e3, 6e4];
|
|
34
30
|
var ConfigRuntime = class {
|
|
35
31
|
_cache;
|
|
36
32
|
_chain;
|
|
37
33
|
_fetchCount;
|
|
38
34
|
_lastFetchAt;
|
|
39
35
|
_closed = false;
|
|
40
|
-
_wsStatus = "disconnected";
|
|
41
|
-
_ws = null;
|
|
42
|
-
_reconnectTimer = null;
|
|
43
|
-
_backoffIndex = 0;
|
|
44
36
|
_listeners = [];
|
|
45
|
-
_configId;
|
|
46
37
|
_environment;
|
|
47
|
-
_apiKey;
|
|
48
|
-
_baseUrl;
|
|
49
38
|
_fetchChain;
|
|
39
|
+
_sharedWs = null;
|
|
50
40
|
/** @internal */
|
|
51
41
|
constructor(options) {
|
|
52
|
-
this._configId = options.configId;
|
|
53
42
|
this._environment = options.environment;
|
|
54
|
-
this._apiKey = options.apiKey;
|
|
55
|
-
this._baseUrl = options.baseUrl;
|
|
56
43
|
this._fetchChain = options.fetchChain;
|
|
57
44
|
this._chain = options.chain;
|
|
58
45
|
this._cache = resolveChain(options.chain, options.environment);
|
|
59
46
|
this._fetchCount = options.chain.length;
|
|
60
47
|
this._lastFetchAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
61
|
-
|
|
48
|
+
if (options.sharedWs) {
|
|
49
|
+
this._sharedWs = options.sharedWs;
|
|
50
|
+
this._sharedWs.on("config_changed", this._handleConfigChanged);
|
|
51
|
+
this._sharedWs.on("config_deleted", this._handleConfigDeleted);
|
|
52
|
+
}
|
|
62
53
|
}
|
|
63
54
|
// ---- Value access (synchronous, local cache) ----
|
|
64
55
|
/**
|
|
@@ -131,7 +122,10 @@ var ConfigRuntime = class {
|
|
|
131
122
|
* Return the current WebSocket connection status.
|
|
132
123
|
*/
|
|
133
124
|
connectionStatus() {
|
|
134
|
-
|
|
125
|
+
if (this._sharedWs) {
|
|
126
|
+
return this._sharedWs.connectionStatus;
|
|
127
|
+
}
|
|
128
|
+
return "disconnected";
|
|
135
129
|
}
|
|
136
130
|
// ---- Lifecycle ----
|
|
137
131
|
/**
|
|
@@ -157,19 +151,14 @@ var ConfigRuntime = class {
|
|
|
157
151
|
/**
|
|
158
152
|
* Close the runtime connection.
|
|
159
153
|
*
|
|
160
|
-
*
|
|
161
|
-
* Safe to call multiple times.
|
|
154
|
+
* Unregisters from the shared WebSocket. Safe to call multiple times.
|
|
162
155
|
*/
|
|
163
156
|
async close() {
|
|
164
157
|
this._closed = true;
|
|
165
|
-
this.
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
this.
|
|
169
|
-
}
|
|
170
|
-
if (this._ws !== null) {
|
|
171
|
-
this._ws.close();
|
|
172
|
-
this._ws = null;
|
|
158
|
+
if (this._sharedWs !== null) {
|
|
159
|
+
this._sharedWs.off("config_changed", this._handleConfigChanged);
|
|
160
|
+
this._sharedWs.off("config_deleted", this._handleConfigDeleted);
|
|
161
|
+
this._sharedWs = null;
|
|
173
162
|
}
|
|
174
163
|
}
|
|
175
164
|
/**
|
|
@@ -178,94 +167,29 @@ var ConfigRuntime = class {
|
|
|
178
167
|
async [Symbol.asyncDispose]() {
|
|
179
168
|
await this.close();
|
|
180
169
|
}
|
|
181
|
-
// ---- WebSocket
|
|
182
|
-
|
|
183
|
-
let url = this._baseUrl;
|
|
184
|
-
if (url.startsWith("https://")) {
|
|
185
|
-
url = "wss://" + url.slice("https://".length);
|
|
186
|
-
} else if (url.startsWith("http://")) {
|
|
187
|
-
url = "ws://" + url.slice("http://".length);
|
|
188
|
-
} else {
|
|
189
|
-
url = "wss://" + url;
|
|
190
|
-
}
|
|
191
|
-
url = url.replace(/\/$/, "");
|
|
192
|
-
return `${url}/api/ws/v1/configs?api_key=${this._apiKey}`;
|
|
193
|
-
}
|
|
194
|
-
_connectWebSocket() {
|
|
170
|
+
// ---- Shared WebSocket event handlers ----
|
|
171
|
+
_handleConfigChanged = (data) => {
|
|
195
172
|
if (this._closed) return;
|
|
196
|
-
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
this.
|
|
207
|
-
this.
|
|
208
|
-
|
|
209
|
-
JSON.stringify({
|
|
210
|
-
type: "subscribe",
|
|
211
|
-
config_id: this._configId,
|
|
212
|
-
environment: this._environment
|
|
213
|
-
})
|
|
214
|
-
);
|
|
215
|
-
});
|
|
216
|
-
ws.on("message", (data) => {
|
|
217
|
-
try {
|
|
218
|
-
const msg = JSON.parse(String(data));
|
|
219
|
-
this._handleMessage(msg);
|
|
220
|
-
} catch {
|
|
221
|
-
}
|
|
173
|
+
const configId = data.config_id;
|
|
174
|
+
const changes = data.changes;
|
|
175
|
+
if (configId && changes) {
|
|
176
|
+
this._applyChanges(configId, changes);
|
|
177
|
+
} else if (this._fetchChain) {
|
|
178
|
+
void this._fetchChain().then((newChain) => {
|
|
179
|
+
const oldCache = this._cache;
|
|
180
|
+
this._chain = newChain;
|
|
181
|
+
this._cache = resolveChain(newChain, this._environment);
|
|
182
|
+
this._fetchCount += newChain.length;
|
|
183
|
+
this._lastFetchAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
184
|
+
this._diffAndFire(oldCache, this._cache, "websocket");
|
|
185
|
+
}).catch(() => {
|
|
222
186
|
});
|
|
223
|
-
ws.on("close", () => {
|
|
224
|
-
if (!this._closed) {
|
|
225
|
-
this._wsStatus = "disconnected";
|
|
226
|
-
this._scheduleReconnect();
|
|
227
|
-
}
|
|
228
|
-
});
|
|
229
|
-
ws.on("error", () => {
|
|
230
|
-
});
|
|
231
|
-
} catch {
|
|
232
|
-
if (!this._closed) {
|
|
233
|
-
this._scheduleReconnect();
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
_scheduleReconnect() {
|
|
238
|
-
if (this._closed) return;
|
|
239
|
-
const delay = BACKOFF_MS[Math.min(this._backoffIndex, BACKOFF_MS.length - 1)];
|
|
240
|
-
this._backoffIndex++;
|
|
241
|
-
this._wsStatus = "connecting";
|
|
242
|
-
this._reconnectTimer = setTimeout(() => {
|
|
243
|
-
this._reconnectTimer = null;
|
|
244
|
-
if (this._fetchChain) {
|
|
245
|
-
this._fetchChain().then((newChain) => {
|
|
246
|
-
const oldCache = this._cache;
|
|
247
|
-
this._chain = newChain;
|
|
248
|
-
this._cache = resolveChain(newChain, this._environment);
|
|
249
|
-
this._fetchCount += newChain.length;
|
|
250
|
-
this._lastFetchAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
251
|
-
this._diffAndFire(oldCache, this._cache, "manual");
|
|
252
|
-
}).catch(() => {
|
|
253
|
-
}).finally(() => {
|
|
254
|
-
this._connectWebSocket();
|
|
255
|
-
});
|
|
256
|
-
} else {
|
|
257
|
-
this._connectWebSocket();
|
|
258
|
-
}
|
|
259
|
-
}, delay);
|
|
260
|
-
}
|
|
261
|
-
_handleMessage(msg) {
|
|
262
|
-
if (msg.type === "config_changed") {
|
|
263
|
-
this._applyChanges(msg.config_id, msg.changes);
|
|
264
|
-
} else if (msg.type === "config_deleted") {
|
|
265
|
-
this._closed = true;
|
|
266
|
-
void this.close();
|
|
267
187
|
}
|
|
268
|
-
}
|
|
188
|
+
};
|
|
189
|
+
_handleConfigDeleted = (_data) => {
|
|
190
|
+
this._closed = true;
|
|
191
|
+
void this.close();
|
|
192
|
+
};
|
|
269
193
|
_applyChanges(configId, changes) {
|
|
270
194
|
const chainEntry = this._chain.find((c) => c.id === configId);
|
|
271
195
|
if (!chainEntry) return;
|
|
@@ -314,4 +238,4 @@ var ConfigRuntime = class {
|
|
|
314
238
|
export {
|
|
315
239
|
ConfigRuntime
|
|
316
240
|
};
|
|
317
|
-
//# sourceMappingURL=chunk-
|
|
241
|
+
//# sourceMappingURL=chunk-2VYY5OMH.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/config/resolve.ts","../src/config/runtime.ts"],"sourcesContent":["/**\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","/**\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 { resolveChain } from \"./resolve.js\";\nimport type { ChainConfig } from \"./resolve.js\";\nimport type { ConfigChangeEvent, ConfigStats, ConnectionStatus } from \"./runtime-types.js\";\nimport type { SharedWebSocket } from \"../ws.js\";\n\n/** @internal */\ninterface ChangeListener {\n callback: (event: ConfigChangeEvent) => void;\n key: string | null;\n}\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\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 sharedWs?: SharedWebSocket | 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 _listeners: ChangeListener[] = [];\n\n private readonly _environment: string;\n private readonly _fetchChain: (() => Promise<ChainConfig[]>) | null;\n private _sharedWs: SharedWebSocket | null = null;\n\n /** @internal */\n constructor(options: ConfigRuntimeOptions) {\n this._environment = options.environment;\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 // Register on shared WebSocket for config_changed events\n if (options.sharedWs) {\n this._sharedWs = options.sharedWs;\n this._sharedWs.on(\"config_changed\", this._handleConfigChanged);\n this._sharedWs.on(\"config_deleted\", this._handleConfigDeleted);\n }\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 if (this._sharedWs) {\n return this._sharedWs.connectionStatus as ConnectionStatus;\n }\n return \"disconnected\";\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 * Unregisters from the shared WebSocket. Safe to call multiple times.\n */\n async close(): Promise<void> {\n this._closed = true;\n\n if (this._sharedWs !== null) {\n this._sharedWs.off(\"config_changed\", this._handleConfigChanged);\n this._sharedWs.off(\"config_deleted\", this._handleConfigDeleted);\n this._sharedWs = 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 // ---- Shared WebSocket event handlers ----\n\n private _handleConfigChanged = (data: Record<string, any>): void => {\n if (this._closed) return;\n const configId = data.config_id as string | undefined;\n const changes = data.changes as\n | Array<{ key: string; old_value: unknown; new_value: unknown }>\n | undefined;\n if (configId && changes) {\n this._applyChanges(configId, changes);\n } else if (this._fetchChain) {\n // Full re-fetch if the event doesn't include granular changes\n void 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, \"websocket\");\n })\n .catch(() => {\n // ignore fetch errors\n });\n }\n };\n\n private _handleConfigDeleted = (_data: Record<string, any>): void => {\n this._closed = true;\n void this.close();\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"],"mappings":"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","names":[]}
|