@smplkit/sdk 1.5.6 → 1.5.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -906,6 +906,8 @@ declare class FlagsClient {
906
906
  private _cache;
907
907
  private _contextProvider;
908
908
  private _contextBuffer;
909
+ private _flagBuffer;
910
+ private _flagFlushTimer;
909
911
  private _handles;
910
912
  private _globalListeners;
911
913
  private _keyListeners;
@@ -1032,6 +1034,7 @@ declare class FlagsClient {
1032
1034
  private _fetchFlagsList;
1033
1035
  private _fireChangeListeners;
1034
1036
  private _fireChangeListenersAll;
1037
+ private _flushFlags;
1035
1038
  private _flushContexts;
1036
1039
  /** @internal */
1037
1040
  _resourceToModel(resource: FlagResource): Flag;
@@ -1267,6 +1270,8 @@ declare class LoggingClient {
1267
1270
  private _keyListeners;
1268
1271
  private _adapters;
1269
1272
  private _explicitAdapters;
1273
+ private _loggerBuffer;
1274
+ private _loggerFlushTimer;
1270
1275
  /** @internal */
1271
1276
  constructor(apiKey: string, ensureWs: () => SharedWebSocket, timeout?: number);
1272
1277
  /**
@@ -1325,6 +1330,8 @@ declare class LoggingClient {
1325
1330
  private _applyLevels;
1326
1331
  /** Called by adapter hooks when a new logger is created in the framework. */
1327
1332
  private _onAdapterNewLogger;
1333
+ /** Flush buffered loggers to the bulk-register endpoint. */
1334
+ private _flushLoggerBuffer;
1328
1335
  private _handleLoggerChanged;
1329
1336
  private _handleGroupChanged;
1330
1337
  private _loggerToModel;
package/dist/index.d.ts CHANGED
@@ -906,6 +906,8 @@ declare class FlagsClient {
906
906
  private _cache;
907
907
  private _contextProvider;
908
908
  private _contextBuffer;
909
+ private _flagBuffer;
910
+ private _flagFlushTimer;
909
911
  private _handles;
910
912
  private _globalListeners;
911
913
  private _keyListeners;
@@ -1032,6 +1034,7 @@ declare class FlagsClient {
1032
1034
  private _fetchFlagsList;
1033
1035
  private _fireChangeListeners;
1034
1036
  private _fireChangeListenersAll;
1037
+ private _flushFlags;
1035
1038
  private _flushContexts;
1036
1039
  /** @internal */
1037
1040
  _resourceToModel(resource: FlagResource): Flag;
@@ -1267,6 +1270,8 @@ declare class LoggingClient {
1267
1270
  private _keyListeners;
1268
1271
  private _adapters;
1269
1272
  private _explicitAdapters;
1273
+ private _loggerBuffer;
1274
+ private _loggerFlushTimer;
1270
1275
  /** @internal */
1271
1276
  constructor(apiKey: string, ensureWs: () => SharedWebSocket, timeout?: number);
1272
1277
  /**
@@ -1325,6 +1330,8 @@ declare class LoggingClient {
1325
1330
  private _applyLevels;
1326
1331
  /** Called by adapter hooks when a new logger is created in the framework. */
1327
1332
  private _onAdapterNewLogger;
1333
+ /** Flush buffered loggers to the bulk-register endpoint. */
1334
+ private _flushLoggerBuffer;
1328
1335
  private _handleLoggerChanged;
1329
1336
  private _handleGroupChanged;
1330
1337
  private _loggerToModel;
package/dist/index.js CHANGED
@@ -17538,6 +17538,8 @@ var APP_BASE_URL = "https://app.smplkit.com";
17538
17538
  var CACHE_MAX_SIZE = 1e4;
17539
17539
  var CONTEXT_REGISTRATION_LRU_SIZE = 1e4;
17540
17540
  var CONTEXT_BATCH_FLUSH_SIZE = 100;
17541
+ var FLAG_REGISTRATION_FLUSH_SIZE = 50;
17542
+ var FLAG_REGISTRATION_FLUSH_INTERVAL_MS = 3e4;
17541
17543
  async function checkError2(response, _context) {
17542
17544
  const body = await response.text().catch(() => "");
17543
17545
  throwForStatus(response.status, body);
@@ -17687,6 +17689,30 @@ var ContextRegistrationBuffer = class {
17687
17689
  return this._pending.length;
17688
17690
  }
17689
17691
  };
17692
+ var FlagRegistrationBuffer = class {
17693
+ _seen = /* @__PURE__ */ new Set();
17694
+ _pending = [];
17695
+ add(id, type, defaultValue, service, environment) {
17696
+ if (!this._seen.has(id)) {
17697
+ this._seen.add(id);
17698
+ this._pending.push({
17699
+ id,
17700
+ type,
17701
+ default: defaultValue,
17702
+ service: service ?? void 0,
17703
+ environment: environment ?? void 0
17704
+ });
17705
+ }
17706
+ }
17707
+ drain() {
17708
+ const batch = this._pending;
17709
+ this._pending = [];
17710
+ return batch;
17711
+ }
17712
+ get pendingCount() {
17713
+ return this._pending.length;
17714
+ }
17715
+ };
17690
17716
  var FlagsManagement = class {
17691
17717
  constructor(_client) {
17692
17718
  this._client = _client;
@@ -17736,6 +17762,8 @@ var FlagsClient = class {
17736
17762
  _cache = new ResolutionCache();
17737
17763
  _contextProvider = null;
17738
17764
  _contextBuffer = new ContextRegistrationBuffer();
17765
+ _flagBuffer = new FlagRegistrationBuffer();
17766
+ _flagFlushTimer = null;
17739
17767
  _handles = {};
17740
17768
  _globalListeners = [];
17741
17769
  _keyListeners = /* @__PURE__ */ new Map();
@@ -17963,6 +17991,16 @@ var FlagsClient = class {
17963
17991
  updatedAt: null
17964
17992
  });
17965
17993
  this._handles[id] = handle;
17994
+ this._flagBuffer.add(
17995
+ id,
17996
+ "BOOLEAN",
17997
+ defaultValue,
17998
+ this._parent?._service ?? null,
17999
+ this._parent?._environment ?? null
18000
+ );
18001
+ if (this._flagBuffer.pendingCount >= FLAG_REGISTRATION_FLUSH_SIZE) {
18002
+ void this._flushFlags();
18003
+ }
17966
18004
  return handle;
17967
18005
  }
17968
18006
  /** Declare a string flag handle for runtime evaluation. */
@@ -17979,6 +18017,16 @@ var FlagsClient = class {
17979
18017
  updatedAt: null
17980
18018
  });
17981
18019
  this._handles[id] = handle;
18020
+ this._flagBuffer.add(
18021
+ id,
18022
+ "STRING",
18023
+ defaultValue,
18024
+ this._parent?._service ?? null,
18025
+ this._parent?._environment ?? null
18026
+ );
18027
+ if (this._flagBuffer.pendingCount >= FLAG_REGISTRATION_FLUSH_SIZE) {
18028
+ void this._flushFlags();
18029
+ }
17982
18030
  return handle;
17983
18031
  }
17984
18032
  /** Declare a numeric flag handle for runtime evaluation. */
@@ -17995,6 +18043,16 @@ var FlagsClient = class {
17995
18043
  updatedAt: null
17996
18044
  });
17997
18045
  this._handles[id] = handle;
18046
+ this._flagBuffer.add(
18047
+ id,
18048
+ "NUMERIC",
18049
+ defaultValue,
18050
+ this._parent?._service ?? null,
18051
+ this._parent?._environment ?? null
18052
+ );
18053
+ if (this._flagBuffer.pendingCount >= FLAG_REGISTRATION_FLUSH_SIZE) {
18054
+ void this._flushFlags();
18055
+ }
17998
18056
  return handle;
17999
18057
  }
18000
18058
  /** Declare a JSON flag handle for runtime evaluation. */
@@ -18011,6 +18069,16 @@ var FlagsClient = class {
18011
18069
  updatedAt: null
18012
18070
  });
18013
18071
  this._handles[id] = handle;
18072
+ this._flagBuffer.add(
18073
+ id,
18074
+ "JSON",
18075
+ defaultValue,
18076
+ this._parent?._service ?? null,
18077
+ this._parent?._environment ?? null
18078
+ );
18079
+ if (this._flagBuffer.pendingCount >= FLAG_REGISTRATION_FLUSH_SIZE) {
18080
+ void this._flushFlags();
18081
+ }
18014
18082
  return handle;
18015
18083
  }
18016
18084
  // ------------------------------------------------------------------
@@ -18044,12 +18112,16 @@ var FlagsClient = class {
18044
18112
  if (this._initialized) return;
18045
18113
  debug("lifecycle", "FlagsClient.initialize() called");
18046
18114
  this._environment = this._parent?._environment ?? null;
18115
+ await this._flushFlags();
18047
18116
  await this._fetchAllFlags();
18048
18117
  this._initialized = true;
18049
18118
  this._cache.clear();
18050
18119
  this._wsManager = this._ensureWs();
18051
18120
  this._wsManager.on("flag_changed", this._handleFlagChanged);
18052
18121
  this._wsManager.on("flag_deleted", this._handleFlagDeleted);
18122
+ this._flagFlushTimer = setInterval(() => {
18123
+ void this._flushFlags();
18124
+ }, FLAG_REGISTRATION_FLUSH_INTERVAL_MS);
18053
18125
  }
18054
18126
  /** Disconnect the flags runtime and release resources. */
18055
18127
  async disconnect() {
@@ -18058,6 +18130,10 @@ var FlagsClient = class {
18058
18130
  this._wsManager.off("flag_deleted", this._handleFlagDeleted);
18059
18131
  this._wsManager = null;
18060
18132
  }
18133
+ if (this._flagFlushTimer !== null) {
18134
+ clearInterval(this._flagFlushTimer);
18135
+ this._flagFlushTimer = null;
18136
+ }
18061
18137
  await this._flushContexts();
18062
18138
  this._flagStore = {};
18063
18139
  this._cache.clear();
@@ -18209,12 +18285,16 @@ var FlagsClient = class {
18209
18285
  /** @internal — called by SmplClient constructor / lazy init. */
18210
18286
  async _connectInternal(environment) {
18211
18287
  this._environment = environment;
18288
+ await this._flushFlags();
18212
18289
  await this._fetchAllFlags();
18213
18290
  this._initialized = true;
18214
18291
  this._cache.clear();
18215
18292
  this._wsManager = this._ensureWs();
18216
18293
  this._wsManager.on("flag_changed", this._handleFlagChanged);
18217
18294
  this._wsManager.on("flag_deleted", this._handleFlagDeleted);
18295
+ this._flagFlushTimer = setInterval(() => {
18296
+ void this._flushFlags();
18297
+ }, FLAG_REGISTRATION_FLUSH_INTERVAL_MS);
18218
18298
  }
18219
18299
  // ------------------------------------------------------------------
18220
18300
  // Internal: event handlers (called by SharedWebSocket)
@@ -18290,6 +18370,23 @@ var FlagsClient = class {
18290
18370
  }
18291
18371
  }
18292
18372
  // ------------------------------------------------------------------
18373
+ // Internal: flag registration flush
18374
+ // ------------------------------------------------------------------
18375
+ async _flushFlags() {
18376
+ const batch = this._flagBuffer.drain();
18377
+ if (batch.length === 0) return;
18378
+ debug("registration", `flushing ${batch.length} flag(s) to bulk-register endpoint`);
18379
+ try {
18380
+ await this._http.POST("/api/v1/flags/bulk", {
18381
+ body: { flags: batch }
18382
+ });
18383
+ } catch (err) {
18384
+ console.warn(
18385
+ `[smplkit] Failed to bulk-register flags: ${err instanceof Error ? err.message : String(err)}`
18386
+ );
18387
+ }
18388
+ }
18389
+ // ------------------------------------------------------------------
18293
18390
  // Internal: context flush
18294
18391
  // ------------------------------------------------------------------
18295
18392
  async _flushContexts() {
@@ -18513,6 +18610,26 @@ var LogGroup = class {
18513
18610
 
18514
18611
  // src/logging/client.ts
18515
18612
  var LOGGING_BASE_URL = "https://logging.smplkit.com";
18613
+ var LoggerRegistrationBuffer = class {
18614
+ _seen = /* @__PURE__ */ new Set();
18615
+ _pending = [];
18616
+ add(id, level, resolvedLevel, service, environment) {
18617
+ if (this._seen.has(id)) return;
18618
+ this._seen.add(id);
18619
+ const item = { id, level, resolved_level: resolvedLevel };
18620
+ if (service) item.service = service;
18621
+ if (environment) item.environment = environment;
18622
+ this._pending.push(item);
18623
+ }
18624
+ drain() {
18625
+ const batch = this._pending;
18626
+ this._pending = [];
18627
+ return batch;
18628
+ }
18629
+ get pendingCount() {
18630
+ return this._pending.length;
18631
+ }
18632
+ };
18516
18633
  async function checkError3(response, _context) {
18517
18634
  const body = await response.text().catch(() => "");
18518
18635
  throwForStatus(response.status, body);
@@ -18583,6 +18700,8 @@ var LoggingClient = class {
18583
18700
  _keyListeners = /* @__PURE__ */ new Map();
18584
18701
  _adapters = [];
18585
18702
  _explicitAdapters = false;
18703
+ _loggerBuffer = new LoggerRegistrationBuffer();
18704
+ _loggerFlushTimer = null;
18586
18705
  /** @internal */
18587
18706
  constructor(apiKey, ensureWs, timeout) {
18588
18707
  this._apiKey = apiKey;
@@ -18828,15 +18947,20 @@ var LoggingClient = class {
18828
18947
  if (!this._explicitAdapters) {
18829
18948
  this._adapters = this._autoLoadAdapters();
18830
18949
  }
18831
- const discovered = [];
18950
+ const service = this._parent?._service ?? null;
18951
+ const environment = this._parent?._environment ?? null;
18952
+ let discoveredCount = 0;
18832
18953
  for (const adapter of this._adapters) {
18833
18954
  try {
18834
18955
  const loggers = adapter.discover();
18835
- discovered.push(...loggers);
18956
+ for (const { name, level } of loggers) {
18957
+ this._loggerBuffer.add(name, level, level, service, environment);
18958
+ discoveredCount++;
18959
+ }
18836
18960
  } catch {
18837
18961
  }
18838
18962
  }
18839
- debug("discovery", `discovered ${discovered.length} logger(s) from adapters`);
18963
+ debug("discovery", `discovered ${discoveredCount} logger(s) from adapters`);
18840
18964
  for (const adapter of this._adapters) {
18841
18965
  try {
18842
18966
  adapter.installHook((name, level) => {
@@ -18846,39 +18970,13 @@ var LoggingClient = class {
18846
18970
  } catch {
18847
18971
  }
18848
18972
  }
18849
- if (discovered.length > 0) {
18973
+ if (discoveredCount > 0) {
18850
18974
  const metrics = this._parent?._metrics;
18851
18975
  if (metrics) {
18852
- metrics.record("logging.loggers_discovered", discovered.length, "loggers");
18853
- }
18854
- }
18855
- if (discovered.length > 0) {
18856
- const service = this._parent?._service ?? null;
18857
- const environment = this._parent?._environment ?? null;
18858
- const loggers = discovered.map(
18859
- ({ name, level }) => ({
18860
- id: name,
18861
- // For Winston/Pino there is no inherited-null distinction — both fields carry the same value.
18862
- level,
18863
- resolved_level: level,
18864
- service: service ?? void 0,
18865
- environment: environment ?? void 0
18866
- })
18867
- );
18868
- debug("registration", `flushing ${loggers.length} logger(s) to bulk-register endpoint`);
18869
- try {
18870
- const result = await this._http.POST("/api/v1/loggers/bulk", {
18871
- body: { loggers }
18872
- });
18873
- if (result.error !== void 0)
18874
- await checkError3(result.response, "Failed to bulk-register loggers");
18875
- debug("registration", `bulk-register complete (${loggers.length} logger(s))`);
18876
- } catch (err) {
18877
- console.warn(
18878
- `[smplkit] Failed to bulk-register loggers: ${err instanceof Error ? err.message : String(err)}`
18879
- );
18976
+ metrics.record("logging.loggers_discovered", discoveredCount, "loggers");
18880
18977
  }
18881
18978
  }
18979
+ await this._flushLoggerBuffer();
18882
18980
  debug("resolution", `starting resolution pass (trigger: start())`);
18883
18981
  try {
18884
18982
  const [serverLoggers, serverGroups] = await Promise.all([
@@ -18897,6 +18995,9 @@ var LoggingClient = class {
18897
18995
  this._wsManager.on("logger_deleted", this._handleLoggerChanged);
18898
18996
  this._wsManager.on("group_changed", this._handleGroupChanged);
18899
18997
  this._wsManager.on("group_deleted", this._handleGroupChanged);
18998
+ this._loggerFlushTimer = setInterval(() => {
18999
+ void this._flushLoggerBuffer();
19000
+ }, 3e4);
18900
19001
  this._started = true;
18901
19002
  }
18902
19003
  // ------------------------------------------------------------------
@@ -18928,6 +19029,10 @@ var LoggingClient = class {
18928
19029
  /** @internal */
18929
19030
  _close() {
18930
19031
  debug("lifecycle", "LoggingClient._close() called");
19032
+ if (this._loggerFlushTimer !== null) {
19033
+ clearInterval(this._loggerFlushTimer);
19034
+ this._loggerFlushTimer = null;
19035
+ }
18931
19036
  for (const adapter of this._adapters) {
18932
19037
  try {
18933
19038
  adapter.uninstallHook();
@@ -18996,12 +19101,34 @@ var LoggingClient = class {
18996
19101
  }
18997
19102
  }
18998
19103
  /** Called by adapter hooks when a new logger is created in the framework. */
18999
- _onAdapterNewLogger(_name, _level) {
19000
- debug("discovery", `new logger intercepted at runtime: ${_name}`);
19001
- const logger = this.management.new(_name, { managed: true });
19002
- logger.setLevel(_level);
19003
- logger.save().catch(() => {
19004
- });
19104
+ _onAdapterNewLogger(name, level) {
19105
+ debug("discovery", `new logger intercepted at runtime: ${name}`);
19106
+ const service = this._parent?._service ?? null;
19107
+ const environment = this._parent?._environment ?? null;
19108
+ this._loggerBuffer.add(name, level, level, service, environment);
19109
+ if (this._loggerBuffer.pendingCount >= 50) {
19110
+ void this._flushLoggerBuffer();
19111
+ }
19112
+ }
19113
+ /** Flush buffered loggers to the bulk-register endpoint. */
19114
+ async _flushLoggerBuffer() {
19115
+ const batch = this._loggerBuffer.drain();
19116
+ if (batch.length === 0) return;
19117
+ debug("registration", `flushing ${batch.length} logger(s) to bulk-register endpoint`);
19118
+ try {
19119
+ const result = await this._http.POST("/api/v1/loggers/bulk", {
19120
+ body: { loggers: batch }
19121
+ });
19122
+ if (result.error !== void 0) {
19123
+ console.warn("[smplkit] Logger bulk registration failed");
19124
+ } else {
19125
+ debug("registration", `bulk-register complete (${batch.length} logger(s))`);
19126
+ }
19127
+ } catch (err) {
19128
+ console.warn(
19129
+ `[smplkit] Logger bulk registration failed: ${err instanceof Error ? err.message : String(err)}`
19130
+ );
19131
+ }
19005
19132
  }
19006
19133
  // ------------------------------------------------------------------
19007
19134
  // Internal: WebSocket handler