@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.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(["keys", "values", "items", "get", "onChange", "_currentValues"]);
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(_target, prop, _value) {
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 new LiveConfigProxy(this, id, model);
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
  }