@smplkit/sdk 1.2.2 → 1.2.3
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/index.cjs +97 -211
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +59 -175
- package/dist/index.d.ts +59 -175
- package/dist/index.js +97 -210
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -33,7 +33,6 @@ __export(index_exports, {
|
|
|
33
33
|
BoolFlagHandle: () => BoolFlagHandle,
|
|
34
34
|
Config: () => Config,
|
|
35
35
|
ConfigClient: () => ConfigClient,
|
|
36
|
-
ConfigRuntime: () => ConfigRuntime,
|
|
37
36
|
Context: () => Context,
|
|
38
37
|
ContextType: () => ContextType,
|
|
39
38
|
Flag: () => Flag,
|
|
@@ -420,6 +419,7 @@ var ConfigClient = class {
|
|
|
420
419
|
_parent = null;
|
|
421
420
|
_configCache = {};
|
|
422
421
|
_connected = false;
|
|
422
|
+
_listeners = [];
|
|
423
423
|
/** @internal */
|
|
424
424
|
constructor(apiKey, timeout) {
|
|
425
425
|
this._apiKey = apiKey;
|
|
@@ -555,6 +555,102 @@ var ConfigClient = class {
|
|
|
555
555
|
}
|
|
556
556
|
return itemKey in resolved ? resolved[itemKey] : defaultValue ?? null;
|
|
557
557
|
}
|
|
558
|
+
/**
|
|
559
|
+
* Return a config value as a string, or `defaultValue` if absent or not a string.
|
|
560
|
+
*
|
|
561
|
+
* @throws {SmplNotConnectedError} If connect() has not been called.
|
|
562
|
+
*/
|
|
563
|
+
getString(configKey, itemKey, defaultValue = null) {
|
|
564
|
+
const value = this.getValue(configKey, itemKey);
|
|
565
|
+
return typeof value === "string" ? value : defaultValue;
|
|
566
|
+
}
|
|
567
|
+
/**
|
|
568
|
+
* Return a config value as a number, or `defaultValue` if absent or not a number.
|
|
569
|
+
*
|
|
570
|
+
* @throws {SmplNotConnectedError} If connect() has not been called.
|
|
571
|
+
*/
|
|
572
|
+
getInt(configKey, itemKey, defaultValue = null) {
|
|
573
|
+
const value = this.getValue(configKey, itemKey);
|
|
574
|
+
return typeof value === "number" ? value : defaultValue;
|
|
575
|
+
}
|
|
576
|
+
/**
|
|
577
|
+
* Return a config value as a boolean, or `defaultValue` if absent or not a boolean.
|
|
578
|
+
*
|
|
579
|
+
* @throws {SmplNotConnectedError} If connect() has not been called.
|
|
580
|
+
*/
|
|
581
|
+
getBool(configKey, itemKey, defaultValue = null) {
|
|
582
|
+
const value = this.getValue(configKey, itemKey);
|
|
583
|
+
return typeof value === "boolean" ? value : defaultValue;
|
|
584
|
+
}
|
|
585
|
+
/**
|
|
586
|
+
* Re-fetch all configs, re-resolve values, and update the cache.
|
|
587
|
+
*
|
|
588
|
+
* Fires change listeners for any values that differ from the previous cache.
|
|
589
|
+
*
|
|
590
|
+
* @throws {SmplNotConnectedError} If connect() has not been called.
|
|
591
|
+
*/
|
|
592
|
+
async refresh() {
|
|
593
|
+
if (!this._connected) {
|
|
594
|
+
throw new SmplNotConnectedError("SmplClient is not connected. Call client.connect() first.");
|
|
595
|
+
}
|
|
596
|
+
const environment = this._parent?._environment;
|
|
597
|
+
if (!environment) {
|
|
598
|
+
throw new SmplError("No environment set.");
|
|
599
|
+
}
|
|
600
|
+
const configs = await this.list();
|
|
601
|
+
const newCache = {};
|
|
602
|
+
for (const cfg of configs) {
|
|
603
|
+
const chain = await cfg._buildChain(this._http);
|
|
604
|
+
newCache[cfg.key] = resolveChain(chain, environment);
|
|
605
|
+
}
|
|
606
|
+
const oldCache = this._configCache;
|
|
607
|
+
this._configCache = newCache;
|
|
608
|
+
this._diffAndFire(oldCache, newCache, "manual");
|
|
609
|
+
}
|
|
610
|
+
/**
|
|
611
|
+
* Register a listener that fires when a config value changes (on refresh).
|
|
612
|
+
*
|
|
613
|
+
* @param callback - Called with a {@link ConfigChangeEvent} on each change.
|
|
614
|
+
* @param options.configKey - If provided, only fire for changes to this config.
|
|
615
|
+
* @param options.itemKey - If provided, only fire for changes to this item key.
|
|
616
|
+
*/
|
|
617
|
+
onChange(callback, options) {
|
|
618
|
+
this._listeners.push({
|
|
619
|
+
callback,
|
|
620
|
+
configKey: options?.configKey ?? null,
|
|
621
|
+
itemKey: options?.itemKey ?? null
|
|
622
|
+
});
|
|
623
|
+
}
|
|
624
|
+
/** @internal */
|
|
625
|
+
_diffAndFire(oldCache, newCache, source) {
|
|
626
|
+
const allConfigKeys = /* @__PURE__ */ new Set([...Object.keys(oldCache), ...Object.keys(newCache)]);
|
|
627
|
+
for (const cfgKey of allConfigKeys) {
|
|
628
|
+
const oldItems = oldCache[cfgKey] ?? {};
|
|
629
|
+
const newItems = newCache[cfgKey] ?? {};
|
|
630
|
+
const allItemKeys = /* @__PURE__ */ new Set([...Object.keys(oldItems), ...Object.keys(newItems)]);
|
|
631
|
+
for (const iKey of allItemKeys) {
|
|
632
|
+
const oldVal = iKey in oldItems ? oldItems[iKey] : null;
|
|
633
|
+
const newVal = iKey in newItems ? newItems[iKey] : null;
|
|
634
|
+
if (JSON.stringify(oldVal) !== JSON.stringify(newVal)) {
|
|
635
|
+
const event = {
|
|
636
|
+
configKey: cfgKey,
|
|
637
|
+
itemKey: iKey,
|
|
638
|
+
oldValue: oldVal,
|
|
639
|
+
newValue: newVal,
|
|
640
|
+
source
|
|
641
|
+
};
|
|
642
|
+
for (const listener of this._listeners) {
|
|
643
|
+
if (listener.configKey !== null && listener.configKey !== cfgKey) continue;
|
|
644
|
+
if (listener.itemKey !== null && listener.itemKey !== iKey) continue;
|
|
645
|
+
try {
|
|
646
|
+
listener.callback(event);
|
|
647
|
+
} catch {
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
}
|
|
558
654
|
/**
|
|
559
655
|
* Internal: PUT a full config update and return the updated model.
|
|
560
656
|
*
|
|
@@ -1927,215 +2023,6 @@ var SmplClient = class {
|
|
|
1927
2023
|
}
|
|
1928
2024
|
};
|
|
1929
2025
|
|
|
1930
|
-
// src/config/runtime.ts
|
|
1931
|
-
var ConfigRuntime = class {
|
|
1932
|
-
_cache;
|
|
1933
|
-
_chain;
|
|
1934
|
-
_fetchCount;
|
|
1935
|
-
_lastFetchAt;
|
|
1936
|
-
_closed = false;
|
|
1937
|
-
_listeners = [];
|
|
1938
|
-
_environment;
|
|
1939
|
-
_fetchChain;
|
|
1940
|
-
_sharedWs = null;
|
|
1941
|
-
/** @internal */
|
|
1942
|
-
constructor(options) {
|
|
1943
|
-
this._environment = options.environment;
|
|
1944
|
-
this._fetchChain = options.fetchChain;
|
|
1945
|
-
this._chain = options.chain;
|
|
1946
|
-
this._cache = resolveChain(options.chain, options.environment);
|
|
1947
|
-
this._fetchCount = options.chain.length;
|
|
1948
|
-
this._lastFetchAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1949
|
-
if (options.sharedWs) {
|
|
1950
|
-
this._sharedWs = options.sharedWs;
|
|
1951
|
-
this._sharedWs.on("config_changed", this._handleConfigChanged);
|
|
1952
|
-
this._sharedWs.on("config_deleted", this._handleConfigDeleted);
|
|
1953
|
-
}
|
|
1954
|
-
}
|
|
1955
|
-
// ---- Value access (synchronous, local cache) ----
|
|
1956
|
-
/**
|
|
1957
|
-
* Return the resolved value for `key`, or `defaultValue` if absent.
|
|
1958
|
-
*
|
|
1959
|
-
* @param key - The config key to look up.
|
|
1960
|
-
* @param defaultValue - Returned when the key is not present (default: null).
|
|
1961
|
-
*/
|
|
1962
|
-
get(key, defaultValue = null) {
|
|
1963
|
-
return key in this._cache ? this._cache[key] : defaultValue;
|
|
1964
|
-
}
|
|
1965
|
-
/**
|
|
1966
|
-
* Return the value as a string, or `defaultValue` if absent or not a string.
|
|
1967
|
-
*/
|
|
1968
|
-
getString(key, defaultValue = null) {
|
|
1969
|
-
const value = this._cache[key];
|
|
1970
|
-
return typeof value === "string" ? value : defaultValue;
|
|
1971
|
-
}
|
|
1972
|
-
/**
|
|
1973
|
-
* Return the value as a number, or `defaultValue` if absent or not a number.
|
|
1974
|
-
*/
|
|
1975
|
-
getInt(key, defaultValue = null) {
|
|
1976
|
-
const value = this._cache[key];
|
|
1977
|
-
return typeof value === "number" ? value : defaultValue;
|
|
1978
|
-
}
|
|
1979
|
-
/**
|
|
1980
|
-
* Return the value as a boolean, or `defaultValue` if absent or not a boolean.
|
|
1981
|
-
*/
|
|
1982
|
-
getBool(key, defaultValue = null) {
|
|
1983
|
-
const value = this._cache[key];
|
|
1984
|
-
return typeof value === "boolean" ? value : defaultValue;
|
|
1985
|
-
}
|
|
1986
|
-
/**
|
|
1987
|
-
* Return whether `key` is present in the resolved configuration.
|
|
1988
|
-
*/
|
|
1989
|
-
exists(key) {
|
|
1990
|
-
return key in this._cache;
|
|
1991
|
-
}
|
|
1992
|
-
/**
|
|
1993
|
-
* Return a shallow copy of the full resolved configuration.
|
|
1994
|
-
*/
|
|
1995
|
-
getAll() {
|
|
1996
|
-
return { ...this._cache };
|
|
1997
|
-
}
|
|
1998
|
-
// ---- Change listeners ----
|
|
1999
|
-
/**
|
|
2000
|
-
* Register a listener that fires when a config value changes.
|
|
2001
|
-
*
|
|
2002
|
-
* @param callback - Called with a {@link ConfigChangeEvent} on each change.
|
|
2003
|
-
* @param options.key - If provided, the listener fires only for this key.
|
|
2004
|
-
* If omitted, the listener fires for all changes.
|
|
2005
|
-
*/
|
|
2006
|
-
onChange(callback, options) {
|
|
2007
|
-
this._listeners.push({
|
|
2008
|
-
callback,
|
|
2009
|
-
key: options?.key ?? null
|
|
2010
|
-
});
|
|
2011
|
-
}
|
|
2012
|
-
// ---- Diagnostics ----
|
|
2013
|
-
/**
|
|
2014
|
-
* Return diagnostic statistics for this runtime.
|
|
2015
|
-
*/
|
|
2016
|
-
stats() {
|
|
2017
|
-
return {
|
|
2018
|
-
fetchCount: this._fetchCount,
|
|
2019
|
-
lastFetchAt: this._lastFetchAt
|
|
2020
|
-
};
|
|
2021
|
-
}
|
|
2022
|
-
/**
|
|
2023
|
-
* Return the current WebSocket connection status.
|
|
2024
|
-
*/
|
|
2025
|
-
connectionStatus() {
|
|
2026
|
-
if (this._sharedWs) {
|
|
2027
|
-
return this._sharedWs.connectionStatus;
|
|
2028
|
-
}
|
|
2029
|
-
return "disconnected";
|
|
2030
|
-
}
|
|
2031
|
-
// ---- Lifecycle ----
|
|
2032
|
-
/**
|
|
2033
|
-
* Force a manual refresh of the cached configuration.
|
|
2034
|
-
*
|
|
2035
|
-
* Re-fetches the full config chain via HTTP, re-resolves values, updates
|
|
2036
|
-
* the local cache, and fires listeners for any detected changes.
|
|
2037
|
-
*
|
|
2038
|
-
* @throws {Error} If no `fetchChain` function was provided on construction.
|
|
2039
|
-
*/
|
|
2040
|
-
async refresh() {
|
|
2041
|
-
if (!this._fetchChain) {
|
|
2042
|
-
throw new Error("No fetchChain function provided; cannot refresh.");
|
|
2043
|
-
}
|
|
2044
|
-
const newChain = await this._fetchChain();
|
|
2045
|
-
const oldCache = this._cache;
|
|
2046
|
-
this._chain = newChain;
|
|
2047
|
-
this._cache = resolveChain(newChain, this._environment);
|
|
2048
|
-
this._fetchCount += newChain.length;
|
|
2049
|
-
this._lastFetchAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2050
|
-
this._diffAndFire(oldCache, this._cache, "manual");
|
|
2051
|
-
}
|
|
2052
|
-
/**
|
|
2053
|
-
* Close the runtime connection.
|
|
2054
|
-
*
|
|
2055
|
-
* Unregisters from the shared WebSocket. Safe to call multiple times.
|
|
2056
|
-
*/
|
|
2057
|
-
async close() {
|
|
2058
|
-
this._closed = true;
|
|
2059
|
-
if (this._sharedWs !== null) {
|
|
2060
|
-
this._sharedWs.off("config_changed", this._handleConfigChanged);
|
|
2061
|
-
this._sharedWs.off("config_deleted", this._handleConfigDeleted);
|
|
2062
|
-
this._sharedWs = null;
|
|
2063
|
-
}
|
|
2064
|
-
}
|
|
2065
|
-
/**
|
|
2066
|
-
* Async dispose support for `await using` (TypeScript 5.2+).
|
|
2067
|
-
*/
|
|
2068
|
-
async [Symbol.asyncDispose]() {
|
|
2069
|
-
await this.close();
|
|
2070
|
-
}
|
|
2071
|
-
// ---- Shared WebSocket event handlers ----
|
|
2072
|
-
_handleConfigChanged = (data) => {
|
|
2073
|
-
if (this._closed) return;
|
|
2074
|
-
const configId = data.config_id;
|
|
2075
|
-
const changes = data.changes;
|
|
2076
|
-
if (configId && changes) {
|
|
2077
|
-
this._applyChanges(configId, changes);
|
|
2078
|
-
} else if (this._fetchChain) {
|
|
2079
|
-
void this._fetchChain().then((newChain) => {
|
|
2080
|
-
const oldCache = this._cache;
|
|
2081
|
-
this._chain = newChain;
|
|
2082
|
-
this._cache = resolveChain(newChain, this._environment);
|
|
2083
|
-
this._fetchCount += newChain.length;
|
|
2084
|
-
this._lastFetchAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2085
|
-
this._diffAndFire(oldCache, this._cache, "websocket");
|
|
2086
|
-
}).catch(() => {
|
|
2087
|
-
});
|
|
2088
|
-
}
|
|
2089
|
-
};
|
|
2090
|
-
_handleConfigDeleted = (_data) => {
|
|
2091
|
-
this._closed = true;
|
|
2092
|
-
void this.close();
|
|
2093
|
-
};
|
|
2094
|
-
_applyChanges(configId, changes) {
|
|
2095
|
-
const chainEntry = this._chain.find((c) => c.id === configId);
|
|
2096
|
-
if (!chainEntry) return;
|
|
2097
|
-
for (const change of changes) {
|
|
2098
|
-
const { key, new_value } = change;
|
|
2099
|
-
const envEntry = chainEntry.environments[this._environment] !== void 0 && chainEntry.environments[this._environment] !== null ? chainEntry.environments[this._environment] : null;
|
|
2100
|
-
const envValues = envEntry !== null && typeof envEntry === "object" ? envEntry.values ?? {} : null;
|
|
2101
|
-
if (new_value === null || new_value === void 0) {
|
|
2102
|
-
delete chainEntry.items[key];
|
|
2103
|
-
if (envValues) delete envValues[key];
|
|
2104
|
-
} else if (envValues && key in envValues) {
|
|
2105
|
-
envValues[key] = new_value;
|
|
2106
|
-
} else if (key in chainEntry.items) {
|
|
2107
|
-
chainEntry.items[key] = new_value;
|
|
2108
|
-
} else {
|
|
2109
|
-
chainEntry.items[key] = new_value;
|
|
2110
|
-
}
|
|
2111
|
-
}
|
|
2112
|
-
const oldCache = this._cache;
|
|
2113
|
-
this._cache = resolveChain(this._chain, this._environment);
|
|
2114
|
-
this._diffAndFire(oldCache, this._cache, "websocket");
|
|
2115
|
-
}
|
|
2116
|
-
_diffAndFire(oldCache, newCache, source) {
|
|
2117
|
-
const allKeys = /* @__PURE__ */ new Set([...Object.keys(oldCache), ...Object.keys(newCache)]);
|
|
2118
|
-
for (const key of allKeys) {
|
|
2119
|
-
const oldVal = key in oldCache ? oldCache[key] : null;
|
|
2120
|
-
const newVal = key in newCache ? newCache[key] : null;
|
|
2121
|
-
if (JSON.stringify(oldVal) !== JSON.stringify(newVal)) {
|
|
2122
|
-
const event = { key, oldValue: oldVal, newValue: newVal, source };
|
|
2123
|
-
this._fireListeners(event);
|
|
2124
|
-
}
|
|
2125
|
-
}
|
|
2126
|
-
}
|
|
2127
|
-
_fireListeners(event) {
|
|
2128
|
-
for (const listener of this._listeners) {
|
|
2129
|
-
if (listener.key === null || listener.key === event.key) {
|
|
2130
|
-
try {
|
|
2131
|
-
listener.callback(event);
|
|
2132
|
-
} catch {
|
|
2133
|
-
}
|
|
2134
|
-
}
|
|
2135
|
-
}
|
|
2136
|
-
}
|
|
2137
|
-
};
|
|
2138
|
-
|
|
2139
2026
|
// src/flags/types.ts
|
|
2140
2027
|
var Context = class {
|
|
2141
2028
|
type;
|
|
@@ -2205,7 +2092,6 @@ var Rule = class {
|
|
|
2205
2092
|
BoolFlagHandle,
|
|
2206
2093
|
Config,
|
|
2207
2094
|
ConfigClient,
|
|
2208
|
-
ConfigRuntime,
|
|
2209
2095
|
Context,
|
|
2210
2096
|
ContextType,
|
|
2211
2097
|
Flag,
|