@smplkit/sdk 3.0.78 → 3.0.79
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 +250 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +112 -1
- package/dist/index.d.ts +112 -1
- package/dist/index.js +250 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -17511,7 +17511,20 @@ var LiveConfigProxy = class {
|
|
|
17511
17511
|
this._client = client;
|
|
17512
17512
|
this._key = key;
|
|
17513
17513
|
this._model = model;
|
|
17514
|
-
const ownMethods = /* @__PURE__ */ new Set([
|
|
17514
|
+
const ownMethods = /* @__PURE__ */ new Set([
|
|
17515
|
+
"keys",
|
|
17516
|
+
"values",
|
|
17517
|
+
"items",
|
|
17518
|
+
"get",
|
|
17519
|
+
"onChange",
|
|
17520
|
+
"getBool",
|
|
17521
|
+
"getInt",
|
|
17522
|
+
"getFloat",
|
|
17523
|
+
"getString",
|
|
17524
|
+
"getJson",
|
|
17525
|
+
"_currentValues",
|
|
17526
|
+
"_registerItem"
|
|
17527
|
+
]);
|
|
17515
17528
|
return new Proxy(this, {
|
|
17516
17529
|
get(target, prop, receiver) {
|
|
17517
17530
|
if (typeof prop === "symbol" || prop === "constructor" || prop === "toJSON") {
|
|
@@ -17528,7 +17541,10 @@ var LiveConfigProxy = class {
|
|
|
17528
17541
|
}
|
|
17529
17542
|
return values[prop];
|
|
17530
17543
|
},
|
|
17531
|
-
set(
|
|
17544
|
+
set(target, prop, value, receiver) {
|
|
17545
|
+
if (typeof prop === "string" && prop.startsWith("_")) {
|
|
17546
|
+
return Reflect.set(target, prop, value, receiver);
|
|
17547
|
+
}
|
|
17532
17548
|
throw new Error(
|
|
17533
17549
|
`LiveConfigProxy is read-only; cannot set ${JSON.stringify(String(prop))}. Mutate config values via client.manage.config.*`
|
|
17534
17550
|
);
|
|
@@ -17583,6 +17599,74 @@ var LiveConfigProxy = class {
|
|
|
17583
17599
|
const values = this._currentValues();
|
|
17584
17600
|
return key in values ? values[key] : defaultValue;
|
|
17585
17601
|
}
|
|
17602
|
+
// ------------------------------------------------------------------
|
|
17603
|
+
// Typed getters (ADR-037 §2.13)
|
|
17604
|
+
//
|
|
17605
|
+
// Each registers the item (key, type, default, description) on first
|
|
17606
|
+
// call within the process, then returns the resolved value. When the
|
|
17607
|
+
// resolved value cannot be coerced to the getter's type — including
|
|
17608
|
+
// the "not yet set on the server" case — the in-code default is
|
|
17609
|
+
// returned and a structured warning is logged.
|
|
17610
|
+
// ------------------------------------------------------------------
|
|
17611
|
+
/** @internal */
|
|
17612
|
+
_registerItem(itemKey, itemType, defaultValue, description) {
|
|
17613
|
+
this._client._observeItemDeclaration(this._key, itemKey, itemType, defaultValue, description);
|
|
17614
|
+
}
|
|
17615
|
+
/** Read a BOOLEAN item, registering the declaration on first call. */
|
|
17616
|
+
getBool(key, defaultValue, options = {}) {
|
|
17617
|
+
this._registerItem(key, "BOOLEAN", defaultValue, options.description);
|
|
17618
|
+
const values = this._currentValues();
|
|
17619
|
+
if (!(key in values)) return defaultValue;
|
|
17620
|
+
const value = values[key];
|
|
17621
|
+
if (typeof value === "boolean") return value;
|
|
17622
|
+
console.warn(
|
|
17623
|
+
`[smplkit] config ${JSON.stringify(this._key)} item ${JSON.stringify(key)}: expected BOOLEAN, got ${typeof value}; returning default`
|
|
17624
|
+
);
|
|
17625
|
+
return defaultValue;
|
|
17626
|
+
}
|
|
17627
|
+
/** Read a NUMBER item as int, registering the declaration on first call. */
|
|
17628
|
+
getInt(key, defaultValue, options = {}) {
|
|
17629
|
+
this._registerItem(key, "NUMBER", defaultValue, options.description);
|
|
17630
|
+
const values = this._currentValues();
|
|
17631
|
+
if (!(key in values)) return defaultValue;
|
|
17632
|
+
const value = values[key];
|
|
17633
|
+
if (typeof value === "number" && Number.isInteger(value)) return value;
|
|
17634
|
+
console.warn(
|
|
17635
|
+
`[smplkit] config ${JSON.stringify(this._key)} item ${JSON.stringify(key)}: expected NUMBER (int), got ${typeof value}; returning default`
|
|
17636
|
+
);
|
|
17637
|
+
return defaultValue;
|
|
17638
|
+
}
|
|
17639
|
+
/** Read a NUMBER item as float, registering the declaration on first call. */
|
|
17640
|
+
getFloat(key, defaultValue, options = {}) {
|
|
17641
|
+
this._registerItem(key, "NUMBER", defaultValue, options.description);
|
|
17642
|
+
const values = this._currentValues();
|
|
17643
|
+
if (!(key in values)) return defaultValue;
|
|
17644
|
+
const value = values[key];
|
|
17645
|
+
if (typeof value === "number") return value;
|
|
17646
|
+
console.warn(
|
|
17647
|
+
`[smplkit] config ${JSON.stringify(this._key)} item ${JSON.stringify(key)}: expected NUMBER (float), got ${typeof value}; returning default`
|
|
17648
|
+
);
|
|
17649
|
+
return defaultValue;
|
|
17650
|
+
}
|
|
17651
|
+
/** Read a STRING item, registering the declaration on first call. */
|
|
17652
|
+
getString(key, defaultValue, options = {}) {
|
|
17653
|
+
this._registerItem(key, "STRING", defaultValue, options.description);
|
|
17654
|
+
const values = this._currentValues();
|
|
17655
|
+
if (!(key in values)) return defaultValue;
|
|
17656
|
+
const value = values[key];
|
|
17657
|
+
if (typeof value === "string") return value;
|
|
17658
|
+
console.warn(
|
|
17659
|
+
`[smplkit] config ${JSON.stringify(this._key)} item ${JSON.stringify(key)}: expected STRING, got ${typeof value}; returning default`
|
|
17660
|
+
);
|
|
17661
|
+
return defaultValue;
|
|
17662
|
+
}
|
|
17663
|
+
/** Read a JSON item, registering the declaration on first call. */
|
|
17664
|
+
getJson(key, defaultValue, options = {}) {
|
|
17665
|
+
this._registerItem(key, "JSON", defaultValue, options.description);
|
|
17666
|
+
const values = this._currentValues();
|
|
17667
|
+
if (!(key in values)) return defaultValue;
|
|
17668
|
+
return values[key];
|
|
17669
|
+
}
|
|
17586
17670
|
onChange(callbackOrItemKey, callback) {
|
|
17587
17671
|
if (typeof callbackOrItemKey === "function") {
|
|
17588
17672
|
this._client.onChange(this._key, callbackOrItemKey);
|
|
@@ -17693,6 +17777,11 @@ var ConfigClient = class {
|
|
|
17693
17777
|
* cache for everyone (including descendants that inherit from it)
|
|
17694
17778
|
* without a full re-list. Mirrors Python's `_raw_config_cache`. */
|
|
17695
17779
|
_configStore = {};
|
|
17780
|
+
/** Cache of LiveConfigProxy instances by config id — ensures repeat
|
|
17781
|
+
* `get_or_create(id)` (or `get(id)` after discovery) returns the same
|
|
17782
|
+
* handle so callers can reference it as a parent via direct ref.
|
|
17783
|
+
* Mirrors Python's `_proxies`. */
|
|
17784
|
+
_proxies = {};
|
|
17696
17785
|
_initialized = false;
|
|
17697
17786
|
_listeners = [];
|
|
17698
17787
|
/** @internal */
|
|
@@ -17752,7 +17841,65 @@ var ConfigClient = class {
|
|
|
17752
17841
|
if (metrics) {
|
|
17753
17842
|
metrics.record("config.resolutions", 1, "resolutions", { config: id });
|
|
17754
17843
|
}
|
|
17755
|
-
return
|
|
17844
|
+
return this._cachedProxy(id, model);
|
|
17845
|
+
}
|
|
17846
|
+
/**
|
|
17847
|
+
* Declare a configuration from code; return a live, dict-like view.
|
|
17848
|
+
*
|
|
17849
|
+
* Idempotent. Repeated calls with the same `id` return the same
|
|
17850
|
+
* {@link LiveConfigProxy} instance. The first call queues a discovery
|
|
17851
|
+
* payload (the config and any items declared via typed getters on the
|
|
17852
|
+
* returned handle) for upload to `POST /api/v1/configs/bulk` on next
|
|
17853
|
+
* flush. If the config already exists server-side, `managed=true`
|
|
17854
|
+
* configs are left untouched; `managed=false` configs receive the
|
|
17855
|
+
* SDK's items via source-row upsert per ADR-024 §2.9.
|
|
17856
|
+
*
|
|
17857
|
+
* Unlike {@link get}, this method does NOT raise `NotFoundError` when
|
|
17858
|
+
* the id is absent from the cache — discovery handles that case.
|
|
17859
|
+
*
|
|
17860
|
+
* Mirrors Python's `client.config.get_or_create(id, ...)`.
|
|
17861
|
+
*/
|
|
17862
|
+
async getOrCreate(id, options = {}) {
|
|
17863
|
+
const parent = options.parent;
|
|
17864
|
+
const parentId = parent instanceof LiveConfigProxy ? parent._key : parent ?? null;
|
|
17865
|
+
this._observeConfigDeclaration(id, parentId, options.name ?? null, options.description ?? null);
|
|
17866
|
+
await this._ensureInitialized();
|
|
17867
|
+
return this._cachedProxy(id, options.model);
|
|
17868
|
+
}
|
|
17869
|
+
/** @internal — return (and cache) the canonical proxy for a config id. */
|
|
17870
|
+
_cachedProxy(id, model) {
|
|
17871
|
+
let proxy = this._proxies[id];
|
|
17872
|
+
if (!proxy) {
|
|
17873
|
+
proxy = new LiveConfigProxy(this, id, model);
|
|
17874
|
+
this._proxies[id] = proxy;
|
|
17875
|
+
} else if (model !== void 0 && proxy._model === void 0) {
|
|
17876
|
+
proxy._model = model;
|
|
17877
|
+
}
|
|
17878
|
+
return proxy;
|
|
17879
|
+
}
|
|
17880
|
+
/** @internal — queue a config declaration with the management buffer. */
|
|
17881
|
+
_observeConfigDeclaration(configId, parent, name, description) {
|
|
17882
|
+
const manage = this._resolveManagement?.();
|
|
17883
|
+
if (!manage) return;
|
|
17884
|
+
manage.config.registerConfig(configId, {
|
|
17885
|
+
service: this._parent?._service ?? null,
|
|
17886
|
+
environment: this._parent?._environment ?? "",
|
|
17887
|
+
parent,
|
|
17888
|
+
name,
|
|
17889
|
+
description
|
|
17890
|
+
});
|
|
17891
|
+
}
|
|
17892
|
+
/** @internal — queue a config item declaration with the management buffer. */
|
|
17893
|
+
_observeItemDeclaration(configId, itemKey, itemType, defaultValue, description) {
|
|
17894
|
+
const manage = this._resolveManagement?.();
|
|
17895
|
+
if (!manage) return;
|
|
17896
|
+
manage.config.registerConfigItem(
|
|
17897
|
+
configId,
|
|
17898
|
+
itemKey,
|
|
17899
|
+
itemType,
|
|
17900
|
+
defaultValue,
|
|
17901
|
+
description ?? null
|
|
17902
|
+
);
|
|
17756
17903
|
}
|
|
17757
17904
|
// ------------------------------------------------------------------
|
|
17758
17905
|
// Runtime: change listeners (3-level overloads)
|
|
@@ -17875,6 +18022,17 @@ var ConfigClient = class {
|
|
|
17875
18022
|
if (!environment) {
|
|
17876
18023
|
throw new SmplError("No environment set. Ensure SmplClient is configured.");
|
|
17877
18024
|
}
|
|
18025
|
+
const manage = this._resolveManagement?.();
|
|
18026
|
+
if (manage) {
|
|
18027
|
+
try {
|
|
18028
|
+
await manage.config.flush();
|
|
18029
|
+
} catch (err) {
|
|
18030
|
+
debug(
|
|
18031
|
+
"config",
|
|
18032
|
+
`pre-start discovery flush failed: ${err instanceof Error ? err.message : String(err)}`
|
|
18033
|
+
);
|
|
18034
|
+
}
|
|
18035
|
+
}
|
|
17878
18036
|
const configs = await this._listConfigs();
|
|
17879
18037
|
const cache = {};
|
|
17880
18038
|
const store = {};
|
|
@@ -18828,11 +18986,99 @@ function resourceToConfig2(resource, client) {
|
|
|
18828
18986
|
updatedAt: attrs.updated_at ?? null
|
|
18829
18987
|
});
|
|
18830
18988
|
}
|
|
18989
|
+
var CONFIG_REGISTRATION_FLUSH_SIZE = 50;
|
|
18990
|
+
var ConfigRegistrationBuffer = class {
|
|
18991
|
+
_pending = /* @__PURE__ */ new Map();
|
|
18992
|
+
_meta = /* @__PURE__ */ new Map();
|
|
18993
|
+
_sentItems = /* @__PURE__ */ new Set();
|
|
18994
|
+
declare(configId, meta) {
|
|
18995
|
+
if (this._meta.has(configId)) return;
|
|
18996
|
+
this._meta.set(configId, meta);
|
|
18997
|
+
this._pending.set(configId, this._buildEntry(configId, {}));
|
|
18998
|
+
}
|
|
18999
|
+
addItem(configId, itemKey, itemType, defaultValue, description) {
|
|
19000
|
+
if (!this._meta.has(configId)) return;
|
|
19001
|
+
const sentKey = `${configId}::${itemKey}`;
|
|
19002
|
+
if (this._sentItems.has(sentKey)) return;
|
|
19003
|
+
let entry = this._pending.get(configId);
|
|
19004
|
+
if (!entry) {
|
|
19005
|
+
entry = this._buildEntry(configId, {});
|
|
19006
|
+
this._pending.set(configId, entry);
|
|
19007
|
+
}
|
|
19008
|
+
if (itemKey in entry.items) return;
|
|
19009
|
+
const def = { value: defaultValue, type: itemType };
|
|
19010
|
+
if (description !== null) def.description = description;
|
|
19011
|
+
entry.items[itemKey] = def;
|
|
19012
|
+
}
|
|
19013
|
+
_buildEntry(configId, items) {
|
|
19014
|
+
const meta = this._meta.get(configId);
|
|
19015
|
+
const entry = { id: configId, items };
|
|
19016
|
+
if (meta.service !== null) entry.service = meta.service;
|
|
19017
|
+
if (meta.environment !== null) entry.environment = meta.environment;
|
|
19018
|
+
if (meta.parent !== null) entry.parent = meta.parent;
|
|
19019
|
+
if (meta.name !== null) entry.name = meta.name;
|
|
19020
|
+
if (meta.description !== null) entry.description = meta.description;
|
|
19021
|
+
return entry;
|
|
19022
|
+
}
|
|
19023
|
+
/** Destructive drain — records sent items so they aren't re-queued. */
|
|
19024
|
+
drain() {
|
|
19025
|
+
const batch = Array.from(this._pending.values());
|
|
19026
|
+
for (const entry of batch) {
|
|
19027
|
+
for (const itemKey of Object.keys(entry.items)) {
|
|
19028
|
+
this._sentItems.add(`${entry.id}::${itemKey}`);
|
|
19029
|
+
}
|
|
19030
|
+
}
|
|
19031
|
+
this._pending.clear();
|
|
19032
|
+
return batch;
|
|
19033
|
+
}
|
|
19034
|
+
get pendingCount() {
|
|
19035
|
+
return this._pending.size;
|
|
19036
|
+
}
|
|
19037
|
+
};
|
|
18831
19038
|
var ManagementConfigClient = class {
|
|
18832
19039
|
/** @internal */
|
|
18833
19040
|
constructor(_http) {
|
|
18834
19041
|
this._http = _http;
|
|
18835
19042
|
}
|
|
19043
|
+
/** @internal */
|
|
19044
|
+
_buffer = new ConfigRegistrationBuffer();
|
|
19045
|
+
/** @internal — queue a configuration declaration for bulk-discovery upload. */
|
|
19046
|
+
registerConfig(configId, meta) {
|
|
19047
|
+
this._buffer.declare(configId, {
|
|
19048
|
+
service: meta.service,
|
|
19049
|
+
environment: meta.environment,
|
|
19050
|
+
parent: meta.parent ?? null,
|
|
19051
|
+
name: meta.name ?? null,
|
|
19052
|
+
description: meta.description ?? null
|
|
19053
|
+
});
|
|
19054
|
+
if (this._buffer.pendingCount >= CONFIG_REGISTRATION_FLUSH_SIZE) {
|
|
19055
|
+
void this.flush();
|
|
19056
|
+
}
|
|
19057
|
+
}
|
|
19058
|
+
/** @internal — queue a config item declaration. */
|
|
19059
|
+
registerConfigItem(configId, itemKey, itemType, defaultValue, description) {
|
|
19060
|
+
this._buffer.addItem(configId, itemKey, itemType, defaultValue, description);
|
|
19061
|
+
if (this._buffer.pendingCount >= CONFIG_REGISTRATION_FLUSH_SIZE) {
|
|
19062
|
+
void this.flush();
|
|
19063
|
+
}
|
|
19064
|
+
}
|
|
19065
|
+
/** Send any pending config declarations to `POST /api/v1/configs/bulk`. */
|
|
19066
|
+
async flush() {
|
|
19067
|
+
const batch = this._buffer.drain();
|
|
19068
|
+
if (batch.length === 0) return;
|
|
19069
|
+
try {
|
|
19070
|
+
const result = await this._http.POST("/api/v1/configs/bulk", {
|
|
19071
|
+
body: { configs: batch }
|
|
19072
|
+
});
|
|
19073
|
+
if (!result.response.ok) {
|
|
19074
|
+
}
|
|
19075
|
+
} catch {
|
|
19076
|
+
}
|
|
19077
|
+
}
|
|
19078
|
+
/** Number of pending config declarations awaiting flush. */
|
|
19079
|
+
get pendingCount() {
|
|
19080
|
+
return this._buffer.pendingCount;
|
|
19081
|
+
}
|
|
18836
19082
|
/** Construct an unsaved {@link Config}. Call `.save()` to persist. */
|
|
18837
19083
|
new(id, options = {}) {
|
|
18838
19084
|
const parent = options.parent;
|
|
@@ -21094,6 +21340,7 @@ var SmplManagementClient = class {
|
|
|
21094
21340
|
await this.contexts.flush();
|
|
21095
21341
|
await this.flags.flush();
|
|
21096
21342
|
await this.loggers.flush();
|
|
21343
|
+
await this.config.flush();
|
|
21097
21344
|
} catch {
|
|
21098
21345
|
}
|
|
21099
21346
|
}
|