@smplkit/sdk 1.3.26 → 1.3.28

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.js CHANGED
@@ -16756,15 +16756,13 @@ function resolveChain(chain, environment) {
16756
16756
 
16757
16757
  // src/config/types.ts
16758
16758
  var Config = class {
16759
- /** UUID of the config, or `null` if unsaved. */
16759
+ /** Unique identifier (slug, e.g. `"user-service"`). */
16760
16760
  id;
16761
- /** Human-readable key (e.g. `"user-service"`). */
16762
- key;
16763
16761
  /** Display name. */
16764
16762
  name;
16765
16763
  /** Optional description. */
16766
16764
  description;
16767
- /** Parent config UUID, or null if this is a root config. */
16765
+ /** Parent config id (slug), or null if this is a root config. */
16768
16766
  parent;
16769
16767
  /** Base key-value pairs. */
16770
16768
  items;
@@ -16783,7 +16781,6 @@ var Config = class {
16783
16781
  constructor(client, fields) {
16784
16782
  this._client = client;
16785
16783
  this.id = fields.id;
16786
- this.key = fields.key;
16787
16784
  this.name = fields.name;
16788
16785
  this.description = fields.description;
16789
16786
  this.parent = fields.parent;
@@ -16799,7 +16796,7 @@ var Config = class {
16799
16796
  * Updates this instance in-place with the server response.
16800
16797
  */
16801
16798
  async save() {
16802
- if (this.id === null) {
16799
+ if (this.createdAt === null) {
16803
16800
  const created = await this._client._createConfig(this);
16804
16801
  this._apply(created);
16805
16802
  } else {
@@ -16833,7 +16830,6 @@ var Config = class {
16833
16830
  /** @internal — copy all fields from another Config instance. */
16834
16831
  _apply(other) {
16835
16832
  this.id = other.id;
16836
- this.key = other.key;
16837
16833
  this.name = other.name;
16838
16834
  this.description = other.description;
16839
16835
  this.parent = other.parent;
@@ -16843,7 +16839,7 @@ var Config = class {
16843
16839
  this.updatedAt = other.updatedAt;
16844
16840
  }
16845
16841
  toString() {
16846
- return `Config(id=${this.id}, key=${this.key}, name=${this.name})`;
16842
+ return `Config(id=${this.id}, name=${this.name})`;
16847
16843
  }
16848
16844
  };
16849
16845
 
@@ -16936,7 +16932,6 @@ function resourceToConfig(resource, client) {
16936
16932
  const attrs = resource.attributes;
16937
16933
  return new Config(client, {
16938
16934
  id: resource.id ?? null,
16939
- key: attrs.key ?? "",
16940
16935
  name: attrs.name,
16941
16936
  description: attrs.description ?? null,
16942
16937
  parent: attrs.parent ?? null,
@@ -16996,7 +16991,6 @@ function buildRequestBody(options) {
16996
16991
  const attrs = {
16997
16992
  name: options.name
16998
16993
  };
16999
- if (options.key !== void 0) attrs.key = options.key;
17000
16994
  if (options.description !== void 0) attrs.description = options.description;
17001
16995
  if (options.parent !== void 0) attrs.parent = options.parent;
17002
16996
  if (options.items !== void 0)
@@ -17055,11 +17049,10 @@ var ConfigClient = class {
17055
17049
  // Management: factory method
17056
17050
  // ------------------------------------------------------------------
17057
17051
  /** Create an unsaved config. Call `.save()` to persist. */
17058
- new(key, options) {
17052
+ new(id, options) {
17059
17053
  return new Config(this, {
17060
- id: null,
17061
- key,
17062
- name: options?.name ?? keyToDisplayName(key),
17054
+ id,
17055
+ name: options?.name ?? keyToDisplayName(id),
17063
17056
  description: options?.description ?? null,
17064
17057
  parent: options?.parent ?? null,
17065
17058
  items: {},
@@ -17071,9 +17064,9 @@ var ConfigClient = class {
17071
17064
  // ------------------------------------------------------------------
17072
17065
  // Management: CRUD
17073
17066
  // ------------------------------------------------------------------
17074
- /** Fetch a config by key. */
17075
- async get(key) {
17076
- return this._getByKey(key);
17067
+ /** Fetch a config by id. */
17068
+ async get(id) {
17069
+ return this._getById(id);
17077
17070
  }
17078
17071
  /** List all configs. */
17079
17072
  async list() {
@@ -17088,15 +17081,14 @@ var ConfigClient = class {
17088
17081
  if (!data) return [];
17089
17082
  return data.data.map((r) => resourceToConfig(r, this));
17090
17083
  }
17091
- /** Delete a config by key. */
17092
- async delete(key) {
17093
- const config = await this.get(key);
17084
+ /** Delete a config by id. */
17085
+ async delete(id) {
17094
17086
  try {
17095
17087
  const result = await this._http.DELETE("/api/v1/configs/{id}", {
17096
- params: { path: { id: config.id } }
17088
+ params: { path: { id } }
17097
17089
  });
17098
17090
  if (result.error !== void 0 && result.response.status !== 204)
17099
- await checkError(result.response, `Failed to delete config '${key}'`);
17091
+ await checkError(result.response, `Failed to delete config '${id}'`);
17100
17092
  } catch (err) {
17101
17093
  wrapFetchError(err);
17102
17094
  }
@@ -17107,8 +17099,8 @@ var ConfigClient = class {
17107
17099
  /** @internal — POST a new config. */
17108
17100
  async _createConfig(config) {
17109
17101
  const body = buildRequestBody({
17102
+ id: config.id,
17110
17103
  name: config.name,
17111
- key: config.key,
17112
17104
  description: config.description,
17113
17105
  parent: config.parent,
17114
17106
  items: config.items,
@@ -17130,7 +17122,6 @@ var ConfigClient = class {
17130
17122
  const body = buildRequestBody({
17131
17123
  id: config.id,
17132
17124
  name: config.name,
17133
- key: config.key,
17134
17125
  description: config.description,
17135
17126
  parent: config.parent,
17136
17127
  items: config.items,
@@ -17151,20 +17142,20 @@ var ConfigClient = class {
17151
17142
  if (!data || !data.data) throw new SmplValidationError(`Failed to update config ${config.id}`);
17152
17143
  return resourceToConfig(data.data, this);
17153
17144
  }
17154
- /** @internal — fetch a config by UUID. */
17155
- async _getById(configId) {
17145
+ /** @internal — fetch a config by id. */
17146
+ async _getById(id) {
17156
17147
  let data;
17157
17148
  try {
17158
17149
  const result = await this._http.GET("/api/v1/configs/{id}", {
17159
- params: { path: { id: configId } }
17150
+ params: { path: { id } }
17160
17151
  });
17161
17152
  if (result.error !== void 0)
17162
- await checkError(result.response, `Config ${configId} not found`);
17153
+ await checkError(result.response, `Config with id '${id}' not found`);
17163
17154
  data = result.data;
17164
17155
  } catch (err) {
17165
17156
  wrapFetchError(err);
17166
17157
  }
17167
- if (!data || !data.data) throw new SmplNotFoundError(`Config ${configId} not found`);
17158
+ if (!data || !data.data) throw new SmplNotFoundError(`Config with id '${id}' not found`);
17168
17159
  return resourceToConfig(data.data, this);
17169
17160
  }
17170
17161
  // ------------------------------------------------------------------
@@ -17176,11 +17167,15 @@ var ConfigClient = class {
17176
17167
  * Returns the resolved key-value pairs for the given config.
17177
17168
  * Optionally pass a model class to map the resolved values.
17178
17169
  */
17179
- async resolve(key, model) {
17170
+ async resolve(id, model) {
17180
17171
  await this._ensureInitialized();
17181
- const values = this._configCache[key];
17172
+ const values = this._configCache[id];
17182
17173
  if (values === void 0) {
17183
- throw new SmplNotFoundError(`Config with key '${key}' not found in cache`);
17174
+ throw new SmplNotFoundError(`Config with id '${id}' not found in cache`);
17175
+ }
17176
+ const metrics = this._parent?._metrics;
17177
+ if (metrics) {
17178
+ metrics.record("config.resolutions", 1, "resolutions", { config_id: id });
17184
17179
  }
17185
17180
  if (model) {
17186
17181
  return new model(values);
@@ -17193,12 +17188,12 @@ var ConfigClient = class {
17193
17188
  *
17194
17189
  * Optionally pass a model class to map the resolved values.
17195
17190
  */
17196
- async subscribe(key, model) {
17191
+ async subscribe(id, model) {
17197
17192
  await this._ensureInitialized();
17198
- if (!(key in this._configCache)) {
17199
- throw new SmplNotFoundError(`Config with key '${key}' not found in cache`);
17193
+ if (!(id in this._configCache)) {
17194
+ throw new SmplNotFoundError(`Config with id '${id}' not found in cache`);
17200
17195
  }
17201
- return new LiveConfigProxy(this, key, model);
17196
+ return new LiveConfigProxy(this, id, model);
17202
17197
  }
17203
17198
  // ------------------------------------------------------------------
17204
17199
  // Runtime: change listeners (3-level overloads)
@@ -17207,26 +17202,26 @@ var ConfigClient = class {
17207
17202
  * Register a change listener.
17208
17203
  *
17209
17204
  * - `onChange(callback)` — fires for any config change (global).
17210
- * - `onChange(configKey, callback)` — fires for changes to a specific config.
17211
- * - `onChange(configKey, itemKey, callback)` — fires for a specific item.
17205
+ * - `onChange(configId, callback)` — fires for changes to a specific config.
17206
+ * - `onChange(configId, itemKey, callback)` — fires for a specific item.
17212
17207
  */
17213
- onChange(callbackOrConfigKey, callbackOrItemKey, callback) {
17214
- if (typeof callbackOrConfigKey === "function") {
17208
+ onChange(callbackOrConfigId, callbackOrItemKey, callback) {
17209
+ if (typeof callbackOrConfigId === "function") {
17215
17210
  this._listeners.push({
17216
- callback: callbackOrConfigKey,
17217
- configKey: null,
17211
+ callback: callbackOrConfigId,
17212
+ configId: null,
17218
17213
  itemKey: null
17219
17214
  });
17220
17215
  } else if (typeof callbackOrItemKey === "function") {
17221
17216
  this._listeners.push({
17222
17217
  callback: callbackOrItemKey,
17223
- configKey: callbackOrConfigKey,
17218
+ configId: callbackOrConfigId,
17224
17219
  itemKey: null
17225
17220
  });
17226
17221
  } else if (typeof callbackOrItemKey === "string" && callback) {
17227
17222
  this._listeners.push({
17228
17223
  callback,
17229
- configKey: callbackOrConfigKey,
17224
+ configId: callbackOrConfigId,
17230
17225
  itemKey: callbackOrItemKey
17231
17226
  });
17232
17227
  }
@@ -17250,7 +17245,7 @@ var ConfigClient = class {
17250
17245
  const newCache = {};
17251
17246
  for (const cfg of configs) {
17252
17247
  const chain = await cfg._buildChain(configs);
17253
- newCache[cfg.key] = resolveChain(chain, environment);
17248
+ newCache[cfg.id] = resolveChain(chain, environment);
17254
17249
  }
17255
17250
  const oldCache = this._configCache;
17256
17251
  this._configCache = newCache;
@@ -17270,7 +17265,7 @@ var ConfigClient = class {
17270
17265
  const cache = {};
17271
17266
  for (const cfg of configs) {
17272
17267
  const chain = await cfg._buildChain(configs);
17273
- cache[cfg.key] = resolveChain(chain, environment);
17268
+ cache[cfg.id] = resolveChain(chain, environment);
17274
17269
  }
17275
17270
  this._configCache = cache;
17276
17271
  this._initialized = true;
@@ -17286,7 +17281,7 @@ var ConfigClient = class {
17286
17281
  const cache = {};
17287
17282
  for (const cfg of configs) {
17288
17283
  const chain = await cfg._buildChain(configs);
17289
- cache[cfg.key] = resolveChain(chain, environment);
17284
+ cache[cfg.id] = resolveChain(chain, environment);
17290
17285
  }
17291
17286
  this._configCache = cache;
17292
17287
  this._initialized = true;
@@ -17316,15 +17311,19 @@ var ConfigClient = class {
17316
17311
  const oldVal = iKey in oldItems ? oldItems[iKey] : null;
17317
17312
  const newVal = iKey in newItems ? newItems[iKey] : null;
17318
17313
  if (JSON.stringify(oldVal) !== JSON.stringify(newVal)) {
17314
+ const metrics = this._parent?._metrics;
17315
+ if (metrics) {
17316
+ metrics.record("config.changes", 1, "changes", { config_id: cfgKey });
17317
+ }
17319
17318
  const event = {
17320
- configKey: cfgKey,
17319
+ configId: cfgKey,
17321
17320
  itemKey: iKey,
17322
17321
  oldValue: oldVal,
17323
17322
  newValue: newVal,
17324
17323
  source
17325
17324
  };
17326
17325
  for (const listener of this._listeners) {
17327
- if (listener.configKey !== null && listener.configKey !== cfgKey) continue;
17326
+ if (listener.configId !== null && listener.configId !== cfgKey) continue;
17328
17327
  if (listener.itemKey !== null && listener.itemKey !== iKey) continue;
17329
17328
  try {
17330
17329
  listener.callback(event);
@@ -17335,26 +17334,6 @@ var ConfigClient = class {
17335
17334
  }
17336
17335
  }
17337
17336
  }
17338
- // ------------------------------------------------------------------
17339
- // Internal: fetch by key
17340
- // ------------------------------------------------------------------
17341
- async _getByKey(key) {
17342
- let data;
17343
- try {
17344
- const result = await this._http.GET("/api/v1/configs", {
17345
- params: { query: { "filter[key]": key } }
17346
- });
17347
- if (result.error !== void 0)
17348
- await checkError(result.response, `Config with key '${key}' not found`);
17349
- data = result.data;
17350
- } catch (err) {
17351
- wrapFetchError(err);
17352
- }
17353
- if (!data || !data.data || data.data.length === 0) {
17354
- throw new SmplNotFoundError(`Config with key '${key}' not found`);
17355
- }
17356
- return resourceToConfig(data.data[0], this);
17357
- }
17358
17337
  };
17359
17338
 
17360
17339
  // src/flags/client.ts
@@ -17362,10 +17341,8 @@ import createClient2 from "openapi-fetch";
17362
17341
 
17363
17342
  // src/flags/models.ts
17364
17343
  var Flag = class {
17365
- /** UUID of the flag, or `null` if unsaved. */
17344
+ /** Unique identifier (slug) within the account. */
17366
17345
  id;
17367
- /** Unique key within the account. */
17368
- key;
17369
17346
  /** Human-readable display name. */
17370
17347
  name;
17371
17348
  /** Value type: BOOLEAN, STRING, NUMERIC, or JSON. */
@@ -17388,7 +17365,6 @@ var Flag = class {
17388
17365
  constructor(client, fields) {
17389
17366
  this._client = client;
17390
17367
  this.id = fields.id;
17391
- this.key = fields.key;
17392
17368
  this.name = fields.name;
17393
17369
  this.type = fields.type;
17394
17370
  this.default = fields.default;
@@ -17405,7 +17381,7 @@ var Flag = class {
17405
17381
  * Updates this instance in-place with the server response.
17406
17382
  */
17407
17383
  async save() {
17408
- if (this.id === null) {
17384
+ if (this.createdAt === null) {
17409
17385
  const created = await this._client._createFlag(this);
17410
17386
  this._apply(created);
17411
17387
  } else {
@@ -17469,12 +17445,11 @@ var Flag = class {
17469
17445
  * Requires `initialize()` to have been called on the flags client.
17470
17446
  */
17471
17447
  get(options) {
17472
- return this._client._evaluateHandle(this.key, this.default, options?.context ?? null);
17448
+ return this._client._evaluateHandle(this.id, this.default, options?.context ?? null);
17473
17449
  }
17474
17450
  /** @internal — copy all fields from another Flag instance. */
17475
17451
  _apply(other) {
17476
17452
  this.id = other.id;
17477
- this.key = other.key;
17478
17453
  this.name = other.name;
17479
17454
  this.type = other.type;
17480
17455
  this.default = other.default;
@@ -17485,12 +17460,12 @@ var Flag = class {
17485
17460
  this.updatedAt = other.updatedAt;
17486
17461
  }
17487
17462
  toString() {
17488
- return `Flag(key=${this.key}, type=${this.type}, default=${this.default})`;
17463
+ return `Flag(id=${this.id}, type=${this.type}, default=${this.default})`;
17489
17464
  }
17490
17465
  };
17491
17466
  var BooleanFlag = class extends Flag {
17492
17467
  get(options) {
17493
- const value = this._client._evaluateHandle(this.key, this.default, options?.context ?? null);
17468
+ const value = this._client._evaluateHandle(this.id, this.default, options?.context ?? null);
17494
17469
  if (typeof value === "boolean") {
17495
17470
  return value;
17496
17471
  }
@@ -17499,7 +17474,7 @@ var BooleanFlag = class extends Flag {
17499
17474
  };
17500
17475
  var StringFlag = class extends Flag {
17501
17476
  get(options) {
17502
- const value = this._client._evaluateHandle(this.key, this.default, options?.context ?? null);
17477
+ const value = this._client._evaluateHandle(this.id, this.default, options?.context ?? null);
17503
17478
  if (typeof value === "string") {
17504
17479
  return value;
17505
17480
  }
@@ -17508,7 +17483,7 @@ var StringFlag = class extends Flag {
17508
17483
  };
17509
17484
  var NumberFlag = class extends Flag {
17510
17485
  get(options) {
17511
- const value = this._client._evaluateHandle(this.key, this.default, options?.context ?? null);
17486
+ const value = this._client._evaluateHandle(this.id, this.default, options?.context ?? null);
17512
17487
  if (typeof value === "number") {
17513
17488
  return value;
17514
17489
  }
@@ -17517,7 +17492,7 @@ var NumberFlag = class extends Flag {
17517
17492
  };
17518
17493
  var JsonFlag = class extends Flag {
17519
17494
  get(options) {
17520
- const value = this._client._evaluateHandle(this.key, this.default, options?.context ?? null);
17495
+ const value = this._client._evaluateHandle(this.id, this.default, options?.context ?? null);
17521
17496
  if (typeof value === "object" && value !== null && !Array.isArray(value)) {
17522
17497
  return value;
17523
17498
  }
@@ -17600,10 +17575,10 @@ function evaluateFlag(flagDef, environment, evalDict) {
17600
17575
  return fallback;
17601
17576
  }
17602
17577
  var FlagChangeEvent = class {
17603
- key;
17578
+ id;
17604
17579
  source;
17605
- constructor(key, source) {
17606
- this.key = key;
17580
+ constructor(id, source) {
17581
+ this.id = id;
17607
17582
  this.source = source;
17608
17583
  }
17609
17584
  };
@@ -17745,11 +17720,10 @@ var FlagsClient = class {
17745
17720
  // Management: factory methods (return unsaved flags)
17746
17721
  // ------------------------------------------------------------------
17747
17722
  /** Create an unsaved boolean flag. Call `.save()` to persist. */
17748
- newBooleanFlag(key, options) {
17723
+ newBooleanFlag(id, options) {
17749
17724
  return new BooleanFlag(this, {
17750
- id: null,
17751
- key,
17752
- name: options.name ?? keyToDisplayName(key),
17725
+ id,
17726
+ name: options.name ?? keyToDisplayName(id),
17753
17727
  type: "BOOLEAN",
17754
17728
  default: options.default,
17755
17729
  values: [
@@ -17763,11 +17737,10 @@ var FlagsClient = class {
17763
17737
  });
17764
17738
  }
17765
17739
  /** Create an unsaved string flag. Call `.save()` to persist. */
17766
- newStringFlag(key, options) {
17740
+ newStringFlag(id, options) {
17767
17741
  return new StringFlag(this, {
17768
- id: null,
17769
- key,
17770
- name: options.name ?? keyToDisplayName(key),
17742
+ id,
17743
+ name: options.name ?? keyToDisplayName(id),
17771
17744
  type: "STRING",
17772
17745
  default: options.default,
17773
17746
  values: options.values ?? null,
@@ -17778,11 +17751,10 @@ var FlagsClient = class {
17778
17751
  });
17779
17752
  }
17780
17753
  /** Create an unsaved number flag. Call `.save()` to persist. */
17781
- newNumberFlag(key, options) {
17754
+ newNumberFlag(id, options) {
17782
17755
  return new NumberFlag(this, {
17783
- id: null,
17784
- key,
17785
- name: options.name ?? keyToDisplayName(key),
17756
+ id,
17757
+ name: options.name ?? keyToDisplayName(id),
17786
17758
  type: "NUMERIC",
17787
17759
  default: options.default,
17788
17760
  values: options.values ?? null,
@@ -17793,11 +17765,10 @@ var FlagsClient = class {
17793
17765
  });
17794
17766
  }
17795
17767
  /** Create an unsaved JSON flag. Call `.save()` to persist. */
17796
- newJsonFlag(key, options) {
17768
+ newJsonFlag(id, options) {
17797
17769
  return new JsonFlag(this, {
17798
- id: null,
17799
- key,
17800
- name: options.name ?? keyToDisplayName(key),
17770
+ id,
17771
+ name: options.name ?? keyToDisplayName(id),
17801
17772
  type: "JSON",
17802
17773
  default: options.default,
17803
17774
  values: options.values ?? null,
@@ -17810,23 +17781,23 @@ var FlagsClient = class {
17810
17781
  // ------------------------------------------------------------------
17811
17782
  // Management: CRUD
17812
17783
  // ------------------------------------------------------------------
17813
- /** Fetch a flag by key. */
17814
- async get(key) {
17784
+ /** Fetch a flag by id. */
17785
+ async get(id) {
17815
17786
  let data;
17816
17787
  try {
17817
- const result = await this._http.GET("/api/v1/flags", {
17818
- params: { query: { "filter[key]": key } }
17788
+ const result = await this._http.GET("/api/v1/flags/{id}", {
17789
+ params: { path: { id } }
17819
17790
  });
17820
17791
  if (result.error !== void 0)
17821
- await checkError2(result.response, `Flag with key '${key}' not found`);
17792
+ await checkError2(result.response, `Flag with id '${id}' not found`);
17822
17793
  data = result.data;
17823
17794
  } catch (err) {
17824
17795
  wrapFetchError2(err);
17825
17796
  }
17826
- if (!data || !data.data || data.data.length === 0) {
17827
- throw new SmplNotFoundError(`Flag with key '${key}' not found`);
17797
+ if (!data || !data.data) {
17798
+ throw new SmplNotFoundError(`Flag with id '${id}' not found`);
17828
17799
  }
17829
- return this._resourceToModel(data.data[0]);
17800
+ return this._resourceToModel(data.data);
17830
17801
  }
17831
17802
  /** List all flags. */
17832
17803
  async list() {
@@ -17841,15 +17812,14 @@ var FlagsClient = class {
17841
17812
  if (!data) return [];
17842
17813
  return data.data.map((r) => this._resourceToModel(r));
17843
17814
  }
17844
- /** Delete a flag by key. */
17845
- async delete(key) {
17846
- const flag = await this.get(key);
17815
+ /** Delete a flag by id. */
17816
+ async delete(id) {
17847
17817
  try {
17848
17818
  const result = await this._http.DELETE("/api/v1/flags/{id}", {
17849
- params: { path: { id: flag.id } }
17819
+ params: { path: { id } }
17850
17820
  });
17851
17821
  if (result.error !== void 0 && result.response.status !== 204)
17852
- await checkError2(result.response, `Failed to delete flag '${key}'`);
17822
+ await checkError2(result.response, `Failed to delete flag '${id}'`);
17853
17823
  } catch (err) {
17854
17824
  wrapFetchError2(err);
17855
17825
  }
@@ -17861,9 +17831,9 @@ var FlagsClient = class {
17861
17831
  async _createFlag(flag) {
17862
17832
  const body = {
17863
17833
  data: {
17834
+ id: flag.id,
17864
17835
  type: "flag",
17865
17836
  attributes: {
17866
- key: flag.key,
17867
17837
  name: flag.name,
17868
17838
  description: flag.description ?? "",
17869
17839
  type: flag.type,
@@ -17890,7 +17860,6 @@ var FlagsClient = class {
17890
17860
  data: {
17891
17861
  type: "flag",
17892
17862
  attributes: {
17893
- key: flag.key,
17894
17863
  name: flag.name,
17895
17864
  type: flag.type,
17896
17865
  default: flag.default,
@@ -17919,11 +17888,10 @@ var FlagsClient = class {
17919
17888
  // Runtime: typed flag handles
17920
17889
  // ------------------------------------------------------------------
17921
17890
  /** Declare a boolean flag handle for runtime evaluation. */
17922
- booleanFlag(key, defaultValue) {
17891
+ booleanFlag(id, defaultValue) {
17923
17892
  const handle = new BooleanFlag(this, {
17924
- id: null,
17925
- key,
17926
- name: key,
17893
+ id,
17894
+ name: id,
17927
17895
  type: "BOOLEAN",
17928
17896
  default: defaultValue,
17929
17897
  values: [],
@@ -17932,15 +17900,14 @@ var FlagsClient = class {
17932
17900
  createdAt: null,
17933
17901
  updatedAt: null
17934
17902
  });
17935
- this._handles[key] = handle;
17903
+ this._handles[id] = handle;
17936
17904
  return handle;
17937
17905
  }
17938
17906
  /** Declare a string flag handle for runtime evaluation. */
17939
- stringFlag(key, defaultValue) {
17907
+ stringFlag(id, defaultValue) {
17940
17908
  const handle = new StringFlag(this, {
17941
- id: null,
17942
- key,
17943
- name: key,
17909
+ id,
17910
+ name: id,
17944
17911
  type: "STRING",
17945
17912
  default: defaultValue,
17946
17913
  values: [],
@@ -17949,15 +17916,14 @@ var FlagsClient = class {
17949
17916
  createdAt: null,
17950
17917
  updatedAt: null
17951
17918
  });
17952
- this._handles[key] = handle;
17919
+ this._handles[id] = handle;
17953
17920
  return handle;
17954
17921
  }
17955
17922
  /** Declare a numeric flag handle for runtime evaluation. */
17956
- numberFlag(key, defaultValue) {
17923
+ numberFlag(id, defaultValue) {
17957
17924
  const handle = new NumberFlag(this, {
17958
- id: null,
17959
- key,
17960
- name: key,
17925
+ id,
17926
+ name: id,
17961
17927
  type: "NUMERIC",
17962
17928
  default: defaultValue,
17963
17929
  values: [],
@@ -17966,15 +17932,14 @@ var FlagsClient = class {
17966
17932
  createdAt: null,
17967
17933
  updatedAt: null
17968
17934
  });
17969
- this._handles[key] = handle;
17935
+ this._handles[id] = handle;
17970
17936
  return handle;
17971
17937
  }
17972
17938
  /** Declare a JSON flag handle for runtime evaluation. */
17973
- jsonFlag(key, defaultValue) {
17939
+ jsonFlag(id, defaultValue) {
17974
17940
  const handle = new JsonFlag(this, {
17975
- id: null,
17976
- key,
17977
- name: key,
17941
+ id,
17942
+ name: id,
17978
17943
  type: "JSON",
17979
17944
  default: defaultValue,
17980
17945
  values: [],
@@ -17983,7 +17948,7 @@ var FlagsClient = class {
17983
17948
  createdAt: null,
17984
17949
  updatedAt: null
17985
17950
  });
17986
- this._handles[key] = handle;
17951
+ this._handles[id] = handle;
17987
17952
  return handle;
17988
17953
  }
17989
17954
  // ------------------------------------------------------------------
@@ -18060,20 +18025,20 @@ var FlagsClient = class {
18060
18025
  * Register a change listener.
18061
18026
  *
18062
18027
  * - `onChange(callback)` — fires for any flag change.
18063
- * - `onChange(key, callback)` — fires only for the specified flag key.
18028
+ * - `onChange(id, callback)` — fires only for the specified flag id.
18064
18029
  */
18065
- onChange(callbackOrKey, callback) {
18066
- if (typeof callbackOrKey === "function") {
18067
- this._globalListeners.push(callbackOrKey);
18030
+ onChange(callbackOrId, callback) {
18031
+ if (typeof callbackOrId === "function") {
18032
+ this._globalListeners.push(callbackOrId);
18068
18033
  } else {
18069
- const key = callbackOrKey;
18034
+ const id = callbackOrId;
18070
18035
  if (!callback) {
18071
- throw new SmplError("onChange(key, callback) requires a callback function.");
18036
+ throw new SmplError("onChange(id, callback) requires a callback function.");
18072
18037
  }
18073
- if (!this._keyListeners.has(key)) {
18074
- this._keyListeners.set(key, []);
18038
+ if (!this._keyListeners.has(id)) {
18039
+ this._keyListeners.set(id, []);
18075
18040
  }
18076
- this._keyListeners.get(key).push(callback);
18041
+ this._keyListeners.get(id).push(callback);
18077
18042
  }
18078
18043
  }
18079
18044
  // ------------------------------------------------------------------
@@ -18101,18 +18066,18 @@ var FlagsClient = class {
18101
18066
  /**
18102
18067
  * Evaluate a flag with an explicit environment and context.
18103
18068
  */
18104
- async evaluate(key, options) {
18069
+ async evaluate(id, options) {
18105
18070
  const evalDict = contextsToEvalDict(options.context);
18106
18071
  if (this._parent?._service && !("service" in evalDict)) {
18107
18072
  evalDict["service"] = { key: this._parent._service };
18108
18073
  }
18109
18074
  let flagDef = null;
18110
- if (this._initialized && key in this._flagStore) {
18111
- flagDef = this._flagStore[key];
18075
+ if (this._initialized && id in this._flagStore) {
18076
+ flagDef = this._flagStore[id];
18112
18077
  } else {
18113
18078
  const flags = await this._fetchFlagsList();
18114
18079
  for (const f of flags) {
18115
- if (f.key === key) {
18080
+ if (f.id === id) {
18116
18081
  flagDef = f;
18117
18082
  break;
18118
18083
  }
@@ -18151,8 +18116,18 @@ var FlagsClient = class {
18151
18116
  const cacheKey = `${key}:${ctxHash}`;
18152
18117
  const [hit, cachedValue] = this._cache.get(cacheKey);
18153
18118
  if (hit) {
18119
+ const metrics2 = this._parent?._metrics;
18120
+ if (metrics2) {
18121
+ metrics2.record("flags.cache_hits", 1, "hits");
18122
+ metrics2.record("flags.evaluations", 1, "evaluations", { flag_id: key });
18123
+ }
18154
18124
  return cachedValue;
18155
18125
  }
18126
+ const metrics = this._parent?._metrics;
18127
+ if (metrics) {
18128
+ metrics.record("flags.cache_misses", 1, "misses");
18129
+ metrics.record("flags.evaluations", 1, "evaluations", { flag_id: key });
18130
+ }
18156
18131
  const flagDef = this._flagStore[key];
18157
18132
  if (flagDef === void 0) {
18158
18133
  this._cache.put(cacheKey, defaultValue);
@@ -18182,17 +18157,17 @@ var FlagsClient = class {
18182
18157
  // Internal: event handlers (called by SharedWebSocket)
18183
18158
  // ------------------------------------------------------------------
18184
18159
  _handleFlagChanged = (data) => {
18185
- const flagKey = data.key;
18160
+ const flagId = data.id;
18186
18161
  void this._fetchAllFlags().then(() => {
18187
18162
  this._cache.clear();
18188
- this._fireChangeListeners(flagKey ?? null, "websocket");
18163
+ this._fireChangeListeners(flagId ?? null, "websocket");
18189
18164
  });
18190
18165
  };
18191
18166
  _handleFlagDeleted = (data) => {
18192
- const flagKey = data.key;
18167
+ const flagId = data.id;
18193
18168
  void this._fetchAllFlags().then(() => {
18194
18169
  this._cache.clear();
18195
- this._fireChangeListeners(flagKey ?? null, "websocket");
18170
+ this._fireChangeListeners(flagId ?? null, "websocket");
18196
18171
  });
18197
18172
  };
18198
18173
  // ------------------------------------------------------------------
@@ -18202,7 +18177,7 @@ var FlagsClient = class {
18202
18177
  const flags = await this._fetchFlagsList();
18203
18178
  const store = {};
18204
18179
  for (const f of flags) {
18205
- store[f.key] = f;
18180
+ store[f.id] = f;
18206
18181
  }
18207
18182
  this._flagStore = store;
18208
18183
  }
@@ -18221,18 +18196,18 @@ var FlagsClient = class {
18221
18196
  // ------------------------------------------------------------------
18222
18197
  // Internal: change listeners
18223
18198
  // ------------------------------------------------------------------
18224
- _fireChangeListeners(flagKey, source) {
18225
- if (flagKey) {
18226
- const event = new FlagChangeEvent(flagKey, source);
18199
+ _fireChangeListeners(flagId, source) {
18200
+ if (flagId) {
18201
+ const event = new FlagChangeEvent(flagId, source);
18227
18202
  for (const cb of this._globalListeners) {
18228
18203
  try {
18229
18204
  cb(event);
18230
18205
  } catch {
18231
18206
  }
18232
18207
  }
18233
- const keyCallbacks = this._keyListeners.get(flagKey);
18234
- if (keyCallbacks) {
18235
- for (const cb of keyCallbacks) {
18208
+ const idCallbacks = this._keyListeners.get(flagId);
18209
+ if (idCallbacks) {
18210
+ for (const cb of idCallbacks) {
18236
18211
  try {
18237
18212
  cb(event);
18238
18213
  } catch {
@@ -18242,8 +18217,8 @@ var FlagsClient = class {
18242
18217
  }
18243
18218
  }
18244
18219
  _fireChangeListenersAll(source) {
18245
- for (const flagKey of Object.keys(this._flagStore)) {
18246
- this._fireChangeListeners(flagKey, source);
18220
+ for (const flagId of Object.keys(this._flagStore)) {
18221
+ this._fireChangeListeners(flagId, source);
18247
18222
  }
18248
18223
  }
18249
18224
  // ------------------------------------------------------------------
@@ -18273,7 +18248,6 @@ var FlagsClient = class {
18273
18248
  const attrs = resource.attributes;
18274
18249
  return new Flag(this, {
18275
18250
  id: resource.id ?? null,
18276
- key: attrs.key,
18277
18251
  name: attrs.name,
18278
18252
  type: attrs.type,
18279
18253
  default: attrs.default,
@@ -18287,7 +18261,7 @@ var FlagsClient = class {
18287
18261
  _resourceToPlainDict(resource) {
18288
18262
  const attrs = resource.attributes;
18289
18263
  return {
18290
- key: attrs.key,
18264
+ id: resource.id ?? null,
18291
18265
  name: attrs.name,
18292
18266
  type: attrs.type,
18293
18267
  default: attrs.default,
@@ -18303,15 +18277,13 @@ import createClient3 from "openapi-fetch";
18303
18277
 
18304
18278
  // src/logging/models.ts
18305
18279
  var Logger = class {
18306
- /** UUID of the logger, or `null` if unsaved. */
18280
+ /** Unique identifier (dot-separated hierarchy, e.g. `"sqlalchemy.engine"`). */
18307
18281
  id;
18308
- /** Unique key (dot-separated hierarchy). */
18309
- key;
18310
18282
  /** Human-readable display name. */
18311
18283
  name;
18312
18284
  /** Base log level, or null if inherited. */
18313
18285
  level;
18314
- /** UUID of the parent log group, or null. */
18286
+ /** Id of the parent log group, or null. */
18315
18287
  group;
18316
18288
  /** Whether this logger is managed by the platform. */
18317
18289
  managed;
@@ -18329,7 +18301,6 @@ var Logger = class {
18329
18301
  constructor(client, fields) {
18330
18302
  this._client = client;
18331
18303
  this.id = fields.id;
18332
- this.key = fields.key;
18333
18304
  this.name = fields.name;
18334
18305
  this.level = fields.level;
18335
18306
  this.group = fields.group;
@@ -18379,7 +18350,6 @@ var Logger = class {
18379
18350
  /** @internal — copy all fields from another Logger instance. */
18380
18351
  _apply(other) {
18381
18352
  this.id = other.id;
18382
- this.key = other.key;
18383
18353
  this.name = other.name;
18384
18354
  this.level = other.level;
18385
18355
  this.group = other.group;
@@ -18390,19 +18360,17 @@ var Logger = class {
18390
18360
  this.updatedAt = other.updatedAt;
18391
18361
  }
18392
18362
  toString() {
18393
- return `Logger(key=${this.key}, level=${this.level})`;
18363
+ return `Logger(id=${this.id}, level=${this.level})`;
18394
18364
  }
18395
18365
  };
18396
18366
  var LogGroup = class {
18397
- /** UUID of the log group, or `null` if unsaved. */
18367
+ /** Unique identifier (slug), or `null` if unsaved. */
18398
18368
  id;
18399
- /** Unique key. */
18400
- key;
18401
18369
  /** Human-readable display name. */
18402
18370
  name;
18403
18371
  /** Base log level, or null if inherited. */
18404
18372
  level;
18405
- /** UUID of the parent log group, or null. */
18373
+ /** Id of the parent log group, or null. */
18406
18374
  group;
18407
18375
  /** Per-environment level overrides. */
18408
18376
  environments;
@@ -18416,7 +18384,6 @@ var LogGroup = class {
18416
18384
  constructor(client, fields) {
18417
18385
  this._client = client;
18418
18386
  this.id = fields.id;
18419
- this.key = fields.key;
18420
18387
  this.name = fields.name;
18421
18388
  this.level = fields.level;
18422
18389
  this.group = fields.group;
@@ -18464,7 +18431,6 @@ var LogGroup = class {
18464
18431
  /** @internal — copy all fields from another LogGroup instance. */
18465
18432
  _apply(other) {
18466
18433
  this.id = other.id;
18467
- this.key = other.key;
18468
18434
  this.name = other.name;
18469
18435
  this.level = other.level;
18470
18436
  this.group = other.group;
@@ -18473,7 +18439,7 @@ var LogGroup = class {
18473
18439
  this.updatedAt = other.updatedAt;
18474
18440
  }
18475
18441
  toString() {
18476
- return `LogGroup(key=${this.key}, level=${this.level})`;
18442
+ return `LogGroup(id=${this.id}, level=${this.level})`;
18477
18443
  }
18478
18444
  };
18479
18445
 
@@ -18557,11 +18523,10 @@ var LoggingClient = class {
18557
18523
  // Management: Logger factory
18558
18524
  // ------------------------------------------------------------------
18559
18525
  /** Create an unsaved logger. Call `.save()` to persist. */
18560
- new(key, options) {
18526
+ new(id, options) {
18561
18527
  return new Logger(this, {
18562
- id: null,
18563
- key,
18564
- name: options?.name ?? keyToDisplayName(key),
18528
+ id,
18529
+ name: options?.name ?? keyToDisplayName(id),
18565
18530
  level: null,
18566
18531
  group: null,
18567
18532
  managed: options?.managed ?? false,
@@ -18574,23 +18539,23 @@ var LoggingClient = class {
18574
18539
  // ------------------------------------------------------------------
18575
18540
  // Management: Logger CRUD
18576
18541
  // ------------------------------------------------------------------
18577
- /** Fetch a logger by key. */
18578
- async get(key) {
18542
+ /** Fetch a logger by id. */
18543
+ async get(id) {
18579
18544
  let data;
18580
18545
  try {
18581
- const result = await this._http.GET("/api/v1/loggers", {
18582
- params: { query: { "filter[key]": key } }
18546
+ const result = await this._http.GET("/api/v1/loggers/{id}", {
18547
+ params: { path: { id } }
18583
18548
  });
18584
18549
  if (result.error !== void 0)
18585
- await checkError3(result.response, `Logger with key '${key}' not found`);
18550
+ await checkError3(result.response, `Logger with id '${id}' not found`);
18586
18551
  data = result.data;
18587
18552
  } catch (err) {
18588
18553
  wrapFetchError3(err);
18589
18554
  }
18590
- if (!data || !data.data || data.data.length === 0) {
18591
- throw new SmplNotFoundError(`Logger with key '${key}' not found`);
18555
+ if (!data || !data.data) {
18556
+ throw new SmplNotFoundError(`Logger with id '${id}' not found`);
18592
18557
  }
18593
- return this._loggerToModel(data.data[0]);
18558
+ return this._loggerToModel(data.data);
18594
18559
  }
18595
18560
  /** List all loggers. */
18596
18561
  async list() {
@@ -18605,15 +18570,14 @@ var LoggingClient = class {
18605
18570
  if (!data) return [];
18606
18571
  return data.data.map((r) => this._loggerToModel(r));
18607
18572
  }
18608
- /** Delete a logger by key. */
18609
- async delete(key) {
18610
- const logger = await this.get(key);
18573
+ /** Delete a logger by id. */
18574
+ async delete(id) {
18611
18575
  try {
18612
18576
  const result = await this._http.DELETE("/api/v1/loggers/{id}", {
18613
- params: { path: { id: logger.id } }
18577
+ params: { path: { id } }
18614
18578
  });
18615
18579
  if (result.error !== void 0 && result.response.status !== 204)
18616
- await checkError3(result.response, `Failed to delete logger '${key}'`);
18580
+ await checkError3(result.response, `Failed to delete logger '${id}'`);
18617
18581
  } catch (err) {
18618
18582
  wrapFetchError3(err);
18619
18583
  }
@@ -18622,11 +18586,10 @@ var LoggingClient = class {
18622
18586
  // Management: LogGroup factory
18623
18587
  // ------------------------------------------------------------------
18624
18588
  /** Create an unsaved log group. Call `.save()` to persist. */
18625
- newGroup(key, options) {
18589
+ newGroup(id, options) {
18626
18590
  return new LogGroup(this, {
18627
- id: null,
18628
- key,
18629
- name: options?.name ?? keyToDisplayName(key),
18591
+ id,
18592
+ name: options?.name ?? keyToDisplayName(id),
18630
18593
  level: null,
18631
18594
  group: options?.group ?? null,
18632
18595
  environments: {},
@@ -18637,12 +18600,12 @@ var LoggingClient = class {
18637
18600
  // ------------------------------------------------------------------
18638
18601
  // Management: LogGroup CRUD
18639
18602
  // ------------------------------------------------------------------
18640
- /** Fetch a log group by key. */
18641
- async getGroup(key) {
18603
+ /** Fetch a log group by id. */
18604
+ async getGroup(id) {
18642
18605
  const groups = await this.listGroups();
18643
- const match = groups.find((g) => g.key === key);
18606
+ const match = groups.find((g) => g.id === id);
18644
18607
  if (!match) {
18645
- throw new SmplNotFoundError(`LogGroup with key '${key}' not found`);
18608
+ throw new SmplNotFoundError(`LogGroup with id '${id}' not found`);
18646
18609
  }
18647
18610
  return match;
18648
18611
  }
@@ -18660,15 +18623,15 @@ var LoggingClient = class {
18660
18623
  if (!data) return [];
18661
18624
  return data.data.map((r) => this._groupToModel(r));
18662
18625
  }
18663
- /** Delete a log group by key. */
18664
- async deleteGroup(key) {
18665
- const group = await this.getGroup(key);
18626
+ /** Delete a log group by id. */
18627
+ async deleteGroup(id) {
18628
+ const group = await this.getGroup(id);
18666
18629
  try {
18667
18630
  const result = await this._http.DELETE("/api/v1/log_groups/{id}", {
18668
18631
  params: { path: { id: group.id } }
18669
18632
  });
18670
18633
  if (result.error !== void 0 && result.response.status !== 204)
18671
- await checkError3(result.response, `Failed to delete log group '${key}'`);
18634
+ await checkError3(result.response, `Failed to delete log group '${id}'`);
18672
18635
  } catch (err) {
18673
18636
  wrapFetchError3(err);
18674
18637
  }
@@ -18680,9 +18643,9 @@ var LoggingClient = class {
18680
18643
  async _saveLogger(logger) {
18681
18644
  const body = {
18682
18645
  data: {
18646
+ id: logger.id,
18683
18647
  type: "logger",
18684
18648
  attributes: {
18685
- key: logger.key,
18686
18649
  name: logger.name,
18687
18650
  level: logger.level,
18688
18651
  group: logger.group,
@@ -18691,7 +18654,7 @@ var LoggingClient = class {
18691
18654
  }
18692
18655
  }
18693
18656
  };
18694
- if (logger.id === null) {
18657
+ if (logger.createdAt === null) {
18695
18658
  let data;
18696
18659
  try {
18697
18660
  const result = await this._http.POST("/api/v1/loggers", { body });
@@ -18725,9 +18688,9 @@ var LoggingClient = class {
18725
18688
  async _saveLogGroup(group) {
18726
18689
  const body = {
18727
18690
  data: {
18691
+ id: group.id,
18728
18692
  type: "log_group",
18729
18693
  attributes: {
18730
- key: group.key,
18731
18694
  name: group.name,
18732
18695
  level: group.level,
18733
18696
  group: group.group,
@@ -18735,7 +18698,7 @@ var LoggingClient = class {
18735
18698
  }
18736
18699
  }
18737
18700
  };
18738
- if (group.id === null) {
18701
+ if (group.createdAt === null) {
18739
18702
  let data;
18740
18703
  try {
18741
18704
  const result = await this._http.POST("/api/v1/log_groups", { body });
@@ -18796,6 +18759,12 @@ var LoggingClient = class {
18796
18759
  } catch {
18797
18760
  }
18798
18761
  }
18762
+ if (discovered.length > 0) {
18763
+ const metrics = this._parent?._metrics;
18764
+ if (metrics) {
18765
+ metrics.record("logging.loggers_discovered", discovered.length, "loggers");
18766
+ }
18767
+ }
18799
18768
  for (const { name, level } of discovered) {
18800
18769
  try {
18801
18770
  const logger = this.new(name, { managed: true });
@@ -18820,20 +18789,20 @@ var LoggingClient = class {
18820
18789
  * Register a change listener.
18821
18790
  *
18822
18791
  * - `onChange(callback)` — fires for any logger change.
18823
- * - `onChange(key, callback)` — fires only for the specified logger key.
18792
+ * - `onChange(id, callback)` — fires only for the specified logger id.
18824
18793
  */
18825
- onChange(callbackOrKey, callback) {
18826
- if (typeof callbackOrKey === "function") {
18827
- this._globalListeners.push(callbackOrKey);
18794
+ onChange(callbackOrId, callback) {
18795
+ if (typeof callbackOrId === "function") {
18796
+ this._globalListeners.push(callbackOrId);
18828
18797
  } else {
18829
- const key = callbackOrKey;
18798
+ const id = callbackOrId;
18830
18799
  if (!callback) {
18831
- throw new SmplError("onChange(key, callback) requires a callback function.");
18800
+ throw new SmplError("onChange(id, callback) requires a callback function.");
18832
18801
  }
18833
- if (!this._keyListeners.has(key)) {
18834
- this._keyListeners.set(key, []);
18802
+ if (!this._keyListeners.has(id)) {
18803
+ this._keyListeners.set(id, []);
18835
18804
  }
18836
- this._keyListeners.get(key).push(callback);
18805
+ this._keyListeners.get(id).push(callback);
18837
18806
  }
18838
18807
  }
18839
18808
  // ------------------------------------------------------------------
@@ -18890,9 +18859,13 @@ var LoggingClient = class {
18890
18859
  effectiveLevel = envOverride.level;
18891
18860
  }
18892
18861
  }
18862
+ const metrics = this._parent?._metrics;
18863
+ if (metrics) {
18864
+ metrics.record("logging.level_changes", 1, "changes", { logger_id: logger.id });
18865
+ }
18893
18866
  for (const adapter of this._adapters) {
18894
18867
  try {
18895
- adapter.applyLevel(logger.key, effectiveLevel);
18868
+ adapter.applyLevel(logger.id, effectiveLevel);
18896
18869
  } catch {
18897
18870
  }
18898
18871
  }
@@ -18909,11 +18882,11 @@ var LoggingClient = class {
18909
18882
  // Internal: WebSocket handler
18910
18883
  // ------------------------------------------------------------------
18911
18884
  _handleLoggerChanged = (data) => {
18912
- const key = data.key;
18913
- if (key) {
18885
+ const id = data.id;
18886
+ if (id) {
18914
18887
  const level = data.level ?? null;
18915
18888
  const event = {
18916
- key,
18889
+ id,
18917
18890
  level,
18918
18891
  source: "websocket"
18919
18892
  };
@@ -18923,9 +18896,9 @@ var LoggingClient = class {
18923
18896
  } catch {
18924
18897
  }
18925
18898
  }
18926
- const keyCallbacks = this._keyListeners.get(key);
18927
- if (keyCallbacks) {
18928
- for (const cb of keyCallbacks) {
18899
+ const idCallbacks = this._keyListeners.get(id);
18900
+ if (idCallbacks) {
18901
+ for (const cb of idCallbacks) {
18929
18902
  try {
18930
18903
  cb(event);
18931
18904
  } catch {
@@ -18941,7 +18914,6 @@ var LoggingClient = class {
18941
18914
  const attrs = resource.attributes;
18942
18915
  return new Logger(this, {
18943
18916
  id: resource.id ?? null,
18944
- key: attrs.key ?? "",
18945
18917
  name: attrs.name,
18946
18918
  level: attrs.level ?? null,
18947
18919
  group: attrs.group ?? null,
@@ -18956,7 +18928,6 @@ var LoggingClient = class {
18956
18928
  const attrs = resource.attributes;
18957
18929
  return new LogGroup(this, {
18958
18930
  id: resource.id ?? null,
18959
- key: attrs.key ?? "",
18960
18931
  name: attrs.name,
18961
18932
  level: attrs.level ?? null,
18962
18933
  group: attrs.group ?? null,
@@ -18973,15 +18944,17 @@ var BACKOFF_MS = [1e3, 2e3, 4e3, 8e3, 16e3, 32e3, 6e4];
18973
18944
  var SharedWebSocket = class {
18974
18945
  _appBaseUrl;
18975
18946
  _apiKey;
18947
+ _metrics;
18976
18948
  _listeners = /* @__PURE__ */ new Map();
18977
18949
  _connectionStatus = "disconnected";
18978
18950
  _closed = false;
18979
18951
  _ws = null;
18980
18952
  _reconnectTimer = null;
18981
18953
  _backoffIndex = 0;
18982
- constructor(appBaseUrl, apiKey) {
18954
+ constructor(appBaseUrl, apiKey, metrics) {
18983
18955
  this._appBaseUrl = appBaseUrl;
18984
18956
  this._apiKey = apiKey;
18957
+ this._metrics = metrics ?? null;
18985
18958
  }
18986
18959
  // ------------------------------------------------------------------
18987
18960
  // Listener registration
@@ -19080,6 +19053,9 @@ var SharedWebSocket = class {
19080
19053
  if (msg.type === "connected") {
19081
19054
  this._backoffIndex = 0;
19082
19055
  this._connectionStatus = "connected";
19056
+ if (this._metrics) {
19057
+ this._metrics.recordGauge("platform.websocket_connections", 1, "connections");
19058
+ }
19083
19059
  return;
19084
19060
  }
19085
19061
  if (msg.type === "error") {
@@ -19093,6 +19069,9 @@ var SharedWebSocket = class {
19093
19069
  }
19094
19070
  });
19095
19071
  ws.on("close", () => {
19072
+ if (this._metrics) {
19073
+ this._metrics.recordGauge("platform.websocket_connections", 0, "connections");
19074
+ }
19096
19075
  if (!this._closed) {
19097
19076
  this._connectionStatus = "disconnected";
19098
19077
  this._scheduleReconnect();
@@ -19173,8 +19152,145 @@ function resolveApiKey(explicit, environment) {
19173
19152
  throw new SmplError(noApiKeyMessage(environment));
19174
19153
  }
19175
19154
 
19176
- // src/client.ts
19155
+ // src/_metrics.ts
19177
19156
  var APP_BASE_URL2 = "https://app.smplkit.com";
19157
+ function makeCounter(unit) {
19158
+ return { value: 0, unit, windowStart: (/* @__PURE__ */ new Date()).toISOString() };
19159
+ }
19160
+ function makeMapKey(name, dimensions) {
19161
+ const sorted = Object.keys(dimensions).sort().map((k) => `${k}=${dimensions[k]}`).join("&");
19162
+ return `${name}|${sorted}`;
19163
+ }
19164
+ var MetricsReporter = class {
19165
+ _apiKey;
19166
+ _environment;
19167
+ _service;
19168
+ _flushInterval;
19169
+ _counters = /* @__PURE__ */ new Map();
19170
+ _gauges = /* @__PURE__ */ new Map();
19171
+ _timer = null;
19172
+ _closed = false;
19173
+ constructor(options) {
19174
+ this._apiKey = options.apiKey;
19175
+ this._environment = options.environment;
19176
+ this._service = options.service;
19177
+ this._flushInterval = options.flushInterval ?? 60;
19178
+ }
19179
+ // ------------------------------------------------------------------
19180
+ // Public recording API
19181
+ // ------------------------------------------------------------------
19182
+ record(name, value = 1, unit = null, dimensions) {
19183
+ const merged = this._mergeDimensions(dimensions);
19184
+ const key = makeMapKey(name, merged);
19185
+ let entry = this._counters.get(key);
19186
+ if (!entry) {
19187
+ entry = { name, dimensions: merged, counter: makeCounter(unit) };
19188
+ this._counters.set(key, entry);
19189
+ }
19190
+ entry.counter.value += value;
19191
+ if (entry.counter.unit === null && unit !== null) {
19192
+ entry.counter.unit = unit;
19193
+ }
19194
+ this._maybeStartTimer();
19195
+ }
19196
+ recordGauge(name, value, unit = null, dimensions) {
19197
+ const merged = this._mergeDimensions(dimensions);
19198
+ const key = makeMapKey(name, merged);
19199
+ let entry = this._gauges.get(key);
19200
+ if (!entry) {
19201
+ entry = { name, dimensions: merged, counter: makeCounter(unit) };
19202
+ this._gauges.set(key, entry);
19203
+ }
19204
+ entry.counter.value = value;
19205
+ if (entry.counter.unit === null && unit !== null) {
19206
+ entry.counter.unit = unit;
19207
+ }
19208
+ this._maybeStartTimer();
19209
+ }
19210
+ // ------------------------------------------------------------------
19211
+ // Flush / close
19212
+ // ------------------------------------------------------------------
19213
+ flush() {
19214
+ this._flush();
19215
+ }
19216
+ close() {
19217
+ if (this._closed) return;
19218
+ this._closed = true;
19219
+ if (this._timer !== null) {
19220
+ clearInterval(this._timer);
19221
+ this._timer = null;
19222
+ }
19223
+ this._flush();
19224
+ }
19225
+ // ------------------------------------------------------------------
19226
+ // Internal
19227
+ // ------------------------------------------------------------------
19228
+ _mergeDimensions(dimensions) {
19229
+ const merged = {
19230
+ environment: this._environment,
19231
+ service: this._service
19232
+ };
19233
+ if (dimensions) {
19234
+ Object.assign(merged, dimensions);
19235
+ }
19236
+ return merged;
19237
+ }
19238
+ _maybeStartTimer() {
19239
+ if (this._timer === null && !this._closed) {
19240
+ this._timer = setInterval(() => this._flush(), this._flushInterval * 1e3);
19241
+ if (typeof this._timer === "object" && "unref" in this._timer) {
19242
+ this._timer.unref();
19243
+ }
19244
+ }
19245
+ }
19246
+ _flush() {
19247
+ const counters = this._counters;
19248
+ const gauges = this._gauges;
19249
+ this._counters = /* @__PURE__ */ new Map();
19250
+ this._gauges = /* @__PURE__ */ new Map();
19251
+ if (counters.size === 0 && gauges.size === 0) return;
19252
+ const payload = this._buildPayload(counters, gauges);
19253
+ try {
19254
+ fetch(`${APP_BASE_URL2}/api/v1/metrics/bulk`, {
19255
+ method: "POST",
19256
+ headers: {
19257
+ Authorization: `Bearer ${this._apiKey}`,
19258
+ "Content-Type": "application/vnd.api+json",
19259
+ Accept: "application/json"
19260
+ },
19261
+ body: JSON.stringify(payload)
19262
+ }).catch(() => {
19263
+ });
19264
+ } catch {
19265
+ }
19266
+ }
19267
+ _buildPayload(counters, gauges) {
19268
+ const data = [];
19269
+ for (const [, entry] of counters) {
19270
+ data.push(this._entry(entry.name, entry.counter, entry.dimensions));
19271
+ }
19272
+ for (const [, entry] of gauges) {
19273
+ data.push(this._entry(entry.name, entry.counter, entry.dimensions));
19274
+ }
19275
+ return { data };
19276
+ }
19277
+ _entry(name, counter, dimensions) {
19278
+ return {
19279
+ type: "metric",
19280
+ attributes: {
19281
+ name,
19282
+ value: counter.value,
19283
+ unit: counter.unit,
19284
+ period_seconds: this._flushInterval,
19285
+ dimensions,
19286
+ recorded_at: counter.windowStart
19287
+ }
19288
+ };
19289
+ }
19290
+ };
19291
+
19292
+ // src/client.ts
19293
+ var APP_BASE_URL3 = "https://app.smplkit.com";
19178
19294
  var NO_ENVIRONMENT_MESSAGE = "No environment provided. Set one of:\n 1. Pass environment to the constructor\n 2. Set the SMPLKIT_ENVIRONMENT environment variable";
19179
19295
  var NO_SERVICE_MESSAGE = "No service provided. Set one of:\n 1. Pass service in options\n 2. Set the SMPLKIT_SERVICE environment variable";
19180
19296
  var SmplClient = class {
@@ -19190,6 +19306,8 @@ var SmplClient = class {
19190
19306
  _environment;
19191
19307
  /** @internal */
19192
19308
  _service;
19309
+ /** @internal */
19310
+ _metrics = null;
19193
19311
  _timeout;
19194
19312
  _appHttp;
19195
19313
  constructor(options = {}) {
@@ -19207,12 +19325,19 @@ var SmplClient = class {
19207
19325
  this._apiKey = apiKey;
19208
19326
  this._timeout = options.timeout ?? 3e4;
19209
19327
  this._appHttp = createClient4({
19210
- baseUrl: APP_BASE_URL2,
19328
+ baseUrl: APP_BASE_URL3,
19211
19329
  headers: {
19212
19330
  Authorization: `Bearer ${apiKey}`,
19213
19331
  Accept: "application/json"
19214
19332
  }
19215
19333
  });
19334
+ if (!options.disableTelemetry) {
19335
+ this._metrics = new MetricsReporter({
19336
+ apiKey,
19337
+ environment: this._environment,
19338
+ service: this._service
19339
+ });
19340
+ }
19216
19341
  this.config = new ConfigClient(apiKey, this._timeout);
19217
19342
  this.flags = new FlagsClient(apiKey, () => this._ensureWs(), this._timeout);
19218
19343
  this.logging = new LoggingClient(apiKey, () => this._ensureWs(), this._timeout);
@@ -19242,13 +19367,16 @@ var SmplClient = class {
19242
19367
  /** Lazily create and start the shared WebSocket. @internal */
19243
19368
  _ensureWs() {
19244
19369
  if (this._wsManager === null) {
19245
- this._wsManager = new SharedWebSocket(APP_BASE_URL2, this._apiKey);
19370
+ this._wsManager = new SharedWebSocket(APP_BASE_URL3, this._apiKey, this._metrics);
19246
19371
  this._wsManager.start();
19247
19372
  }
19248
19373
  return this._wsManager;
19249
19374
  }
19250
19375
  /** Close the shared WebSocket and release resources. */
19251
19376
  close() {
19377
+ if (this._metrics !== null) {
19378
+ this._metrics.close();
19379
+ }
19252
19380
  this.logging._close();
19253
19381
  if (this._wsManager !== null) {
19254
19382
  this._wsManager.stop();