@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.cjs CHANGED
@@ -16794,15 +16794,13 @@ function resolveChain(chain, environment) {
16794
16794
 
16795
16795
  // src/config/types.ts
16796
16796
  var Config = class {
16797
- /** UUID of the config, or `null` if unsaved. */
16797
+ /** Unique identifier (slug, e.g. `"user-service"`). */
16798
16798
  id;
16799
- /** Human-readable key (e.g. `"user-service"`). */
16800
- key;
16801
16799
  /** Display name. */
16802
16800
  name;
16803
16801
  /** Optional description. */
16804
16802
  description;
16805
- /** Parent config UUID, or null if this is a root config. */
16803
+ /** Parent config id (slug), or null if this is a root config. */
16806
16804
  parent;
16807
16805
  /** Base key-value pairs. */
16808
16806
  items;
@@ -16821,7 +16819,6 @@ var Config = class {
16821
16819
  constructor(client, fields) {
16822
16820
  this._client = client;
16823
16821
  this.id = fields.id;
16824
- this.key = fields.key;
16825
16822
  this.name = fields.name;
16826
16823
  this.description = fields.description;
16827
16824
  this.parent = fields.parent;
@@ -16837,7 +16834,7 @@ var Config = class {
16837
16834
  * Updates this instance in-place with the server response.
16838
16835
  */
16839
16836
  async save() {
16840
- if (this.id === null) {
16837
+ if (this.createdAt === null) {
16841
16838
  const created = await this._client._createConfig(this);
16842
16839
  this._apply(created);
16843
16840
  } else {
@@ -16871,7 +16868,6 @@ var Config = class {
16871
16868
  /** @internal — copy all fields from another Config instance. */
16872
16869
  _apply(other) {
16873
16870
  this.id = other.id;
16874
- this.key = other.key;
16875
16871
  this.name = other.name;
16876
16872
  this.description = other.description;
16877
16873
  this.parent = other.parent;
@@ -16881,7 +16877,7 @@ var Config = class {
16881
16877
  this.updatedAt = other.updatedAt;
16882
16878
  }
16883
16879
  toString() {
16884
- return `Config(id=${this.id}, key=${this.key}, name=${this.name})`;
16880
+ return `Config(id=${this.id}, name=${this.name})`;
16885
16881
  }
16886
16882
  };
16887
16883
 
@@ -16974,7 +16970,6 @@ function resourceToConfig(resource, client) {
16974
16970
  const attrs = resource.attributes;
16975
16971
  return new Config(client, {
16976
16972
  id: resource.id ?? null,
16977
- key: attrs.key ?? "",
16978
16973
  name: attrs.name,
16979
16974
  description: attrs.description ?? null,
16980
16975
  parent: attrs.parent ?? null,
@@ -17034,7 +17029,6 @@ function buildRequestBody(options) {
17034
17029
  const attrs = {
17035
17030
  name: options.name
17036
17031
  };
17037
- if (options.key !== void 0) attrs.key = options.key;
17038
17032
  if (options.description !== void 0) attrs.description = options.description;
17039
17033
  if (options.parent !== void 0) attrs.parent = options.parent;
17040
17034
  if (options.items !== void 0)
@@ -17093,11 +17087,10 @@ var ConfigClient = class {
17093
17087
  // Management: factory method
17094
17088
  // ------------------------------------------------------------------
17095
17089
  /** Create an unsaved config. Call `.save()` to persist. */
17096
- new(key, options) {
17090
+ new(id, options) {
17097
17091
  return new Config(this, {
17098
- id: null,
17099
- key,
17100
- name: options?.name ?? keyToDisplayName(key),
17092
+ id,
17093
+ name: options?.name ?? keyToDisplayName(id),
17101
17094
  description: options?.description ?? null,
17102
17095
  parent: options?.parent ?? null,
17103
17096
  items: {},
@@ -17109,9 +17102,9 @@ var ConfigClient = class {
17109
17102
  // ------------------------------------------------------------------
17110
17103
  // Management: CRUD
17111
17104
  // ------------------------------------------------------------------
17112
- /** Fetch a config by key. */
17113
- async get(key) {
17114
- return this._getByKey(key);
17105
+ /** Fetch a config by id. */
17106
+ async get(id) {
17107
+ return this._getById(id);
17115
17108
  }
17116
17109
  /** List all configs. */
17117
17110
  async list() {
@@ -17126,15 +17119,14 @@ var ConfigClient = class {
17126
17119
  if (!data) return [];
17127
17120
  return data.data.map((r) => resourceToConfig(r, this));
17128
17121
  }
17129
- /** Delete a config by key. */
17130
- async delete(key) {
17131
- const config = await this.get(key);
17122
+ /** Delete a config by id. */
17123
+ async delete(id) {
17132
17124
  try {
17133
17125
  const result = await this._http.DELETE("/api/v1/configs/{id}", {
17134
- params: { path: { id: config.id } }
17126
+ params: { path: { id } }
17135
17127
  });
17136
17128
  if (result.error !== void 0 && result.response.status !== 204)
17137
- await checkError(result.response, `Failed to delete config '${key}'`);
17129
+ await checkError(result.response, `Failed to delete config '${id}'`);
17138
17130
  } catch (err) {
17139
17131
  wrapFetchError(err);
17140
17132
  }
@@ -17145,8 +17137,8 @@ var ConfigClient = class {
17145
17137
  /** @internal — POST a new config. */
17146
17138
  async _createConfig(config) {
17147
17139
  const body = buildRequestBody({
17140
+ id: config.id,
17148
17141
  name: config.name,
17149
- key: config.key,
17150
17142
  description: config.description,
17151
17143
  parent: config.parent,
17152
17144
  items: config.items,
@@ -17168,7 +17160,6 @@ var ConfigClient = class {
17168
17160
  const body = buildRequestBody({
17169
17161
  id: config.id,
17170
17162
  name: config.name,
17171
- key: config.key,
17172
17163
  description: config.description,
17173
17164
  parent: config.parent,
17174
17165
  items: config.items,
@@ -17189,20 +17180,20 @@ var ConfigClient = class {
17189
17180
  if (!data || !data.data) throw new SmplValidationError(`Failed to update config ${config.id}`);
17190
17181
  return resourceToConfig(data.data, this);
17191
17182
  }
17192
- /** @internal — fetch a config by UUID. */
17193
- async _getById(configId) {
17183
+ /** @internal — fetch a config by id. */
17184
+ async _getById(id) {
17194
17185
  let data;
17195
17186
  try {
17196
17187
  const result = await this._http.GET("/api/v1/configs/{id}", {
17197
- params: { path: { id: configId } }
17188
+ params: { path: { id } }
17198
17189
  });
17199
17190
  if (result.error !== void 0)
17200
- await checkError(result.response, `Config ${configId} not found`);
17191
+ await checkError(result.response, `Config with id '${id}' not found`);
17201
17192
  data = result.data;
17202
17193
  } catch (err) {
17203
17194
  wrapFetchError(err);
17204
17195
  }
17205
- if (!data || !data.data) throw new SmplNotFoundError(`Config ${configId} not found`);
17196
+ if (!data || !data.data) throw new SmplNotFoundError(`Config with id '${id}' not found`);
17206
17197
  return resourceToConfig(data.data, this);
17207
17198
  }
17208
17199
  // ------------------------------------------------------------------
@@ -17214,11 +17205,15 @@ var ConfigClient = class {
17214
17205
  * Returns the resolved key-value pairs for the given config.
17215
17206
  * Optionally pass a model class to map the resolved values.
17216
17207
  */
17217
- async resolve(key, model) {
17208
+ async resolve(id, model) {
17218
17209
  await this._ensureInitialized();
17219
- const values = this._configCache[key];
17210
+ const values = this._configCache[id];
17220
17211
  if (values === void 0) {
17221
- throw new SmplNotFoundError(`Config with key '${key}' not found in cache`);
17212
+ throw new SmplNotFoundError(`Config with id '${id}' not found in cache`);
17213
+ }
17214
+ const metrics = this._parent?._metrics;
17215
+ if (metrics) {
17216
+ metrics.record("config.resolutions", 1, "resolutions", { config_id: id });
17222
17217
  }
17223
17218
  if (model) {
17224
17219
  return new model(values);
@@ -17231,12 +17226,12 @@ var ConfigClient = class {
17231
17226
  *
17232
17227
  * Optionally pass a model class to map the resolved values.
17233
17228
  */
17234
- async subscribe(key, model) {
17229
+ async subscribe(id, model) {
17235
17230
  await this._ensureInitialized();
17236
- if (!(key in this._configCache)) {
17237
- throw new SmplNotFoundError(`Config with key '${key}' not found in cache`);
17231
+ if (!(id in this._configCache)) {
17232
+ throw new SmplNotFoundError(`Config with id '${id}' not found in cache`);
17238
17233
  }
17239
- return new LiveConfigProxy(this, key, model);
17234
+ return new LiveConfigProxy(this, id, model);
17240
17235
  }
17241
17236
  // ------------------------------------------------------------------
17242
17237
  // Runtime: change listeners (3-level overloads)
@@ -17245,26 +17240,26 @@ var ConfigClient = class {
17245
17240
  * Register a change listener.
17246
17241
  *
17247
17242
  * - `onChange(callback)` — fires for any config change (global).
17248
- * - `onChange(configKey, callback)` — fires for changes to a specific config.
17249
- * - `onChange(configKey, itemKey, callback)` — fires for a specific item.
17243
+ * - `onChange(configId, callback)` — fires for changes to a specific config.
17244
+ * - `onChange(configId, itemKey, callback)` — fires for a specific item.
17250
17245
  */
17251
- onChange(callbackOrConfigKey, callbackOrItemKey, callback) {
17252
- if (typeof callbackOrConfigKey === "function") {
17246
+ onChange(callbackOrConfigId, callbackOrItemKey, callback) {
17247
+ if (typeof callbackOrConfigId === "function") {
17253
17248
  this._listeners.push({
17254
- callback: callbackOrConfigKey,
17255
- configKey: null,
17249
+ callback: callbackOrConfigId,
17250
+ configId: null,
17256
17251
  itemKey: null
17257
17252
  });
17258
17253
  } else if (typeof callbackOrItemKey === "function") {
17259
17254
  this._listeners.push({
17260
17255
  callback: callbackOrItemKey,
17261
- configKey: callbackOrConfigKey,
17256
+ configId: callbackOrConfigId,
17262
17257
  itemKey: null
17263
17258
  });
17264
17259
  } else if (typeof callbackOrItemKey === "string" && callback) {
17265
17260
  this._listeners.push({
17266
17261
  callback,
17267
- configKey: callbackOrConfigKey,
17262
+ configId: callbackOrConfigId,
17268
17263
  itemKey: callbackOrItemKey
17269
17264
  });
17270
17265
  }
@@ -17288,7 +17283,7 @@ var ConfigClient = class {
17288
17283
  const newCache = {};
17289
17284
  for (const cfg of configs) {
17290
17285
  const chain = await cfg._buildChain(configs);
17291
- newCache[cfg.key] = resolveChain(chain, environment);
17286
+ newCache[cfg.id] = resolveChain(chain, environment);
17292
17287
  }
17293
17288
  const oldCache = this._configCache;
17294
17289
  this._configCache = newCache;
@@ -17308,7 +17303,7 @@ var ConfigClient = class {
17308
17303
  const cache = {};
17309
17304
  for (const cfg of configs) {
17310
17305
  const chain = await cfg._buildChain(configs);
17311
- cache[cfg.key] = resolveChain(chain, environment);
17306
+ cache[cfg.id] = resolveChain(chain, environment);
17312
17307
  }
17313
17308
  this._configCache = cache;
17314
17309
  this._initialized = true;
@@ -17324,7 +17319,7 @@ var ConfigClient = class {
17324
17319
  const cache = {};
17325
17320
  for (const cfg of configs) {
17326
17321
  const chain = await cfg._buildChain(configs);
17327
- cache[cfg.key] = resolveChain(chain, environment);
17322
+ cache[cfg.id] = resolveChain(chain, environment);
17328
17323
  }
17329
17324
  this._configCache = cache;
17330
17325
  this._initialized = true;
@@ -17354,15 +17349,19 @@ var ConfigClient = class {
17354
17349
  const oldVal = iKey in oldItems ? oldItems[iKey] : null;
17355
17350
  const newVal = iKey in newItems ? newItems[iKey] : null;
17356
17351
  if (JSON.stringify(oldVal) !== JSON.stringify(newVal)) {
17352
+ const metrics = this._parent?._metrics;
17353
+ if (metrics) {
17354
+ metrics.record("config.changes", 1, "changes", { config_id: cfgKey });
17355
+ }
17357
17356
  const event = {
17358
- configKey: cfgKey,
17357
+ configId: cfgKey,
17359
17358
  itemKey: iKey,
17360
17359
  oldValue: oldVal,
17361
17360
  newValue: newVal,
17362
17361
  source
17363
17362
  };
17364
17363
  for (const listener of this._listeners) {
17365
- if (listener.configKey !== null && listener.configKey !== cfgKey) continue;
17364
+ if (listener.configId !== null && listener.configId !== cfgKey) continue;
17366
17365
  if (listener.itemKey !== null && listener.itemKey !== iKey) continue;
17367
17366
  try {
17368
17367
  listener.callback(event);
@@ -17373,26 +17372,6 @@ var ConfigClient = class {
17373
17372
  }
17374
17373
  }
17375
17374
  }
17376
- // ------------------------------------------------------------------
17377
- // Internal: fetch by key
17378
- // ------------------------------------------------------------------
17379
- async _getByKey(key) {
17380
- let data;
17381
- try {
17382
- const result = await this._http.GET("/api/v1/configs", {
17383
- params: { query: { "filter[key]": key } }
17384
- });
17385
- if (result.error !== void 0)
17386
- await checkError(result.response, `Config with key '${key}' not found`);
17387
- data = result.data;
17388
- } catch (err) {
17389
- wrapFetchError(err);
17390
- }
17391
- if (!data || !data.data || data.data.length === 0) {
17392
- throw new SmplNotFoundError(`Config with key '${key}' not found`);
17393
- }
17394
- return resourceToConfig(data.data[0], this);
17395
- }
17396
17375
  };
17397
17376
 
17398
17377
  // src/flags/client.ts
@@ -17400,10 +17379,8 @@ var import_openapi_fetch2 = __toESM(require("openapi-fetch"), 1);
17400
17379
 
17401
17380
  // src/flags/models.ts
17402
17381
  var Flag = class {
17403
- /** UUID of the flag, or `null` if unsaved. */
17382
+ /** Unique identifier (slug) within the account. */
17404
17383
  id;
17405
- /** Unique key within the account. */
17406
- key;
17407
17384
  /** Human-readable display name. */
17408
17385
  name;
17409
17386
  /** Value type: BOOLEAN, STRING, NUMERIC, or JSON. */
@@ -17426,7 +17403,6 @@ var Flag = class {
17426
17403
  constructor(client, fields) {
17427
17404
  this._client = client;
17428
17405
  this.id = fields.id;
17429
- this.key = fields.key;
17430
17406
  this.name = fields.name;
17431
17407
  this.type = fields.type;
17432
17408
  this.default = fields.default;
@@ -17443,7 +17419,7 @@ var Flag = class {
17443
17419
  * Updates this instance in-place with the server response.
17444
17420
  */
17445
17421
  async save() {
17446
- if (this.id === null) {
17422
+ if (this.createdAt === null) {
17447
17423
  const created = await this._client._createFlag(this);
17448
17424
  this._apply(created);
17449
17425
  } else {
@@ -17507,12 +17483,11 @@ var Flag = class {
17507
17483
  * Requires `initialize()` to have been called on the flags client.
17508
17484
  */
17509
17485
  get(options) {
17510
- return this._client._evaluateHandle(this.key, this.default, options?.context ?? null);
17486
+ return this._client._evaluateHandle(this.id, this.default, options?.context ?? null);
17511
17487
  }
17512
17488
  /** @internal — copy all fields from another Flag instance. */
17513
17489
  _apply(other) {
17514
17490
  this.id = other.id;
17515
- this.key = other.key;
17516
17491
  this.name = other.name;
17517
17492
  this.type = other.type;
17518
17493
  this.default = other.default;
@@ -17523,12 +17498,12 @@ var Flag = class {
17523
17498
  this.updatedAt = other.updatedAt;
17524
17499
  }
17525
17500
  toString() {
17526
- return `Flag(key=${this.key}, type=${this.type}, default=${this.default})`;
17501
+ return `Flag(id=${this.id}, type=${this.type}, default=${this.default})`;
17527
17502
  }
17528
17503
  };
17529
17504
  var BooleanFlag = class extends Flag {
17530
17505
  get(options) {
17531
- const value = this._client._evaluateHandle(this.key, this.default, options?.context ?? null);
17506
+ const value = this._client._evaluateHandle(this.id, this.default, options?.context ?? null);
17532
17507
  if (typeof value === "boolean") {
17533
17508
  return value;
17534
17509
  }
@@ -17537,7 +17512,7 @@ var BooleanFlag = class extends Flag {
17537
17512
  };
17538
17513
  var StringFlag = class extends Flag {
17539
17514
  get(options) {
17540
- const value = this._client._evaluateHandle(this.key, this.default, options?.context ?? null);
17515
+ const value = this._client._evaluateHandle(this.id, this.default, options?.context ?? null);
17541
17516
  if (typeof value === "string") {
17542
17517
  return value;
17543
17518
  }
@@ -17546,7 +17521,7 @@ var StringFlag = class extends Flag {
17546
17521
  };
17547
17522
  var NumberFlag = class extends Flag {
17548
17523
  get(options) {
17549
- const value = this._client._evaluateHandle(this.key, this.default, options?.context ?? null);
17524
+ const value = this._client._evaluateHandle(this.id, this.default, options?.context ?? null);
17550
17525
  if (typeof value === "number") {
17551
17526
  return value;
17552
17527
  }
@@ -17555,7 +17530,7 @@ var NumberFlag = class extends Flag {
17555
17530
  };
17556
17531
  var JsonFlag = class extends Flag {
17557
17532
  get(options) {
17558
- const value = this._client._evaluateHandle(this.key, this.default, options?.context ?? null);
17533
+ const value = this._client._evaluateHandle(this.id, this.default, options?.context ?? null);
17559
17534
  if (typeof value === "object" && value !== null && !Array.isArray(value)) {
17560
17535
  return value;
17561
17536
  }
@@ -17638,10 +17613,10 @@ function evaluateFlag(flagDef, environment, evalDict) {
17638
17613
  return fallback;
17639
17614
  }
17640
17615
  var FlagChangeEvent = class {
17641
- key;
17616
+ id;
17642
17617
  source;
17643
- constructor(key, source) {
17644
- this.key = key;
17618
+ constructor(id, source) {
17619
+ this.id = id;
17645
17620
  this.source = source;
17646
17621
  }
17647
17622
  };
@@ -17783,11 +17758,10 @@ var FlagsClient = class {
17783
17758
  // Management: factory methods (return unsaved flags)
17784
17759
  // ------------------------------------------------------------------
17785
17760
  /** Create an unsaved boolean flag. Call `.save()` to persist. */
17786
- newBooleanFlag(key, options) {
17761
+ newBooleanFlag(id, options) {
17787
17762
  return new BooleanFlag(this, {
17788
- id: null,
17789
- key,
17790
- name: options.name ?? keyToDisplayName(key),
17763
+ id,
17764
+ name: options.name ?? keyToDisplayName(id),
17791
17765
  type: "BOOLEAN",
17792
17766
  default: options.default,
17793
17767
  values: [
@@ -17801,11 +17775,10 @@ var FlagsClient = class {
17801
17775
  });
17802
17776
  }
17803
17777
  /** Create an unsaved string flag. Call `.save()` to persist. */
17804
- newStringFlag(key, options) {
17778
+ newStringFlag(id, options) {
17805
17779
  return new StringFlag(this, {
17806
- id: null,
17807
- key,
17808
- name: options.name ?? keyToDisplayName(key),
17780
+ id,
17781
+ name: options.name ?? keyToDisplayName(id),
17809
17782
  type: "STRING",
17810
17783
  default: options.default,
17811
17784
  values: options.values ?? null,
@@ -17816,11 +17789,10 @@ var FlagsClient = class {
17816
17789
  });
17817
17790
  }
17818
17791
  /** Create an unsaved number flag. Call `.save()` to persist. */
17819
- newNumberFlag(key, options) {
17792
+ newNumberFlag(id, options) {
17820
17793
  return new NumberFlag(this, {
17821
- id: null,
17822
- key,
17823
- name: options.name ?? keyToDisplayName(key),
17794
+ id,
17795
+ name: options.name ?? keyToDisplayName(id),
17824
17796
  type: "NUMERIC",
17825
17797
  default: options.default,
17826
17798
  values: options.values ?? null,
@@ -17831,11 +17803,10 @@ var FlagsClient = class {
17831
17803
  });
17832
17804
  }
17833
17805
  /** Create an unsaved JSON flag. Call `.save()` to persist. */
17834
- newJsonFlag(key, options) {
17806
+ newJsonFlag(id, options) {
17835
17807
  return new JsonFlag(this, {
17836
- id: null,
17837
- key,
17838
- name: options.name ?? keyToDisplayName(key),
17808
+ id,
17809
+ name: options.name ?? keyToDisplayName(id),
17839
17810
  type: "JSON",
17840
17811
  default: options.default,
17841
17812
  values: options.values ?? null,
@@ -17848,23 +17819,23 @@ var FlagsClient = class {
17848
17819
  // ------------------------------------------------------------------
17849
17820
  // Management: CRUD
17850
17821
  // ------------------------------------------------------------------
17851
- /** Fetch a flag by key. */
17852
- async get(key) {
17822
+ /** Fetch a flag by id. */
17823
+ async get(id) {
17853
17824
  let data;
17854
17825
  try {
17855
- const result = await this._http.GET("/api/v1/flags", {
17856
- params: { query: { "filter[key]": key } }
17826
+ const result = await this._http.GET("/api/v1/flags/{id}", {
17827
+ params: { path: { id } }
17857
17828
  });
17858
17829
  if (result.error !== void 0)
17859
- await checkError2(result.response, `Flag with key '${key}' not found`);
17830
+ await checkError2(result.response, `Flag with id '${id}' not found`);
17860
17831
  data = result.data;
17861
17832
  } catch (err) {
17862
17833
  wrapFetchError2(err);
17863
17834
  }
17864
- if (!data || !data.data || data.data.length === 0) {
17865
- throw new SmplNotFoundError(`Flag with key '${key}' not found`);
17835
+ if (!data || !data.data) {
17836
+ throw new SmplNotFoundError(`Flag with id '${id}' not found`);
17866
17837
  }
17867
- return this._resourceToModel(data.data[0]);
17838
+ return this._resourceToModel(data.data);
17868
17839
  }
17869
17840
  /** List all flags. */
17870
17841
  async list() {
@@ -17879,15 +17850,14 @@ var FlagsClient = class {
17879
17850
  if (!data) return [];
17880
17851
  return data.data.map((r) => this._resourceToModel(r));
17881
17852
  }
17882
- /** Delete a flag by key. */
17883
- async delete(key) {
17884
- const flag = await this.get(key);
17853
+ /** Delete a flag by id. */
17854
+ async delete(id) {
17885
17855
  try {
17886
17856
  const result = await this._http.DELETE("/api/v1/flags/{id}", {
17887
- params: { path: { id: flag.id } }
17857
+ params: { path: { id } }
17888
17858
  });
17889
17859
  if (result.error !== void 0 && result.response.status !== 204)
17890
- await checkError2(result.response, `Failed to delete flag '${key}'`);
17860
+ await checkError2(result.response, `Failed to delete flag '${id}'`);
17891
17861
  } catch (err) {
17892
17862
  wrapFetchError2(err);
17893
17863
  }
@@ -17899,9 +17869,9 @@ var FlagsClient = class {
17899
17869
  async _createFlag(flag) {
17900
17870
  const body = {
17901
17871
  data: {
17872
+ id: flag.id,
17902
17873
  type: "flag",
17903
17874
  attributes: {
17904
- key: flag.key,
17905
17875
  name: flag.name,
17906
17876
  description: flag.description ?? "",
17907
17877
  type: flag.type,
@@ -17928,7 +17898,6 @@ var FlagsClient = class {
17928
17898
  data: {
17929
17899
  type: "flag",
17930
17900
  attributes: {
17931
- key: flag.key,
17932
17901
  name: flag.name,
17933
17902
  type: flag.type,
17934
17903
  default: flag.default,
@@ -17957,11 +17926,10 @@ var FlagsClient = class {
17957
17926
  // Runtime: typed flag handles
17958
17927
  // ------------------------------------------------------------------
17959
17928
  /** Declare a boolean flag handle for runtime evaluation. */
17960
- booleanFlag(key, defaultValue) {
17929
+ booleanFlag(id, defaultValue) {
17961
17930
  const handle = new BooleanFlag(this, {
17962
- id: null,
17963
- key,
17964
- name: key,
17931
+ id,
17932
+ name: id,
17965
17933
  type: "BOOLEAN",
17966
17934
  default: defaultValue,
17967
17935
  values: [],
@@ -17970,15 +17938,14 @@ var FlagsClient = class {
17970
17938
  createdAt: null,
17971
17939
  updatedAt: null
17972
17940
  });
17973
- this._handles[key] = handle;
17941
+ this._handles[id] = handle;
17974
17942
  return handle;
17975
17943
  }
17976
17944
  /** Declare a string flag handle for runtime evaluation. */
17977
- stringFlag(key, defaultValue) {
17945
+ stringFlag(id, defaultValue) {
17978
17946
  const handle = new StringFlag(this, {
17979
- id: null,
17980
- key,
17981
- name: key,
17947
+ id,
17948
+ name: id,
17982
17949
  type: "STRING",
17983
17950
  default: defaultValue,
17984
17951
  values: [],
@@ -17987,15 +17954,14 @@ var FlagsClient = class {
17987
17954
  createdAt: null,
17988
17955
  updatedAt: null
17989
17956
  });
17990
- this._handles[key] = handle;
17957
+ this._handles[id] = handle;
17991
17958
  return handle;
17992
17959
  }
17993
17960
  /** Declare a numeric flag handle for runtime evaluation. */
17994
- numberFlag(key, defaultValue) {
17961
+ numberFlag(id, defaultValue) {
17995
17962
  const handle = new NumberFlag(this, {
17996
- id: null,
17997
- key,
17998
- name: key,
17963
+ id,
17964
+ name: id,
17999
17965
  type: "NUMERIC",
18000
17966
  default: defaultValue,
18001
17967
  values: [],
@@ -18004,15 +17970,14 @@ var FlagsClient = class {
18004
17970
  createdAt: null,
18005
17971
  updatedAt: null
18006
17972
  });
18007
- this._handles[key] = handle;
17973
+ this._handles[id] = handle;
18008
17974
  return handle;
18009
17975
  }
18010
17976
  /** Declare a JSON flag handle for runtime evaluation. */
18011
- jsonFlag(key, defaultValue) {
17977
+ jsonFlag(id, defaultValue) {
18012
17978
  const handle = new JsonFlag(this, {
18013
- id: null,
18014
- key,
18015
- name: key,
17979
+ id,
17980
+ name: id,
18016
17981
  type: "JSON",
18017
17982
  default: defaultValue,
18018
17983
  values: [],
@@ -18021,7 +17986,7 @@ var FlagsClient = class {
18021
17986
  createdAt: null,
18022
17987
  updatedAt: null
18023
17988
  });
18024
- this._handles[key] = handle;
17989
+ this._handles[id] = handle;
18025
17990
  return handle;
18026
17991
  }
18027
17992
  // ------------------------------------------------------------------
@@ -18098,20 +18063,20 @@ var FlagsClient = class {
18098
18063
  * Register a change listener.
18099
18064
  *
18100
18065
  * - `onChange(callback)` — fires for any flag change.
18101
- * - `onChange(key, callback)` — fires only for the specified flag key.
18066
+ * - `onChange(id, callback)` — fires only for the specified flag id.
18102
18067
  */
18103
- onChange(callbackOrKey, callback) {
18104
- if (typeof callbackOrKey === "function") {
18105
- this._globalListeners.push(callbackOrKey);
18068
+ onChange(callbackOrId, callback) {
18069
+ if (typeof callbackOrId === "function") {
18070
+ this._globalListeners.push(callbackOrId);
18106
18071
  } else {
18107
- const key = callbackOrKey;
18072
+ const id = callbackOrId;
18108
18073
  if (!callback) {
18109
- throw new SmplError("onChange(key, callback) requires a callback function.");
18074
+ throw new SmplError("onChange(id, callback) requires a callback function.");
18110
18075
  }
18111
- if (!this._keyListeners.has(key)) {
18112
- this._keyListeners.set(key, []);
18076
+ if (!this._keyListeners.has(id)) {
18077
+ this._keyListeners.set(id, []);
18113
18078
  }
18114
- this._keyListeners.get(key).push(callback);
18079
+ this._keyListeners.get(id).push(callback);
18115
18080
  }
18116
18081
  }
18117
18082
  // ------------------------------------------------------------------
@@ -18139,18 +18104,18 @@ var FlagsClient = class {
18139
18104
  /**
18140
18105
  * Evaluate a flag with an explicit environment and context.
18141
18106
  */
18142
- async evaluate(key, options) {
18107
+ async evaluate(id, options) {
18143
18108
  const evalDict = contextsToEvalDict(options.context);
18144
18109
  if (this._parent?._service && !("service" in evalDict)) {
18145
18110
  evalDict["service"] = { key: this._parent._service };
18146
18111
  }
18147
18112
  let flagDef = null;
18148
- if (this._initialized && key in this._flagStore) {
18149
- flagDef = this._flagStore[key];
18113
+ if (this._initialized && id in this._flagStore) {
18114
+ flagDef = this._flagStore[id];
18150
18115
  } else {
18151
18116
  const flags = await this._fetchFlagsList();
18152
18117
  for (const f of flags) {
18153
- if (f.key === key) {
18118
+ if (f.id === id) {
18154
18119
  flagDef = f;
18155
18120
  break;
18156
18121
  }
@@ -18189,8 +18154,18 @@ var FlagsClient = class {
18189
18154
  const cacheKey = `${key}:${ctxHash}`;
18190
18155
  const [hit, cachedValue] = this._cache.get(cacheKey);
18191
18156
  if (hit) {
18157
+ const metrics2 = this._parent?._metrics;
18158
+ if (metrics2) {
18159
+ metrics2.record("flags.cache_hits", 1, "hits");
18160
+ metrics2.record("flags.evaluations", 1, "evaluations", { flag_id: key });
18161
+ }
18192
18162
  return cachedValue;
18193
18163
  }
18164
+ const metrics = this._parent?._metrics;
18165
+ if (metrics) {
18166
+ metrics.record("flags.cache_misses", 1, "misses");
18167
+ metrics.record("flags.evaluations", 1, "evaluations", { flag_id: key });
18168
+ }
18194
18169
  const flagDef = this._flagStore[key];
18195
18170
  if (flagDef === void 0) {
18196
18171
  this._cache.put(cacheKey, defaultValue);
@@ -18220,17 +18195,17 @@ var FlagsClient = class {
18220
18195
  // Internal: event handlers (called by SharedWebSocket)
18221
18196
  // ------------------------------------------------------------------
18222
18197
  _handleFlagChanged = (data) => {
18223
- const flagKey = data.key;
18198
+ const flagId = data.id;
18224
18199
  void this._fetchAllFlags().then(() => {
18225
18200
  this._cache.clear();
18226
- this._fireChangeListeners(flagKey ?? null, "websocket");
18201
+ this._fireChangeListeners(flagId ?? null, "websocket");
18227
18202
  });
18228
18203
  };
18229
18204
  _handleFlagDeleted = (data) => {
18230
- const flagKey = data.key;
18205
+ const flagId = data.id;
18231
18206
  void this._fetchAllFlags().then(() => {
18232
18207
  this._cache.clear();
18233
- this._fireChangeListeners(flagKey ?? null, "websocket");
18208
+ this._fireChangeListeners(flagId ?? null, "websocket");
18234
18209
  });
18235
18210
  };
18236
18211
  // ------------------------------------------------------------------
@@ -18240,7 +18215,7 @@ var FlagsClient = class {
18240
18215
  const flags = await this._fetchFlagsList();
18241
18216
  const store = {};
18242
18217
  for (const f of flags) {
18243
- store[f.key] = f;
18218
+ store[f.id] = f;
18244
18219
  }
18245
18220
  this._flagStore = store;
18246
18221
  }
@@ -18259,18 +18234,18 @@ var FlagsClient = class {
18259
18234
  // ------------------------------------------------------------------
18260
18235
  // Internal: change listeners
18261
18236
  // ------------------------------------------------------------------
18262
- _fireChangeListeners(flagKey, source) {
18263
- if (flagKey) {
18264
- const event = new FlagChangeEvent(flagKey, source);
18237
+ _fireChangeListeners(flagId, source) {
18238
+ if (flagId) {
18239
+ const event = new FlagChangeEvent(flagId, source);
18265
18240
  for (const cb of this._globalListeners) {
18266
18241
  try {
18267
18242
  cb(event);
18268
18243
  } catch {
18269
18244
  }
18270
18245
  }
18271
- const keyCallbacks = this._keyListeners.get(flagKey);
18272
- if (keyCallbacks) {
18273
- for (const cb of keyCallbacks) {
18246
+ const idCallbacks = this._keyListeners.get(flagId);
18247
+ if (idCallbacks) {
18248
+ for (const cb of idCallbacks) {
18274
18249
  try {
18275
18250
  cb(event);
18276
18251
  } catch {
@@ -18280,8 +18255,8 @@ var FlagsClient = class {
18280
18255
  }
18281
18256
  }
18282
18257
  _fireChangeListenersAll(source) {
18283
- for (const flagKey of Object.keys(this._flagStore)) {
18284
- this._fireChangeListeners(flagKey, source);
18258
+ for (const flagId of Object.keys(this._flagStore)) {
18259
+ this._fireChangeListeners(flagId, source);
18285
18260
  }
18286
18261
  }
18287
18262
  // ------------------------------------------------------------------
@@ -18311,7 +18286,6 @@ var FlagsClient = class {
18311
18286
  const attrs = resource.attributes;
18312
18287
  return new Flag(this, {
18313
18288
  id: resource.id ?? null,
18314
- key: attrs.key,
18315
18289
  name: attrs.name,
18316
18290
  type: attrs.type,
18317
18291
  default: attrs.default,
@@ -18325,7 +18299,7 @@ var FlagsClient = class {
18325
18299
  _resourceToPlainDict(resource) {
18326
18300
  const attrs = resource.attributes;
18327
18301
  return {
18328
- key: attrs.key,
18302
+ id: resource.id ?? null,
18329
18303
  name: attrs.name,
18330
18304
  type: attrs.type,
18331
18305
  default: attrs.default,
@@ -18341,15 +18315,13 @@ var import_openapi_fetch3 = __toESM(require("openapi-fetch"), 1);
18341
18315
 
18342
18316
  // src/logging/models.ts
18343
18317
  var Logger = class {
18344
- /** UUID of the logger, or `null` if unsaved. */
18318
+ /** Unique identifier (dot-separated hierarchy, e.g. `"sqlalchemy.engine"`). */
18345
18319
  id;
18346
- /** Unique key (dot-separated hierarchy). */
18347
- key;
18348
18320
  /** Human-readable display name. */
18349
18321
  name;
18350
18322
  /** Base log level, or null if inherited. */
18351
18323
  level;
18352
- /** UUID of the parent log group, or null. */
18324
+ /** Id of the parent log group, or null. */
18353
18325
  group;
18354
18326
  /** Whether this logger is managed by the platform. */
18355
18327
  managed;
@@ -18367,7 +18339,6 @@ var Logger = class {
18367
18339
  constructor(client, fields) {
18368
18340
  this._client = client;
18369
18341
  this.id = fields.id;
18370
- this.key = fields.key;
18371
18342
  this.name = fields.name;
18372
18343
  this.level = fields.level;
18373
18344
  this.group = fields.group;
@@ -18417,7 +18388,6 @@ var Logger = class {
18417
18388
  /** @internal — copy all fields from another Logger instance. */
18418
18389
  _apply(other) {
18419
18390
  this.id = other.id;
18420
- this.key = other.key;
18421
18391
  this.name = other.name;
18422
18392
  this.level = other.level;
18423
18393
  this.group = other.group;
@@ -18428,19 +18398,17 @@ var Logger = class {
18428
18398
  this.updatedAt = other.updatedAt;
18429
18399
  }
18430
18400
  toString() {
18431
- return `Logger(key=${this.key}, level=${this.level})`;
18401
+ return `Logger(id=${this.id}, level=${this.level})`;
18432
18402
  }
18433
18403
  };
18434
18404
  var LogGroup = class {
18435
- /** UUID of the log group, or `null` if unsaved. */
18405
+ /** Unique identifier (slug), or `null` if unsaved. */
18436
18406
  id;
18437
- /** Unique key. */
18438
- key;
18439
18407
  /** Human-readable display name. */
18440
18408
  name;
18441
18409
  /** Base log level, or null if inherited. */
18442
18410
  level;
18443
- /** UUID of the parent log group, or null. */
18411
+ /** Id of the parent log group, or null. */
18444
18412
  group;
18445
18413
  /** Per-environment level overrides. */
18446
18414
  environments;
@@ -18454,7 +18422,6 @@ var LogGroup = class {
18454
18422
  constructor(client, fields) {
18455
18423
  this._client = client;
18456
18424
  this.id = fields.id;
18457
- this.key = fields.key;
18458
18425
  this.name = fields.name;
18459
18426
  this.level = fields.level;
18460
18427
  this.group = fields.group;
@@ -18502,7 +18469,6 @@ var LogGroup = class {
18502
18469
  /** @internal — copy all fields from another LogGroup instance. */
18503
18470
  _apply(other) {
18504
18471
  this.id = other.id;
18505
- this.key = other.key;
18506
18472
  this.name = other.name;
18507
18473
  this.level = other.level;
18508
18474
  this.group = other.group;
@@ -18511,7 +18477,7 @@ var LogGroup = class {
18511
18477
  this.updatedAt = other.updatedAt;
18512
18478
  }
18513
18479
  toString() {
18514
- return `LogGroup(key=${this.key}, level=${this.level})`;
18480
+ return `LogGroup(id=${this.id}, level=${this.level})`;
18515
18481
  }
18516
18482
  };
18517
18483
 
@@ -18595,11 +18561,10 @@ var LoggingClient = class {
18595
18561
  // Management: Logger factory
18596
18562
  // ------------------------------------------------------------------
18597
18563
  /** Create an unsaved logger. Call `.save()` to persist. */
18598
- new(key, options) {
18564
+ new(id, options) {
18599
18565
  return new Logger(this, {
18600
- id: null,
18601
- key,
18602
- name: options?.name ?? keyToDisplayName(key),
18566
+ id,
18567
+ name: options?.name ?? keyToDisplayName(id),
18603
18568
  level: null,
18604
18569
  group: null,
18605
18570
  managed: options?.managed ?? false,
@@ -18612,23 +18577,23 @@ var LoggingClient = class {
18612
18577
  // ------------------------------------------------------------------
18613
18578
  // Management: Logger CRUD
18614
18579
  // ------------------------------------------------------------------
18615
- /** Fetch a logger by key. */
18616
- async get(key) {
18580
+ /** Fetch a logger by id. */
18581
+ async get(id) {
18617
18582
  let data;
18618
18583
  try {
18619
- const result = await this._http.GET("/api/v1/loggers", {
18620
- params: { query: { "filter[key]": key } }
18584
+ const result = await this._http.GET("/api/v1/loggers/{id}", {
18585
+ params: { path: { id } }
18621
18586
  });
18622
18587
  if (result.error !== void 0)
18623
- await checkError3(result.response, `Logger with key '${key}' not found`);
18588
+ await checkError3(result.response, `Logger with id '${id}' not found`);
18624
18589
  data = result.data;
18625
18590
  } catch (err) {
18626
18591
  wrapFetchError3(err);
18627
18592
  }
18628
- if (!data || !data.data || data.data.length === 0) {
18629
- throw new SmplNotFoundError(`Logger with key '${key}' not found`);
18593
+ if (!data || !data.data) {
18594
+ throw new SmplNotFoundError(`Logger with id '${id}' not found`);
18630
18595
  }
18631
- return this._loggerToModel(data.data[0]);
18596
+ return this._loggerToModel(data.data);
18632
18597
  }
18633
18598
  /** List all loggers. */
18634
18599
  async list() {
@@ -18643,15 +18608,14 @@ var LoggingClient = class {
18643
18608
  if (!data) return [];
18644
18609
  return data.data.map((r) => this._loggerToModel(r));
18645
18610
  }
18646
- /** Delete a logger by key. */
18647
- async delete(key) {
18648
- const logger = await this.get(key);
18611
+ /** Delete a logger by id. */
18612
+ async delete(id) {
18649
18613
  try {
18650
18614
  const result = await this._http.DELETE("/api/v1/loggers/{id}", {
18651
- params: { path: { id: logger.id } }
18615
+ params: { path: { id } }
18652
18616
  });
18653
18617
  if (result.error !== void 0 && result.response.status !== 204)
18654
- await checkError3(result.response, `Failed to delete logger '${key}'`);
18618
+ await checkError3(result.response, `Failed to delete logger '${id}'`);
18655
18619
  } catch (err) {
18656
18620
  wrapFetchError3(err);
18657
18621
  }
@@ -18660,11 +18624,10 @@ var LoggingClient = class {
18660
18624
  // Management: LogGroup factory
18661
18625
  // ------------------------------------------------------------------
18662
18626
  /** Create an unsaved log group. Call `.save()` to persist. */
18663
- newGroup(key, options) {
18627
+ newGroup(id, options) {
18664
18628
  return new LogGroup(this, {
18665
- id: null,
18666
- key,
18667
- name: options?.name ?? keyToDisplayName(key),
18629
+ id,
18630
+ name: options?.name ?? keyToDisplayName(id),
18668
18631
  level: null,
18669
18632
  group: options?.group ?? null,
18670
18633
  environments: {},
@@ -18675,12 +18638,12 @@ var LoggingClient = class {
18675
18638
  // ------------------------------------------------------------------
18676
18639
  // Management: LogGroup CRUD
18677
18640
  // ------------------------------------------------------------------
18678
- /** Fetch a log group by key. */
18679
- async getGroup(key) {
18641
+ /** Fetch a log group by id. */
18642
+ async getGroup(id) {
18680
18643
  const groups = await this.listGroups();
18681
- const match = groups.find((g) => g.key === key);
18644
+ const match = groups.find((g) => g.id === id);
18682
18645
  if (!match) {
18683
- throw new SmplNotFoundError(`LogGroup with key '${key}' not found`);
18646
+ throw new SmplNotFoundError(`LogGroup with id '${id}' not found`);
18684
18647
  }
18685
18648
  return match;
18686
18649
  }
@@ -18698,15 +18661,15 @@ var LoggingClient = class {
18698
18661
  if (!data) return [];
18699
18662
  return data.data.map((r) => this._groupToModel(r));
18700
18663
  }
18701
- /** Delete a log group by key. */
18702
- async deleteGroup(key) {
18703
- const group = await this.getGroup(key);
18664
+ /** Delete a log group by id. */
18665
+ async deleteGroup(id) {
18666
+ const group = await this.getGroup(id);
18704
18667
  try {
18705
18668
  const result = await this._http.DELETE("/api/v1/log_groups/{id}", {
18706
18669
  params: { path: { id: group.id } }
18707
18670
  });
18708
18671
  if (result.error !== void 0 && result.response.status !== 204)
18709
- await checkError3(result.response, `Failed to delete log group '${key}'`);
18672
+ await checkError3(result.response, `Failed to delete log group '${id}'`);
18710
18673
  } catch (err) {
18711
18674
  wrapFetchError3(err);
18712
18675
  }
@@ -18718,9 +18681,9 @@ var LoggingClient = class {
18718
18681
  async _saveLogger(logger) {
18719
18682
  const body = {
18720
18683
  data: {
18684
+ id: logger.id,
18721
18685
  type: "logger",
18722
18686
  attributes: {
18723
- key: logger.key,
18724
18687
  name: logger.name,
18725
18688
  level: logger.level,
18726
18689
  group: logger.group,
@@ -18729,7 +18692,7 @@ var LoggingClient = class {
18729
18692
  }
18730
18693
  }
18731
18694
  };
18732
- if (logger.id === null) {
18695
+ if (logger.createdAt === null) {
18733
18696
  let data;
18734
18697
  try {
18735
18698
  const result = await this._http.POST("/api/v1/loggers", { body });
@@ -18763,9 +18726,9 @@ var LoggingClient = class {
18763
18726
  async _saveLogGroup(group) {
18764
18727
  const body = {
18765
18728
  data: {
18729
+ id: group.id,
18766
18730
  type: "log_group",
18767
18731
  attributes: {
18768
- key: group.key,
18769
18732
  name: group.name,
18770
18733
  level: group.level,
18771
18734
  group: group.group,
@@ -18773,7 +18736,7 @@ var LoggingClient = class {
18773
18736
  }
18774
18737
  }
18775
18738
  };
18776
- if (group.id === null) {
18739
+ if (group.createdAt === null) {
18777
18740
  let data;
18778
18741
  try {
18779
18742
  const result = await this._http.POST("/api/v1/log_groups", { body });
@@ -18834,6 +18797,12 @@ var LoggingClient = class {
18834
18797
  } catch {
18835
18798
  }
18836
18799
  }
18800
+ if (discovered.length > 0) {
18801
+ const metrics = this._parent?._metrics;
18802
+ if (metrics) {
18803
+ metrics.record("logging.loggers_discovered", discovered.length, "loggers");
18804
+ }
18805
+ }
18837
18806
  for (const { name, level } of discovered) {
18838
18807
  try {
18839
18808
  const logger = this.new(name, { managed: true });
@@ -18858,20 +18827,20 @@ var LoggingClient = class {
18858
18827
  * Register a change listener.
18859
18828
  *
18860
18829
  * - `onChange(callback)` — fires for any logger change.
18861
- * - `onChange(key, callback)` — fires only for the specified logger key.
18830
+ * - `onChange(id, callback)` — fires only for the specified logger id.
18862
18831
  */
18863
- onChange(callbackOrKey, callback) {
18864
- if (typeof callbackOrKey === "function") {
18865
- this._globalListeners.push(callbackOrKey);
18832
+ onChange(callbackOrId, callback) {
18833
+ if (typeof callbackOrId === "function") {
18834
+ this._globalListeners.push(callbackOrId);
18866
18835
  } else {
18867
- const key = callbackOrKey;
18836
+ const id = callbackOrId;
18868
18837
  if (!callback) {
18869
- throw new SmplError("onChange(key, callback) requires a callback function.");
18838
+ throw new SmplError("onChange(id, callback) requires a callback function.");
18870
18839
  }
18871
- if (!this._keyListeners.has(key)) {
18872
- this._keyListeners.set(key, []);
18840
+ if (!this._keyListeners.has(id)) {
18841
+ this._keyListeners.set(id, []);
18873
18842
  }
18874
- this._keyListeners.get(key).push(callback);
18843
+ this._keyListeners.get(id).push(callback);
18875
18844
  }
18876
18845
  }
18877
18846
  // ------------------------------------------------------------------
@@ -18928,9 +18897,13 @@ var LoggingClient = class {
18928
18897
  effectiveLevel = envOverride.level;
18929
18898
  }
18930
18899
  }
18900
+ const metrics = this._parent?._metrics;
18901
+ if (metrics) {
18902
+ metrics.record("logging.level_changes", 1, "changes", { logger_id: logger.id });
18903
+ }
18931
18904
  for (const adapter of this._adapters) {
18932
18905
  try {
18933
- adapter.applyLevel(logger.key, effectiveLevel);
18906
+ adapter.applyLevel(logger.id, effectiveLevel);
18934
18907
  } catch {
18935
18908
  }
18936
18909
  }
@@ -18947,11 +18920,11 @@ var LoggingClient = class {
18947
18920
  // Internal: WebSocket handler
18948
18921
  // ------------------------------------------------------------------
18949
18922
  _handleLoggerChanged = (data) => {
18950
- const key = data.key;
18951
- if (key) {
18923
+ const id = data.id;
18924
+ if (id) {
18952
18925
  const level = data.level ?? null;
18953
18926
  const event = {
18954
- key,
18927
+ id,
18955
18928
  level,
18956
18929
  source: "websocket"
18957
18930
  };
@@ -18961,9 +18934,9 @@ var LoggingClient = class {
18961
18934
  } catch {
18962
18935
  }
18963
18936
  }
18964
- const keyCallbacks = this._keyListeners.get(key);
18965
- if (keyCallbacks) {
18966
- for (const cb of keyCallbacks) {
18937
+ const idCallbacks = this._keyListeners.get(id);
18938
+ if (idCallbacks) {
18939
+ for (const cb of idCallbacks) {
18967
18940
  try {
18968
18941
  cb(event);
18969
18942
  } catch {
@@ -18979,7 +18952,6 @@ var LoggingClient = class {
18979
18952
  const attrs = resource.attributes;
18980
18953
  return new Logger(this, {
18981
18954
  id: resource.id ?? null,
18982
- key: attrs.key ?? "",
18983
18955
  name: attrs.name,
18984
18956
  level: attrs.level ?? null,
18985
18957
  group: attrs.group ?? null,
@@ -18994,7 +18966,6 @@ var LoggingClient = class {
18994
18966
  const attrs = resource.attributes;
18995
18967
  return new LogGroup(this, {
18996
18968
  id: resource.id ?? null,
18997
- key: attrs.key ?? "",
18998
18969
  name: attrs.name,
18999
18970
  level: attrs.level ?? null,
19000
18971
  group: attrs.group ?? null,
@@ -19011,15 +18982,17 @@ var BACKOFF_MS = [1e3, 2e3, 4e3, 8e3, 16e3, 32e3, 6e4];
19011
18982
  var SharedWebSocket = class {
19012
18983
  _appBaseUrl;
19013
18984
  _apiKey;
18985
+ _metrics;
19014
18986
  _listeners = /* @__PURE__ */ new Map();
19015
18987
  _connectionStatus = "disconnected";
19016
18988
  _closed = false;
19017
18989
  _ws = null;
19018
18990
  _reconnectTimer = null;
19019
18991
  _backoffIndex = 0;
19020
- constructor(appBaseUrl, apiKey) {
18992
+ constructor(appBaseUrl, apiKey, metrics) {
19021
18993
  this._appBaseUrl = appBaseUrl;
19022
18994
  this._apiKey = apiKey;
18995
+ this._metrics = metrics ?? null;
19023
18996
  }
19024
18997
  // ------------------------------------------------------------------
19025
18998
  // Listener registration
@@ -19118,6 +19091,9 @@ var SharedWebSocket = class {
19118
19091
  if (msg.type === "connected") {
19119
19092
  this._backoffIndex = 0;
19120
19093
  this._connectionStatus = "connected";
19094
+ if (this._metrics) {
19095
+ this._metrics.recordGauge("platform.websocket_connections", 1, "connections");
19096
+ }
19121
19097
  return;
19122
19098
  }
19123
19099
  if (msg.type === "error") {
@@ -19131,6 +19107,9 @@ var SharedWebSocket = class {
19131
19107
  }
19132
19108
  });
19133
19109
  ws.on("close", () => {
19110
+ if (this._metrics) {
19111
+ this._metrics.recordGauge("platform.websocket_connections", 0, "connections");
19112
+ }
19134
19113
  if (!this._closed) {
19135
19114
  this._connectionStatus = "disconnected";
19136
19115
  this._scheduleReconnect();
@@ -19211,8 +19190,145 @@ function resolveApiKey(explicit, environment) {
19211
19190
  throw new SmplError(noApiKeyMessage(environment));
19212
19191
  }
19213
19192
 
19214
- // src/client.ts
19193
+ // src/_metrics.ts
19215
19194
  var APP_BASE_URL2 = "https://app.smplkit.com";
19195
+ function makeCounter(unit) {
19196
+ return { value: 0, unit, windowStart: (/* @__PURE__ */ new Date()).toISOString() };
19197
+ }
19198
+ function makeMapKey(name, dimensions) {
19199
+ const sorted = Object.keys(dimensions).sort().map((k) => `${k}=${dimensions[k]}`).join("&");
19200
+ return `${name}|${sorted}`;
19201
+ }
19202
+ var MetricsReporter = class {
19203
+ _apiKey;
19204
+ _environment;
19205
+ _service;
19206
+ _flushInterval;
19207
+ _counters = /* @__PURE__ */ new Map();
19208
+ _gauges = /* @__PURE__ */ new Map();
19209
+ _timer = null;
19210
+ _closed = false;
19211
+ constructor(options) {
19212
+ this._apiKey = options.apiKey;
19213
+ this._environment = options.environment;
19214
+ this._service = options.service;
19215
+ this._flushInterval = options.flushInterval ?? 60;
19216
+ }
19217
+ // ------------------------------------------------------------------
19218
+ // Public recording API
19219
+ // ------------------------------------------------------------------
19220
+ record(name, value = 1, unit = null, dimensions) {
19221
+ const merged = this._mergeDimensions(dimensions);
19222
+ const key = makeMapKey(name, merged);
19223
+ let entry = this._counters.get(key);
19224
+ if (!entry) {
19225
+ entry = { name, dimensions: merged, counter: makeCounter(unit) };
19226
+ this._counters.set(key, entry);
19227
+ }
19228
+ entry.counter.value += value;
19229
+ if (entry.counter.unit === null && unit !== null) {
19230
+ entry.counter.unit = unit;
19231
+ }
19232
+ this._maybeStartTimer();
19233
+ }
19234
+ recordGauge(name, value, unit = null, dimensions) {
19235
+ const merged = this._mergeDimensions(dimensions);
19236
+ const key = makeMapKey(name, merged);
19237
+ let entry = this._gauges.get(key);
19238
+ if (!entry) {
19239
+ entry = { name, dimensions: merged, counter: makeCounter(unit) };
19240
+ this._gauges.set(key, entry);
19241
+ }
19242
+ entry.counter.value = value;
19243
+ if (entry.counter.unit === null && unit !== null) {
19244
+ entry.counter.unit = unit;
19245
+ }
19246
+ this._maybeStartTimer();
19247
+ }
19248
+ // ------------------------------------------------------------------
19249
+ // Flush / close
19250
+ // ------------------------------------------------------------------
19251
+ flush() {
19252
+ this._flush();
19253
+ }
19254
+ close() {
19255
+ if (this._closed) return;
19256
+ this._closed = true;
19257
+ if (this._timer !== null) {
19258
+ clearInterval(this._timer);
19259
+ this._timer = null;
19260
+ }
19261
+ this._flush();
19262
+ }
19263
+ // ------------------------------------------------------------------
19264
+ // Internal
19265
+ // ------------------------------------------------------------------
19266
+ _mergeDimensions(dimensions) {
19267
+ const merged = {
19268
+ environment: this._environment,
19269
+ service: this._service
19270
+ };
19271
+ if (dimensions) {
19272
+ Object.assign(merged, dimensions);
19273
+ }
19274
+ return merged;
19275
+ }
19276
+ _maybeStartTimer() {
19277
+ if (this._timer === null && !this._closed) {
19278
+ this._timer = setInterval(() => this._flush(), this._flushInterval * 1e3);
19279
+ if (typeof this._timer === "object" && "unref" in this._timer) {
19280
+ this._timer.unref();
19281
+ }
19282
+ }
19283
+ }
19284
+ _flush() {
19285
+ const counters = this._counters;
19286
+ const gauges = this._gauges;
19287
+ this._counters = /* @__PURE__ */ new Map();
19288
+ this._gauges = /* @__PURE__ */ new Map();
19289
+ if (counters.size === 0 && gauges.size === 0) return;
19290
+ const payload = this._buildPayload(counters, gauges);
19291
+ try {
19292
+ fetch(`${APP_BASE_URL2}/api/v1/metrics/bulk`, {
19293
+ method: "POST",
19294
+ headers: {
19295
+ Authorization: `Bearer ${this._apiKey}`,
19296
+ "Content-Type": "application/vnd.api+json",
19297
+ Accept: "application/json"
19298
+ },
19299
+ body: JSON.stringify(payload)
19300
+ }).catch(() => {
19301
+ });
19302
+ } catch {
19303
+ }
19304
+ }
19305
+ _buildPayload(counters, gauges) {
19306
+ const data = [];
19307
+ for (const [, entry] of counters) {
19308
+ data.push(this._entry(entry.name, entry.counter, entry.dimensions));
19309
+ }
19310
+ for (const [, entry] of gauges) {
19311
+ data.push(this._entry(entry.name, entry.counter, entry.dimensions));
19312
+ }
19313
+ return { data };
19314
+ }
19315
+ _entry(name, counter, dimensions) {
19316
+ return {
19317
+ type: "metric",
19318
+ attributes: {
19319
+ name,
19320
+ value: counter.value,
19321
+ unit: counter.unit,
19322
+ period_seconds: this._flushInterval,
19323
+ dimensions,
19324
+ recorded_at: counter.windowStart
19325
+ }
19326
+ };
19327
+ }
19328
+ };
19329
+
19330
+ // src/client.ts
19331
+ var APP_BASE_URL3 = "https://app.smplkit.com";
19216
19332
  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";
19217
19333
  var NO_SERVICE_MESSAGE = "No service provided. Set one of:\n 1. Pass service in options\n 2. Set the SMPLKIT_SERVICE environment variable";
19218
19334
  var SmplClient = class {
@@ -19228,6 +19344,8 @@ var SmplClient = class {
19228
19344
  _environment;
19229
19345
  /** @internal */
19230
19346
  _service;
19347
+ /** @internal */
19348
+ _metrics = null;
19231
19349
  _timeout;
19232
19350
  _appHttp;
19233
19351
  constructor(options = {}) {
@@ -19245,12 +19363,19 @@ var SmplClient = class {
19245
19363
  this._apiKey = apiKey;
19246
19364
  this._timeout = options.timeout ?? 3e4;
19247
19365
  this._appHttp = (0, import_openapi_fetch4.default)({
19248
- baseUrl: APP_BASE_URL2,
19366
+ baseUrl: APP_BASE_URL3,
19249
19367
  headers: {
19250
19368
  Authorization: `Bearer ${apiKey}`,
19251
19369
  Accept: "application/json"
19252
19370
  }
19253
19371
  });
19372
+ if (!options.disableTelemetry) {
19373
+ this._metrics = new MetricsReporter({
19374
+ apiKey,
19375
+ environment: this._environment,
19376
+ service: this._service
19377
+ });
19378
+ }
19254
19379
  this.config = new ConfigClient(apiKey, this._timeout);
19255
19380
  this.flags = new FlagsClient(apiKey, () => this._ensureWs(), this._timeout);
19256
19381
  this.logging = new LoggingClient(apiKey, () => this._ensureWs(), this._timeout);
@@ -19280,13 +19405,16 @@ var SmplClient = class {
19280
19405
  /** Lazily create and start the shared WebSocket. @internal */
19281
19406
  _ensureWs() {
19282
19407
  if (this._wsManager === null) {
19283
- this._wsManager = new SharedWebSocket(APP_BASE_URL2, this._apiKey);
19408
+ this._wsManager = new SharedWebSocket(APP_BASE_URL3, this._apiKey, this._metrics);
19284
19409
  this._wsManager.start();
19285
19410
  }
19286
19411
  return this._wsManager;
19287
19412
  }
19288
19413
  /** Close the shared WebSocket and release resources. */
19289
19414
  close() {
19415
+ if (this._metrics !== null) {
19416
+ this._metrics.close();
19417
+ }
19290
19418
  this.logging._close();
19291
19419
  if (this._wsManager !== null) {
19292
19420
  this._wsManager.stop();