@smplkit/sdk 3.0.0 → 3.0.2

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 CHANGED
@@ -16903,6 +16903,13 @@ function environmentsToWire(environments) {
16903
16903
  }
16904
16904
  return out;
16905
16905
  }
16906
+ function environmentsForResolver(environments) {
16907
+ const out = {};
16908
+ for (const [envId, env] of Object.entries(environments)) {
16909
+ out[envId] = { values: env.values };
16910
+ }
16911
+ return out;
16912
+ }
16906
16913
  var Config = class {
16907
16914
  id;
16908
16915
  name;
@@ -17092,8 +17099,8 @@ var Config = class {
17092
17099
  const chain = [
17093
17100
  {
17094
17101
  id: this.id,
17095
- items: this._itemsRaw,
17096
- environments: environmentsToWire(this._environments)
17102
+ items: this.items,
17103
+ environments: environmentsForResolver(this._environments)
17097
17104
  }
17098
17105
  ];
17099
17106
  let current = this;
@@ -17115,8 +17122,8 @@ var Config = class {
17115
17122
  }
17116
17123
  chain.push({
17117
17124
  id: parentConfig.id,
17118
- items: parentConfig._itemsRaw,
17119
- environments: environmentsToWire(parentConfig._environments)
17125
+ items: parentConfig.items,
17126
+ environments: environmentsForResolver(parentConfig._environments)
17120
17127
  });
17121
17128
  current = parentConfig;
17122
17129
  }
@@ -17128,6 +17135,22 @@ var Config = class {
17128
17135
  };
17129
17136
 
17130
17137
  // src/config/proxy.ts
17138
+ function _unflattenDotNotation(flat) {
17139
+ const nested = {};
17140
+ for (const [key, value] of Object.entries(flat)) {
17141
+ const parts = key.split(".");
17142
+ let current = nested;
17143
+ for (let i = 0; i < parts.length - 1; i++) {
17144
+ const part = parts[i];
17145
+ if (current[part] === void 0 || typeof current[part] !== "object" || current[part] === null) {
17146
+ current[part] = {};
17147
+ }
17148
+ current = current[part];
17149
+ }
17150
+ current[parts[parts.length - 1]] = value;
17151
+ }
17152
+ return nested;
17153
+ }
17131
17154
  var LiveConfigProxy = class {
17132
17155
  /** @internal */
17133
17156
  _client;
@@ -17150,7 +17173,8 @@ var LiveConfigProxy = class {
17150
17173
  }
17151
17174
  const values = target._currentValues();
17152
17175
  if (target._model) {
17153
- const instance = new target._model(values);
17176
+ const nested = _unflattenDotNotation(values);
17177
+ const instance = new target._model(nested);
17154
17178
  return instance[prop];
17155
17179
  }
17156
17180
  return values[prop];
@@ -17312,6 +17336,11 @@ var ConfigClient = class {
17312
17336
  /** @internal — resolves the management config sub-client used by lazy-init/refresh. */
17313
17337
  _resolveManagement;
17314
17338
  _configCache = {};
17339
+ /** Raw Config objects keyed by id, kept around so a single-config
17340
+ * change (WS event) can refetch one config and rebuild the resolved
17341
+ * cache for everyone (including descendants that inherit from it)
17342
+ * without a full re-list. Mirrors Python's `_raw_config_cache`. */
17343
+ _configStore = {};
17315
17344
  _initialized = false;
17316
17345
  _listeners = [];
17317
17346
  /** @internal */
@@ -17420,12 +17449,15 @@ var ConfigClient = class {
17420
17449
  }
17421
17450
  const configs = await this._listConfigs();
17422
17451
  const newCache = {};
17452
+ const newStore = {};
17423
17453
  for (const cfg of configs) {
17424
17454
  const chain = await cfg._buildChain(configs);
17425
17455
  newCache[cfg.id] = resolveChain(chain, environment);
17456
+ newStore[cfg.id] = cfg;
17426
17457
  }
17427
17458
  const oldCache = this._configCache;
17428
17459
  this._configCache = newCache;
17460
+ this._configStore = newStore;
17429
17461
  this._diffAndFire(oldCache, newCache, "manual");
17430
17462
  }
17431
17463
  /**
@@ -17467,11 +17499,14 @@ var ConfigClient = class {
17467
17499
  }
17468
17500
  const configs = await this._listConfigs();
17469
17501
  const cache = {};
17502
+ const store = {};
17470
17503
  for (const cfg of configs) {
17471
17504
  const chain = await cfg._buildChain(configs);
17472
17505
  cache[cfg.id] = resolveChain(chain, environment);
17506
+ store[cfg.id] = cfg;
17473
17507
  }
17474
17508
  this._configCache = cache;
17509
+ this._configStore = store;
17475
17510
  this._initialized = true;
17476
17511
  if (this._getSharedWs) {
17477
17512
  const ws = this._getSharedWs();
@@ -17485,11 +17520,14 @@ var ConfigClient = class {
17485
17520
  if (this._initialized) return;
17486
17521
  const configs = await this._listConfigs();
17487
17522
  const cache = {};
17523
+ const store = {};
17488
17524
  for (const cfg of configs) {
17489
17525
  const chain = await cfg._buildChain(configs);
17490
17526
  cache[cfg.id] = resolveChain(chain, environment);
17527
+ store[cfg.id] = cfg;
17491
17528
  }
17492
17529
  this._configCache = cache;
17530
+ this._configStore = store;
17493
17531
  this._initialized = true;
17494
17532
  }
17495
17533
  /** @internal — get resolved config from cache. Used by LiveConfigProxy. */
@@ -17503,26 +17541,29 @@ var ConfigClient = class {
17503
17541
  debug("websocket", `config_changed event received: ${JSON.stringify(data)}`);
17504
17542
  const configKey = data.id;
17505
17543
  if (!configKey) return;
17544
+ const environment = this._parent?._environment;
17545
+ if (!environment) return;
17506
17546
  void this._fetchSingleConfig(configKey).then((newConfig) => {
17507
- const environment = this._parent?._environment;
17508
- if (!environment) return;
17509
- const oldValues = this._configCache[configKey];
17510
- let newValues;
17511
- if (newConfig !== null) {
17512
- newValues = this._resolveConfigValues(newConfig, environment);
17547
+ const newStore = { ...this._configStore };
17548
+ if (newConfig === null) {
17549
+ delete newStore[configKey];
17513
17550
  } else {
17514
- newValues = {};
17515
- }
17516
- const oldJson = JSON.stringify(oldValues ?? {});
17517
- const newJson = JSON.stringify(newValues);
17518
- if (oldJson === newJson) return;
17519
- const oldCache = { ...this._configCache };
17520
- if (newConfig !== null) {
17521
- this._configCache[configKey] = newValues;
17522
- } else {
17523
- delete this._configCache[configKey];
17524
- }
17525
- this._diffAndFire(oldCache, this._configCache, "websocket");
17551
+ newStore[configKey] = newConfig;
17552
+ }
17553
+ const allConfigs = Object.values(newStore);
17554
+ return Promise.all(
17555
+ allConfigs.map(async (cfg) => {
17556
+ const chain = await cfg._buildChain(allConfigs);
17557
+ return [cfg.id, resolveChain(chain, environment)];
17558
+ })
17559
+ ).then((entries) => ({ entries, newStore }));
17560
+ }).then(({ entries, newStore }) => {
17561
+ const newCache = {};
17562
+ for (const [id, values] of entries) newCache[id] = values;
17563
+ const oldCache = this._configCache;
17564
+ this._configCache = newCache;
17565
+ this._configStore = newStore;
17566
+ this._diffAndFire(oldCache, newCache, "websocket");
17526
17567
  }).catch((err) => {
17527
17568
  debug(
17528
17569
  "websocket",
@@ -17530,6 +17571,20 @@ var ConfigClient = class {
17530
17571
  );
17531
17572
  });
17532
17573
  };
17574
+ /** Fetch a single config by key. Returns null if not found. @internal */
17575
+ async _fetchSingleConfig(key) {
17576
+ debug("api", `GET /api/v1/configs/${key}`);
17577
+ try {
17578
+ const result = await this._http.GET("/api/v1/configs/{id}", {
17579
+ params: { path: { id: key } }
17580
+ });
17581
+ if (!result.response.ok) return null;
17582
+ if (!result.data?.data) return null;
17583
+ return resourceToConfig(result.data.data);
17584
+ } catch {
17585
+ return null;
17586
+ }
17587
+ }
17533
17588
  _handleConfigDeleted = (data) => {
17534
17589
  debug("websocket", `config_deleted event received: ${JSON.stringify(data)}`);
17535
17590
  const configKey = data.id;
@@ -17548,27 +17603,6 @@ var ConfigClient = class {
17548
17603
  // ------------------------------------------------------------------
17549
17604
  // Internal: change detection
17550
17605
  // ------------------------------------------------------------------
17551
- /** Fetch a single config by key. Returns null if not found. @internal */
17552
- async _fetchSingleConfig(key) {
17553
- debug("api", `GET /api/v1/configs/${key}`);
17554
- try {
17555
- const result = await this._http.GET("/api/v1/configs/{id}", {
17556
- params: { path: { id: key } }
17557
- });
17558
- if (!result.response.ok) return null;
17559
- if (!result.data?.data) return null;
17560
- return resourceToConfig(result.data.data);
17561
- } catch {
17562
- return null;
17563
- }
17564
- }
17565
- /** Resolve a config's values for an environment (no parent chain). @internal */
17566
- _resolveConfigValues(config, environment) {
17567
- const base = config.items ?? {};
17568
- const envEntry = config.environments?.[environment];
17569
- if (!envEntry?.values) return { ...base };
17570
- return { ...base, ...envEntry.values };
17571
- }
17572
17606
  /** @internal */
17573
17607
  _diffAndFire(oldCache, newCache, source) {
17574
17608
  const allConfigKeys = /* @__PURE__ */ new Set([...Object.keys(oldCache), ...Object.keys(newCache)]);
@@ -21497,6 +21531,7 @@ var LoggingClient = class {
21497
21531
  // src/ws.ts
21498
21532
  var import_ws = __toESM(require("ws"), 1);
21499
21533
  var BACKOFF_MS = [1e3, 2e3, 4e3, 8e3, 16e3, 32e3, 6e4];
21534
+ var SDK_VERSION = "0.0.0";
21500
21535
  var SharedWebSocket = class {
21501
21536
  _appBaseUrl;
21502
21537
  _apiKey;
@@ -21597,7 +21632,9 @@ var SharedWebSocket = class {
21597
21632
  const safeUrl = wsUrl.split("?")[0];
21598
21633
  debug("websocket", `connecting to ${safeUrl}`);
21599
21634
  try {
21600
- const ws = new import_ws.default(wsUrl);
21635
+ const ws = new import_ws.default(wsUrl, {
21636
+ headers: { "User-Agent": `smplkit-typescript-sdk/${SDK_VERSION}` }
21637
+ });
21601
21638
  this._ws = ws;
21602
21639
  ws.on("open", () => {
21603
21640
  if (this._closed) {