@smplkit/sdk 3.0.77 → 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.d.cts
CHANGED
|
@@ -632,6 +632,28 @@ declare class LiveConfigProxy<T = Record<string, unknown>> {
|
|
|
632
632
|
items(): Array<[string, unknown]>;
|
|
633
633
|
/** Dict method: get a value by key, returning `defaultValue` if absent. */
|
|
634
634
|
get<V = unknown>(key: string, defaultValue?: V): V | unknown;
|
|
635
|
+
/** @internal */
|
|
636
|
+
private _registerItem;
|
|
637
|
+
/** Read a BOOLEAN item, registering the declaration on first call. */
|
|
638
|
+
getBool(key: string, defaultValue: boolean, options?: {
|
|
639
|
+
description?: string;
|
|
640
|
+
}): boolean;
|
|
641
|
+
/** Read a NUMBER item as int, registering the declaration on first call. */
|
|
642
|
+
getInt(key: string, defaultValue: number, options?: {
|
|
643
|
+
description?: string;
|
|
644
|
+
}): number;
|
|
645
|
+
/** Read a NUMBER item as float, registering the declaration on first call. */
|
|
646
|
+
getFloat(key: string, defaultValue: number, options?: {
|
|
647
|
+
description?: string;
|
|
648
|
+
}): number;
|
|
649
|
+
/** Read a STRING item, registering the declaration on first call. */
|
|
650
|
+
getString(key: string, defaultValue: string, options?: {
|
|
651
|
+
description?: string;
|
|
652
|
+
}): string;
|
|
653
|
+
/** Read a JSON item, registering the declaration on first call. */
|
|
654
|
+
getJson<V = unknown>(key: string, defaultValue: V, options?: {
|
|
655
|
+
description?: string;
|
|
656
|
+
}): V;
|
|
635
657
|
/**
|
|
636
658
|
* Register a change listener scoped to this config.
|
|
637
659
|
*
|
|
@@ -2365,13 +2387,69 @@ declare class Config {
|
|
|
2365
2387
|
}
|
|
2366
2388
|
|
|
2367
2389
|
type ConfigHttp$1 = ReturnType<typeof createClient<___generated_config_d_ts.paths>>;
|
|
2390
|
+
interface ConfigBufferEntry {
|
|
2391
|
+
id: string;
|
|
2392
|
+
items: Record<string, {
|
|
2393
|
+
value: unknown;
|
|
2394
|
+
type: string;
|
|
2395
|
+
description?: string;
|
|
2396
|
+
}>;
|
|
2397
|
+
service?: string;
|
|
2398
|
+
environment?: string;
|
|
2399
|
+
parent?: string;
|
|
2400
|
+
name?: string;
|
|
2401
|
+
description?: string;
|
|
2402
|
+
}
|
|
2403
|
+
interface ConfigBufferMeta {
|
|
2404
|
+
service: string | null;
|
|
2405
|
+
environment: string | null;
|
|
2406
|
+
parent: string | null;
|
|
2407
|
+
name: string | null;
|
|
2408
|
+
description: string | null;
|
|
2409
|
+
}
|
|
2410
|
+
/**
|
|
2411
|
+
* Buffer pending config declarations for bulk registration. @internal
|
|
2412
|
+
*
|
|
2413
|
+
* Configs differ from flags because each entry carries a nested `items`
|
|
2414
|
+
* dict that grows incrementally as typed getters fire. We store per-config
|
|
2415
|
+
* metadata permanently so post-flush deltas re-attribute correctly, and
|
|
2416
|
+
* dedupe items per `(configId, itemKey)` so an already-sent item never
|
|
2417
|
+
* re-sends. Mirrors Python's `_ConfigRegistrationBuffer`.
|
|
2418
|
+
*/
|
|
2419
|
+
declare class ConfigRegistrationBuffer {
|
|
2420
|
+
private _pending;
|
|
2421
|
+
private _meta;
|
|
2422
|
+
private _sentItems;
|
|
2423
|
+
declare(configId: string, meta: ConfigBufferMeta): void;
|
|
2424
|
+
addItem(configId: string, itemKey: string, itemType: string, defaultValue: unknown, description: string | null): void;
|
|
2425
|
+
private _buildEntry;
|
|
2426
|
+
/** Destructive drain — records sent items so they aren't re-queued. */
|
|
2427
|
+
drain(): ConfigBufferEntry[];
|
|
2428
|
+
get pendingCount(): number;
|
|
2429
|
+
}
|
|
2368
2430
|
/**
|
|
2369
|
-
* `mgmt.config.*` — CRUD client for configs.
|
|
2431
|
+
* `mgmt.config.*` — CRUD client for configs + bulk registration buffer.
|
|
2370
2432
|
*/
|
|
2371
2433
|
declare class ManagementConfigClient {
|
|
2372
2434
|
private readonly _http;
|
|
2373
2435
|
/** @internal */
|
|
2436
|
+
readonly _buffer: ConfigRegistrationBuffer;
|
|
2437
|
+
/** @internal */
|
|
2374
2438
|
constructor(_http: ConfigHttp$1);
|
|
2439
|
+
/** @internal — queue a configuration declaration for bulk-discovery upload. */
|
|
2440
|
+
registerConfig(configId: string, meta: {
|
|
2441
|
+
service: string | null;
|
|
2442
|
+
environment: string | null;
|
|
2443
|
+
parent?: string | null;
|
|
2444
|
+
name?: string | null;
|
|
2445
|
+
description?: string | null;
|
|
2446
|
+
}): void;
|
|
2447
|
+
/** @internal — queue a config item declaration. */
|
|
2448
|
+
registerConfigItem(configId: string, itemKey: string, itemType: string, defaultValue: unknown, description: string | null): void;
|
|
2449
|
+
/** Send any pending config declarations to `POST /api/v1/configs/bulk`. */
|
|
2450
|
+
flush(): Promise<void>;
|
|
2451
|
+
/** Number of pending config declarations awaiting flush. */
|
|
2452
|
+
get pendingCount(): number;
|
|
2375
2453
|
/** Construct an unsaved {@link Config}. Call `.save()` to persist. */
|
|
2376
2454
|
new(id: string, options?: {
|
|
2377
2455
|
name?: string;
|
|
@@ -3544,6 +3622,11 @@ declare class ConfigClient {
|
|
|
3544
3622
|
* cache for everyone (including descendants that inherit from it)
|
|
3545
3623
|
* without a full re-list. Mirrors Python's `_raw_config_cache`. */
|
|
3546
3624
|
private _configStore;
|
|
3625
|
+
/** Cache of LiveConfigProxy instances by config id — ensures repeat
|
|
3626
|
+
* `get_or_create(id)` (or `get(id)` after discovery) returns the same
|
|
3627
|
+
* handle so callers can reference it as a parent via direct ref.
|
|
3628
|
+
* Mirrors Python's `_proxies`. */
|
|
3629
|
+
private _proxies;
|
|
3547
3630
|
private _initialized;
|
|
3548
3631
|
private _listeners;
|
|
3549
3632
|
/** @internal */
|
|
@@ -3565,6 +3648,34 @@ declare class ConfigClient {
|
|
|
3565
3648
|
* There is no `subscribe()` — it was unified into `get()`.
|
|
3566
3649
|
*/
|
|
3567
3650
|
get<T = Record<string, unknown>>(id: string, model?: new (data: any) => T): Promise<LiveConfigProxy<T>>;
|
|
3651
|
+
/**
|
|
3652
|
+
* Declare a configuration from code; return a live, dict-like view.
|
|
3653
|
+
*
|
|
3654
|
+
* Idempotent. Repeated calls with the same `id` return the same
|
|
3655
|
+
* {@link LiveConfigProxy} instance. The first call queues a discovery
|
|
3656
|
+
* payload (the config and any items declared via typed getters on the
|
|
3657
|
+
* returned handle) for upload to `POST /api/v1/configs/bulk` on next
|
|
3658
|
+
* flush. If the config already exists server-side, `managed=true`
|
|
3659
|
+
* configs are left untouched; `managed=false` configs receive the
|
|
3660
|
+
* SDK's items via source-row upsert per ADR-024 §2.9.
|
|
3661
|
+
*
|
|
3662
|
+
* Unlike {@link get}, this method does NOT raise `NotFoundError` when
|
|
3663
|
+
* the id is absent from the cache — discovery handles that case.
|
|
3664
|
+
*
|
|
3665
|
+
* Mirrors Python's `client.config.get_or_create(id, ...)`.
|
|
3666
|
+
*/
|
|
3667
|
+
getOrCreate<T = Record<string, unknown>>(id: string, options?: {
|
|
3668
|
+
parent?: string | LiveConfigProxy<any> | null;
|
|
3669
|
+
name?: string;
|
|
3670
|
+
description?: string;
|
|
3671
|
+
model?: new (data: any) => T;
|
|
3672
|
+
}): Promise<LiveConfigProxy<T>>;
|
|
3673
|
+
/** @internal — return (and cache) the canonical proxy for a config id. */
|
|
3674
|
+
_cachedProxy<T>(id: string, model?: new (data: any) => T): LiveConfigProxy<T>;
|
|
3675
|
+
/** @internal — queue a config declaration with the management buffer. */
|
|
3676
|
+
_observeConfigDeclaration(configId: string, parent: string | null, name: string | null, description: string | null): void;
|
|
3677
|
+
/** @internal — queue a config item declaration with the management buffer. */
|
|
3678
|
+
_observeItemDeclaration(configId: string, itemKey: string, itemType: string, defaultValue: unknown, description?: string): void;
|
|
3568
3679
|
/**
|
|
3569
3680
|
* Register a change listener.
|
|
3570
3681
|
*
|
package/dist/index.d.ts
CHANGED
|
@@ -632,6 +632,28 @@ declare class LiveConfigProxy<T = Record<string, unknown>> {
|
|
|
632
632
|
items(): Array<[string, unknown]>;
|
|
633
633
|
/** Dict method: get a value by key, returning `defaultValue` if absent. */
|
|
634
634
|
get<V = unknown>(key: string, defaultValue?: V): V | unknown;
|
|
635
|
+
/** @internal */
|
|
636
|
+
private _registerItem;
|
|
637
|
+
/** Read a BOOLEAN item, registering the declaration on first call. */
|
|
638
|
+
getBool(key: string, defaultValue: boolean, options?: {
|
|
639
|
+
description?: string;
|
|
640
|
+
}): boolean;
|
|
641
|
+
/** Read a NUMBER item as int, registering the declaration on first call. */
|
|
642
|
+
getInt(key: string, defaultValue: number, options?: {
|
|
643
|
+
description?: string;
|
|
644
|
+
}): number;
|
|
645
|
+
/** Read a NUMBER item as float, registering the declaration on first call. */
|
|
646
|
+
getFloat(key: string, defaultValue: number, options?: {
|
|
647
|
+
description?: string;
|
|
648
|
+
}): number;
|
|
649
|
+
/** Read a STRING item, registering the declaration on first call. */
|
|
650
|
+
getString(key: string, defaultValue: string, options?: {
|
|
651
|
+
description?: string;
|
|
652
|
+
}): string;
|
|
653
|
+
/** Read a JSON item, registering the declaration on first call. */
|
|
654
|
+
getJson<V = unknown>(key: string, defaultValue: V, options?: {
|
|
655
|
+
description?: string;
|
|
656
|
+
}): V;
|
|
635
657
|
/**
|
|
636
658
|
* Register a change listener scoped to this config.
|
|
637
659
|
*
|
|
@@ -2365,13 +2387,69 @@ declare class Config {
|
|
|
2365
2387
|
}
|
|
2366
2388
|
|
|
2367
2389
|
type ConfigHttp$1 = ReturnType<typeof createClient<___generated_config_d_ts.paths>>;
|
|
2390
|
+
interface ConfigBufferEntry {
|
|
2391
|
+
id: string;
|
|
2392
|
+
items: Record<string, {
|
|
2393
|
+
value: unknown;
|
|
2394
|
+
type: string;
|
|
2395
|
+
description?: string;
|
|
2396
|
+
}>;
|
|
2397
|
+
service?: string;
|
|
2398
|
+
environment?: string;
|
|
2399
|
+
parent?: string;
|
|
2400
|
+
name?: string;
|
|
2401
|
+
description?: string;
|
|
2402
|
+
}
|
|
2403
|
+
interface ConfigBufferMeta {
|
|
2404
|
+
service: string | null;
|
|
2405
|
+
environment: string | null;
|
|
2406
|
+
parent: string | null;
|
|
2407
|
+
name: string | null;
|
|
2408
|
+
description: string | null;
|
|
2409
|
+
}
|
|
2410
|
+
/**
|
|
2411
|
+
* Buffer pending config declarations for bulk registration. @internal
|
|
2412
|
+
*
|
|
2413
|
+
* Configs differ from flags because each entry carries a nested `items`
|
|
2414
|
+
* dict that grows incrementally as typed getters fire. We store per-config
|
|
2415
|
+
* metadata permanently so post-flush deltas re-attribute correctly, and
|
|
2416
|
+
* dedupe items per `(configId, itemKey)` so an already-sent item never
|
|
2417
|
+
* re-sends. Mirrors Python's `_ConfigRegistrationBuffer`.
|
|
2418
|
+
*/
|
|
2419
|
+
declare class ConfigRegistrationBuffer {
|
|
2420
|
+
private _pending;
|
|
2421
|
+
private _meta;
|
|
2422
|
+
private _sentItems;
|
|
2423
|
+
declare(configId: string, meta: ConfigBufferMeta): void;
|
|
2424
|
+
addItem(configId: string, itemKey: string, itemType: string, defaultValue: unknown, description: string | null): void;
|
|
2425
|
+
private _buildEntry;
|
|
2426
|
+
/** Destructive drain — records sent items so they aren't re-queued. */
|
|
2427
|
+
drain(): ConfigBufferEntry[];
|
|
2428
|
+
get pendingCount(): number;
|
|
2429
|
+
}
|
|
2368
2430
|
/**
|
|
2369
|
-
* `mgmt.config.*` — CRUD client for configs.
|
|
2431
|
+
* `mgmt.config.*` — CRUD client for configs + bulk registration buffer.
|
|
2370
2432
|
*/
|
|
2371
2433
|
declare class ManagementConfigClient {
|
|
2372
2434
|
private readonly _http;
|
|
2373
2435
|
/** @internal */
|
|
2436
|
+
readonly _buffer: ConfigRegistrationBuffer;
|
|
2437
|
+
/** @internal */
|
|
2374
2438
|
constructor(_http: ConfigHttp$1);
|
|
2439
|
+
/** @internal — queue a configuration declaration for bulk-discovery upload. */
|
|
2440
|
+
registerConfig(configId: string, meta: {
|
|
2441
|
+
service: string | null;
|
|
2442
|
+
environment: string | null;
|
|
2443
|
+
parent?: string | null;
|
|
2444
|
+
name?: string | null;
|
|
2445
|
+
description?: string | null;
|
|
2446
|
+
}): void;
|
|
2447
|
+
/** @internal — queue a config item declaration. */
|
|
2448
|
+
registerConfigItem(configId: string, itemKey: string, itemType: string, defaultValue: unknown, description: string | null): void;
|
|
2449
|
+
/** Send any pending config declarations to `POST /api/v1/configs/bulk`. */
|
|
2450
|
+
flush(): Promise<void>;
|
|
2451
|
+
/** Number of pending config declarations awaiting flush. */
|
|
2452
|
+
get pendingCount(): number;
|
|
2375
2453
|
/** Construct an unsaved {@link Config}. Call `.save()` to persist. */
|
|
2376
2454
|
new(id: string, options?: {
|
|
2377
2455
|
name?: string;
|
|
@@ -3544,6 +3622,11 @@ declare class ConfigClient {
|
|
|
3544
3622
|
* cache for everyone (including descendants that inherit from it)
|
|
3545
3623
|
* without a full re-list. Mirrors Python's `_raw_config_cache`. */
|
|
3546
3624
|
private _configStore;
|
|
3625
|
+
/** Cache of LiveConfigProxy instances by config id — ensures repeat
|
|
3626
|
+
* `get_or_create(id)` (or `get(id)` after discovery) returns the same
|
|
3627
|
+
* handle so callers can reference it as a parent via direct ref.
|
|
3628
|
+
* Mirrors Python's `_proxies`. */
|
|
3629
|
+
private _proxies;
|
|
3547
3630
|
private _initialized;
|
|
3548
3631
|
private _listeners;
|
|
3549
3632
|
/** @internal */
|
|
@@ -3565,6 +3648,34 @@ declare class ConfigClient {
|
|
|
3565
3648
|
* There is no `subscribe()` — it was unified into `get()`.
|
|
3566
3649
|
*/
|
|
3567
3650
|
get<T = Record<string, unknown>>(id: string, model?: new (data: any) => T): Promise<LiveConfigProxy<T>>;
|
|
3651
|
+
/**
|
|
3652
|
+
* Declare a configuration from code; return a live, dict-like view.
|
|
3653
|
+
*
|
|
3654
|
+
* Idempotent. Repeated calls with the same `id` return the same
|
|
3655
|
+
* {@link LiveConfigProxy} instance. The first call queues a discovery
|
|
3656
|
+
* payload (the config and any items declared via typed getters on the
|
|
3657
|
+
* returned handle) for upload to `POST /api/v1/configs/bulk` on next
|
|
3658
|
+
* flush. If the config already exists server-side, `managed=true`
|
|
3659
|
+
* configs are left untouched; `managed=false` configs receive the
|
|
3660
|
+
* SDK's items via source-row upsert per ADR-024 §2.9.
|
|
3661
|
+
*
|
|
3662
|
+
* Unlike {@link get}, this method does NOT raise `NotFoundError` when
|
|
3663
|
+
* the id is absent from the cache — discovery handles that case.
|
|
3664
|
+
*
|
|
3665
|
+
* Mirrors Python's `client.config.get_or_create(id, ...)`.
|
|
3666
|
+
*/
|
|
3667
|
+
getOrCreate<T = Record<string, unknown>>(id: string, options?: {
|
|
3668
|
+
parent?: string | LiveConfigProxy<any> | null;
|
|
3669
|
+
name?: string;
|
|
3670
|
+
description?: string;
|
|
3671
|
+
model?: new (data: any) => T;
|
|
3672
|
+
}): Promise<LiveConfigProxy<T>>;
|
|
3673
|
+
/** @internal — return (and cache) the canonical proxy for a config id. */
|
|
3674
|
+
_cachedProxy<T>(id: string, model?: new (data: any) => T): LiveConfigProxy<T>;
|
|
3675
|
+
/** @internal — queue a config declaration with the management buffer. */
|
|
3676
|
+
_observeConfigDeclaration(configId: string, parent: string | null, name: string | null, description: string | null): void;
|
|
3677
|
+
/** @internal — queue a config item declaration with the management buffer. */
|
|
3678
|
+
_observeItemDeclaration(configId: string, itemKey: string, itemType: string, defaultValue: unknown, description?: string): void;
|
|
3568
3679
|
/**
|
|
3569
3680
|
* Register a change listener.
|
|
3570
3681
|
*
|
package/dist/index.js
CHANGED
|
@@ -17441,7 +17441,20 @@ var LiveConfigProxy = class {
|
|
|
17441
17441
|
this._client = client;
|
|
17442
17442
|
this._key = key;
|
|
17443
17443
|
this._model = model;
|
|
17444
|
-
const ownMethods = /* @__PURE__ */ new Set([
|
|
17444
|
+
const ownMethods = /* @__PURE__ */ new Set([
|
|
17445
|
+
"keys",
|
|
17446
|
+
"values",
|
|
17447
|
+
"items",
|
|
17448
|
+
"get",
|
|
17449
|
+
"onChange",
|
|
17450
|
+
"getBool",
|
|
17451
|
+
"getInt",
|
|
17452
|
+
"getFloat",
|
|
17453
|
+
"getString",
|
|
17454
|
+
"getJson",
|
|
17455
|
+
"_currentValues",
|
|
17456
|
+
"_registerItem"
|
|
17457
|
+
]);
|
|
17445
17458
|
return new Proxy(this, {
|
|
17446
17459
|
get(target, prop, receiver) {
|
|
17447
17460
|
if (typeof prop === "symbol" || prop === "constructor" || prop === "toJSON") {
|
|
@@ -17458,7 +17471,10 @@ var LiveConfigProxy = class {
|
|
|
17458
17471
|
}
|
|
17459
17472
|
return values[prop];
|
|
17460
17473
|
},
|
|
17461
|
-
set(
|
|
17474
|
+
set(target, prop, value, receiver) {
|
|
17475
|
+
if (typeof prop === "string" && prop.startsWith("_")) {
|
|
17476
|
+
return Reflect.set(target, prop, value, receiver);
|
|
17477
|
+
}
|
|
17462
17478
|
throw new Error(
|
|
17463
17479
|
`LiveConfigProxy is read-only; cannot set ${JSON.stringify(String(prop))}. Mutate config values via client.manage.config.*`
|
|
17464
17480
|
);
|
|
@@ -17513,6 +17529,74 @@ var LiveConfigProxy = class {
|
|
|
17513
17529
|
const values = this._currentValues();
|
|
17514
17530
|
return key in values ? values[key] : defaultValue;
|
|
17515
17531
|
}
|
|
17532
|
+
// ------------------------------------------------------------------
|
|
17533
|
+
// Typed getters (ADR-037 §2.13)
|
|
17534
|
+
//
|
|
17535
|
+
// Each registers the item (key, type, default, description) on first
|
|
17536
|
+
// call within the process, then returns the resolved value. When the
|
|
17537
|
+
// resolved value cannot be coerced to the getter's type — including
|
|
17538
|
+
// the "not yet set on the server" case — the in-code default is
|
|
17539
|
+
// returned and a structured warning is logged.
|
|
17540
|
+
// ------------------------------------------------------------------
|
|
17541
|
+
/** @internal */
|
|
17542
|
+
_registerItem(itemKey, itemType, defaultValue, description) {
|
|
17543
|
+
this._client._observeItemDeclaration(this._key, itemKey, itemType, defaultValue, description);
|
|
17544
|
+
}
|
|
17545
|
+
/** Read a BOOLEAN item, registering the declaration on first call. */
|
|
17546
|
+
getBool(key, defaultValue, options = {}) {
|
|
17547
|
+
this._registerItem(key, "BOOLEAN", defaultValue, options.description);
|
|
17548
|
+
const values = this._currentValues();
|
|
17549
|
+
if (!(key in values)) return defaultValue;
|
|
17550
|
+
const value = values[key];
|
|
17551
|
+
if (typeof value === "boolean") return value;
|
|
17552
|
+
console.warn(
|
|
17553
|
+
`[smplkit] config ${JSON.stringify(this._key)} item ${JSON.stringify(key)}: expected BOOLEAN, got ${typeof value}; returning default`
|
|
17554
|
+
);
|
|
17555
|
+
return defaultValue;
|
|
17556
|
+
}
|
|
17557
|
+
/** Read a NUMBER item as int, registering the declaration on first call. */
|
|
17558
|
+
getInt(key, defaultValue, options = {}) {
|
|
17559
|
+
this._registerItem(key, "NUMBER", defaultValue, options.description);
|
|
17560
|
+
const values = this._currentValues();
|
|
17561
|
+
if (!(key in values)) return defaultValue;
|
|
17562
|
+
const value = values[key];
|
|
17563
|
+
if (typeof value === "number" && Number.isInteger(value)) return value;
|
|
17564
|
+
console.warn(
|
|
17565
|
+
`[smplkit] config ${JSON.stringify(this._key)} item ${JSON.stringify(key)}: expected NUMBER (int), got ${typeof value}; returning default`
|
|
17566
|
+
);
|
|
17567
|
+
return defaultValue;
|
|
17568
|
+
}
|
|
17569
|
+
/** Read a NUMBER item as float, registering the declaration on first call. */
|
|
17570
|
+
getFloat(key, defaultValue, options = {}) {
|
|
17571
|
+
this._registerItem(key, "NUMBER", defaultValue, options.description);
|
|
17572
|
+
const values = this._currentValues();
|
|
17573
|
+
if (!(key in values)) return defaultValue;
|
|
17574
|
+
const value = values[key];
|
|
17575
|
+
if (typeof value === "number") return value;
|
|
17576
|
+
console.warn(
|
|
17577
|
+
`[smplkit] config ${JSON.stringify(this._key)} item ${JSON.stringify(key)}: expected NUMBER (float), got ${typeof value}; returning default`
|
|
17578
|
+
);
|
|
17579
|
+
return defaultValue;
|
|
17580
|
+
}
|
|
17581
|
+
/** Read a STRING item, registering the declaration on first call. */
|
|
17582
|
+
getString(key, defaultValue, options = {}) {
|
|
17583
|
+
this._registerItem(key, "STRING", defaultValue, options.description);
|
|
17584
|
+
const values = this._currentValues();
|
|
17585
|
+
if (!(key in values)) return defaultValue;
|
|
17586
|
+
const value = values[key];
|
|
17587
|
+
if (typeof value === "string") return value;
|
|
17588
|
+
console.warn(
|
|
17589
|
+
`[smplkit] config ${JSON.stringify(this._key)} item ${JSON.stringify(key)}: expected STRING, got ${typeof value}; returning default`
|
|
17590
|
+
);
|
|
17591
|
+
return defaultValue;
|
|
17592
|
+
}
|
|
17593
|
+
/** Read a JSON item, registering the declaration on first call. */
|
|
17594
|
+
getJson(key, defaultValue, options = {}) {
|
|
17595
|
+
this._registerItem(key, "JSON", defaultValue, options.description);
|
|
17596
|
+
const values = this._currentValues();
|
|
17597
|
+
if (!(key in values)) return defaultValue;
|
|
17598
|
+
return values[key];
|
|
17599
|
+
}
|
|
17516
17600
|
onChange(callbackOrItemKey, callback) {
|
|
17517
17601
|
if (typeof callbackOrItemKey === "function") {
|
|
17518
17602
|
this._client.onChange(this._key, callbackOrItemKey);
|
|
@@ -17623,6 +17707,11 @@ var ConfigClient = class {
|
|
|
17623
17707
|
* cache for everyone (including descendants that inherit from it)
|
|
17624
17708
|
* without a full re-list. Mirrors Python's `_raw_config_cache`. */
|
|
17625
17709
|
_configStore = {};
|
|
17710
|
+
/** Cache of LiveConfigProxy instances by config id — ensures repeat
|
|
17711
|
+
* `get_or_create(id)` (or `get(id)` after discovery) returns the same
|
|
17712
|
+
* handle so callers can reference it as a parent via direct ref.
|
|
17713
|
+
* Mirrors Python's `_proxies`. */
|
|
17714
|
+
_proxies = {};
|
|
17626
17715
|
_initialized = false;
|
|
17627
17716
|
_listeners = [];
|
|
17628
17717
|
/** @internal */
|
|
@@ -17682,7 +17771,65 @@ var ConfigClient = class {
|
|
|
17682
17771
|
if (metrics) {
|
|
17683
17772
|
metrics.record("config.resolutions", 1, "resolutions", { config: id });
|
|
17684
17773
|
}
|
|
17685
|
-
return
|
|
17774
|
+
return this._cachedProxy(id, model);
|
|
17775
|
+
}
|
|
17776
|
+
/**
|
|
17777
|
+
* Declare a configuration from code; return a live, dict-like view.
|
|
17778
|
+
*
|
|
17779
|
+
* Idempotent. Repeated calls with the same `id` return the same
|
|
17780
|
+
* {@link LiveConfigProxy} instance. The first call queues a discovery
|
|
17781
|
+
* payload (the config and any items declared via typed getters on the
|
|
17782
|
+
* returned handle) for upload to `POST /api/v1/configs/bulk` on next
|
|
17783
|
+
* flush. If the config already exists server-side, `managed=true`
|
|
17784
|
+
* configs are left untouched; `managed=false` configs receive the
|
|
17785
|
+
* SDK's items via source-row upsert per ADR-024 §2.9.
|
|
17786
|
+
*
|
|
17787
|
+
* Unlike {@link get}, this method does NOT raise `NotFoundError` when
|
|
17788
|
+
* the id is absent from the cache — discovery handles that case.
|
|
17789
|
+
*
|
|
17790
|
+
* Mirrors Python's `client.config.get_or_create(id, ...)`.
|
|
17791
|
+
*/
|
|
17792
|
+
async getOrCreate(id, options = {}) {
|
|
17793
|
+
const parent = options.parent;
|
|
17794
|
+
const parentId = parent instanceof LiveConfigProxy ? parent._key : parent ?? null;
|
|
17795
|
+
this._observeConfigDeclaration(id, parentId, options.name ?? null, options.description ?? null);
|
|
17796
|
+
await this._ensureInitialized();
|
|
17797
|
+
return this._cachedProxy(id, options.model);
|
|
17798
|
+
}
|
|
17799
|
+
/** @internal — return (and cache) the canonical proxy for a config id. */
|
|
17800
|
+
_cachedProxy(id, model) {
|
|
17801
|
+
let proxy = this._proxies[id];
|
|
17802
|
+
if (!proxy) {
|
|
17803
|
+
proxy = new LiveConfigProxy(this, id, model);
|
|
17804
|
+
this._proxies[id] = proxy;
|
|
17805
|
+
} else if (model !== void 0 && proxy._model === void 0) {
|
|
17806
|
+
proxy._model = model;
|
|
17807
|
+
}
|
|
17808
|
+
return proxy;
|
|
17809
|
+
}
|
|
17810
|
+
/** @internal — queue a config declaration with the management buffer. */
|
|
17811
|
+
_observeConfigDeclaration(configId, parent, name, description) {
|
|
17812
|
+
const manage = this._resolveManagement?.();
|
|
17813
|
+
if (!manage) return;
|
|
17814
|
+
manage.config.registerConfig(configId, {
|
|
17815
|
+
service: this._parent?._service ?? null,
|
|
17816
|
+
environment: this._parent?._environment ?? "",
|
|
17817
|
+
parent,
|
|
17818
|
+
name,
|
|
17819
|
+
description
|
|
17820
|
+
});
|
|
17821
|
+
}
|
|
17822
|
+
/** @internal — queue a config item declaration with the management buffer. */
|
|
17823
|
+
_observeItemDeclaration(configId, itemKey, itemType, defaultValue, description) {
|
|
17824
|
+
const manage = this._resolveManagement?.();
|
|
17825
|
+
if (!manage) return;
|
|
17826
|
+
manage.config.registerConfigItem(
|
|
17827
|
+
configId,
|
|
17828
|
+
itemKey,
|
|
17829
|
+
itemType,
|
|
17830
|
+
defaultValue,
|
|
17831
|
+
description ?? null
|
|
17832
|
+
);
|
|
17686
17833
|
}
|
|
17687
17834
|
// ------------------------------------------------------------------
|
|
17688
17835
|
// Runtime: change listeners (3-level overloads)
|
|
@@ -17805,6 +17952,17 @@ var ConfigClient = class {
|
|
|
17805
17952
|
if (!environment) {
|
|
17806
17953
|
throw new SmplError("No environment set. Ensure SmplClient is configured.");
|
|
17807
17954
|
}
|
|
17955
|
+
const manage = this._resolveManagement?.();
|
|
17956
|
+
if (manage) {
|
|
17957
|
+
try {
|
|
17958
|
+
await manage.config.flush();
|
|
17959
|
+
} catch (err) {
|
|
17960
|
+
debug(
|
|
17961
|
+
"config",
|
|
17962
|
+
`pre-start discovery flush failed: ${err instanceof Error ? err.message : String(err)}`
|
|
17963
|
+
);
|
|
17964
|
+
}
|
|
17965
|
+
}
|
|
17808
17966
|
const configs = await this._listConfigs();
|
|
17809
17967
|
const cache = {};
|
|
17810
17968
|
const store = {};
|
|
@@ -18758,11 +18916,99 @@ function resourceToConfig2(resource, client) {
|
|
|
18758
18916
|
updatedAt: attrs.updated_at ?? null
|
|
18759
18917
|
});
|
|
18760
18918
|
}
|
|
18919
|
+
var CONFIG_REGISTRATION_FLUSH_SIZE = 50;
|
|
18920
|
+
var ConfigRegistrationBuffer = class {
|
|
18921
|
+
_pending = /* @__PURE__ */ new Map();
|
|
18922
|
+
_meta = /* @__PURE__ */ new Map();
|
|
18923
|
+
_sentItems = /* @__PURE__ */ new Set();
|
|
18924
|
+
declare(configId, meta) {
|
|
18925
|
+
if (this._meta.has(configId)) return;
|
|
18926
|
+
this._meta.set(configId, meta);
|
|
18927
|
+
this._pending.set(configId, this._buildEntry(configId, {}));
|
|
18928
|
+
}
|
|
18929
|
+
addItem(configId, itemKey, itemType, defaultValue, description) {
|
|
18930
|
+
if (!this._meta.has(configId)) return;
|
|
18931
|
+
const sentKey = `${configId}::${itemKey}`;
|
|
18932
|
+
if (this._sentItems.has(sentKey)) return;
|
|
18933
|
+
let entry = this._pending.get(configId);
|
|
18934
|
+
if (!entry) {
|
|
18935
|
+
entry = this._buildEntry(configId, {});
|
|
18936
|
+
this._pending.set(configId, entry);
|
|
18937
|
+
}
|
|
18938
|
+
if (itemKey in entry.items) return;
|
|
18939
|
+
const def = { value: defaultValue, type: itemType };
|
|
18940
|
+
if (description !== null) def.description = description;
|
|
18941
|
+
entry.items[itemKey] = def;
|
|
18942
|
+
}
|
|
18943
|
+
_buildEntry(configId, items) {
|
|
18944
|
+
const meta = this._meta.get(configId);
|
|
18945
|
+
const entry = { id: configId, items };
|
|
18946
|
+
if (meta.service !== null) entry.service = meta.service;
|
|
18947
|
+
if (meta.environment !== null) entry.environment = meta.environment;
|
|
18948
|
+
if (meta.parent !== null) entry.parent = meta.parent;
|
|
18949
|
+
if (meta.name !== null) entry.name = meta.name;
|
|
18950
|
+
if (meta.description !== null) entry.description = meta.description;
|
|
18951
|
+
return entry;
|
|
18952
|
+
}
|
|
18953
|
+
/** Destructive drain — records sent items so they aren't re-queued. */
|
|
18954
|
+
drain() {
|
|
18955
|
+
const batch = Array.from(this._pending.values());
|
|
18956
|
+
for (const entry of batch) {
|
|
18957
|
+
for (const itemKey of Object.keys(entry.items)) {
|
|
18958
|
+
this._sentItems.add(`${entry.id}::${itemKey}`);
|
|
18959
|
+
}
|
|
18960
|
+
}
|
|
18961
|
+
this._pending.clear();
|
|
18962
|
+
return batch;
|
|
18963
|
+
}
|
|
18964
|
+
get pendingCount() {
|
|
18965
|
+
return this._pending.size;
|
|
18966
|
+
}
|
|
18967
|
+
};
|
|
18761
18968
|
var ManagementConfigClient = class {
|
|
18762
18969
|
/** @internal */
|
|
18763
18970
|
constructor(_http) {
|
|
18764
18971
|
this._http = _http;
|
|
18765
18972
|
}
|
|
18973
|
+
/** @internal */
|
|
18974
|
+
_buffer = new ConfigRegistrationBuffer();
|
|
18975
|
+
/** @internal — queue a configuration declaration for bulk-discovery upload. */
|
|
18976
|
+
registerConfig(configId, meta) {
|
|
18977
|
+
this._buffer.declare(configId, {
|
|
18978
|
+
service: meta.service,
|
|
18979
|
+
environment: meta.environment,
|
|
18980
|
+
parent: meta.parent ?? null,
|
|
18981
|
+
name: meta.name ?? null,
|
|
18982
|
+
description: meta.description ?? null
|
|
18983
|
+
});
|
|
18984
|
+
if (this._buffer.pendingCount >= CONFIG_REGISTRATION_FLUSH_SIZE) {
|
|
18985
|
+
void this.flush();
|
|
18986
|
+
}
|
|
18987
|
+
}
|
|
18988
|
+
/** @internal — queue a config item declaration. */
|
|
18989
|
+
registerConfigItem(configId, itemKey, itemType, defaultValue, description) {
|
|
18990
|
+
this._buffer.addItem(configId, itemKey, itemType, defaultValue, description);
|
|
18991
|
+
if (this._buffer.pendingCount >= CONFIG_REGISTRATION_FLUSH_SIZE) {
|
|
18992
|
+
void this.flush();
|
|
18993
|
+
}
|
|
18994
|
+
}
|
|
18995
|
+
/** Send any pending config declarations to `POST /api/v1/configs/bulk`. */
|
|
18996
|
+
async flush() {
|
|
18997
|
+
const batch = this._buffer.drain();
|
|
18998
|
+
if (batch.length === 0) return;
|
|
18999
|
+
try {
|
|
19000
|
+
const result = await this._http.POST("/api/v1/configs/bulk", {
|
|
19001
|
+
body: { configs: batch }
|
|
19002
|
+
});
|
|
19003
|
+
if (!result.response.ok) {
|
|
19004
|
+
}
|
|
19005
|
+
} catch {
|
|
19006
|
+
}
|
|
19007
|
+
}
|
|
19008
|
+
/** Number of pending config declarations awaiting flush. */
|
|
19009
|
+
get pendingCount() {
|
|
19010
|
+
return this._buffer.pendingCount;
|
|
19011
|
+
}
|
|
18766
19012
|
/** Construct an unsaved {@link Config}. Call `.save()` to persist. */
|
|
18767
19013
|
new(id, options = {}) {
|
|
18768
19014
|
const parent = options.parent;
|
|
@@ -21024,6 +21270,7 @@ var SmplManagementClient = class {
|
|
|
21024
21270
|
await this.contexts.flush();
|
|
21025
21271
|
await this.flags.flush();
|
|
21026
21272
|
await this.loggers.flush();
|
|
21273
|
+
await this.config.flush();
|
|
21027
21274
|
} catch {
|
|
21028
21275
|
}
|
|
21029
21276
|
}
|