@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.cjs CHANGED
@@ -17579,6 +17579,8 @@ var APP_BASE_URL = "https://app.smplkit.com";
17579
17579
  var CACHE_MAX_SIZE = 1e4;
17580
17580
  var CONTEXT_REGISTRATION_LRU_SIZE = 1e4;
17581
17581
  var CONTEXT_BATCH_FLUSH_SIZE = 100;
17582
+ var FLAG_REGISTRATION_FLUSH_SIZE = 50;
17583
+ var FLAG_REGISTRATION_FLUSH_INTERVAL_MS = 3e4;
17582
17584
  async function checkError2(response, _context) {
17583
17585
  const body = await response.text().catch(() => "");
17584
17586
  throwForStatus(response.status, body);
@@ -17728,6 +17730,30 @@ var ContextRegistrationBuffer = class {
17728
17730
  return this._pending.length;
17729
17731
  }
17730
17732
  };
17733
+ var FlagRegistrationBuffer = class {
17734
+ _seen = /* @__PURE__ */ new Set();
17735
+ _pending = [];
17736
+ add(id, type, defaultValue, service, environment) {
17737
+ if (!this._seen.has(id)) {
17738
+ this._seen.add(id);
17739
+ this._pending.push({
17740
+ id,
17741
+ type,
17742
+ default: defaultValue,
17743
+ service: service ?? void 0,
17744
+ environment: environment ?? void 0
17745
+ });
17746
+ }
17747
+ }
17748
+ drain() {
17749
+ const batch = this._pending;
17750
+ this._pending = [];
17751
+ return batch;
17752
+ }
17753
+ get pendingCount() {
17754
+ return this._pending.length;
17755
+ }
17756
+ };
17731
17757
  var FlagsManagement = class {
17732
17758
  constructor(_client) {
17733
17759
  this._client = _client;
@@ -17777,6 +17803,8 @@ var FlagsClient = class {
17777
17803
  _cache = new ResolutionCache();
17778
17804
  _contextProvider = null;
17779
17805
  _contextBuffer = new ContextRegistrationBuffer();
17806
+ _flagBuffer = new FlagRegistrationBuffer();
17807
+ _flagFlushTimer = null;
17780
17808
  _handles = {};
17781
17809
  _globalListeners = [];
17782
17810
  _keyListeners = /* @__PURE__ */ new Map();
@@ -18004,6 +18032,16 @@ var FlagsClient = class {
18004
18032
  updatedAt: null
18005
18033
  });
18006
18034
  this._handles[id] = handle;
18035
+ this._flagBuffer.add(
18036
+ id,
18037
+ "BOOLEAN",
18038
+ defaultValue,
18039
+ this._parent?._service ?? null,
18040
+ this._parent?._environment ?? null
18041
+ );
18042
+ if (this._flagBuffer.pendingCount >= FLAG_REGISTRATION_FLUSH_SIZE) {
18043
+ void this._flushFlags();
18044
+ }
18007
18045
  return handle;
18008
18046
  }
18009
18047
  /** Declare a string flag handle for runtime evaluation. */
@@ -18020,6 +18058,16 @@ var FlagsClient = class {
18020
18058
  updatedAt: null
18021
18059
  });
18022
18060
  this._handles[id] = handle;
18061
+ this._flagBuffer.add(
18062
+ id,
18063
+ "STRING",
18064
+ defaultValue,
18065
+ this._parent?._service ?? null,
18066
+ this._parent?._environment ?? null
18067
+ );
18068
+ if (this._flagBuffer.pendingCount >= FLAG_REGISTRATION_FLUSH_SIZE) {
18069
+ void this._flushFlags();
18070
+ }
18023
18071
  return handle;
18024
18072
  }
18025
18073
  /** Declare a numeric flag handle for runtime evaluation. */
@@ -18036,6 +18084,16 @@ var FlagsClient = class {
18036
18084
  updatedAt: null
18037
18085
  });
18038
18086
  this._handles[id] = handle;
18087
+ this._flagBuffer.add(
18088
+ id,
18089
+ "NUMERIC",
18090
+ defaultValue,
18091
+ this._parent?._service ?? null,
18092
+ this._parent?._environment ?? null
18093
+ );
18094
+ if (this._flagBuffer.pendingCount >= FLAG_REGISTRATION_FLUSH_SIZE) {
18095
+ void this._flushFlags();
18096
+ }
18039
18097
  return handle;
18040
18098
  }
18041
18099
  /** Declare a JSON flag handle for runtime evaluation. */
@@ -18052,6 +18110,16 @@ var FlagsClient = class {
18052
18110
  updatedAt: null
18053
18111
  });
18054
18112
  this._handles[id] = handle;
18113
+ this._flagBuffer.add(
18114
+ id,
18115
+ "JSON",
18116
+ defaultValue,
18117
+ this._parent?._service ?? null,
18118
+ this._parent?._environment ?? null
18119
+ );
18120
+ if (this._flagBuffer.pendingCount >= FLAG_REGISTRATION_FLUSH_SIZE) {
18121
+ void this._flushFlags();
18122
+ }
18055
18123
  return handle;
18056
18124
  }
18057
18125
  // ------------------------------------------------------------------
@@ -18085,12 +18153,16 @@ var FlagsClient = class {
18085
18153
  if (this._initialized) return;
18086
18154
  debug("lifecycle", "FlagsClient.initialize() called");
18087
18155
  this._environment = this._parent?._environment ?? null;
18156
+ await this._flushFlags();
18088
18157
  await this._fetchAllFlags();
18089
18158
  this._initialized = true;
18090
18159
  this._cache.clear();
18091
18160
  this._wsManager = this._ensureWs();
18092
18161
  this._wsManager.on("flag_changed", this._handleFlagChanged);
18093
18162
  this._wsManager.on("flag_deleted", this._handleFlagDeleted);
18163
+ this._flagFlushTimer = setInterval(() => {
18164
+ void this._flushFlags();
18165
+ }, FLAG_REGISTRATION_FLUSH_INTERVAL_MS);
18094
18166
  }
18095
18167
  /** Disconnect the flags runtime and release resources. */
18096
18168
  async disconnect() {
@@ -18099,6 +18171,10 @@ var FlagsClient = class {
18099
18171
  this._wsManager.off("flag_deleted", this._handleFlagDeleted);
18100
18172
  this._wsManager = null;
18101
18173
  }
18174
+ if (this._flagFlushTimer !== null) {
18175
+ clearInterval(this._flagFlushTimer);
18176
+ this._flagFlushTimer = null;
18177
+ }
18102
18178
  await this._flushContexts();
18103
18179
  this._flagStore = {};
18104
18180
  this._cache.clear();
@@ -18250,12 +18326,16 @@ var FlagsClient = class {
18250
18326
  /** @internal — called by SmplClient constructor / lazy init. */
18251
18327
  async _connectInternal(environment) {
18252
18328
  this._environment = environment;
18329
+ await this._flushFlags();
18253
18330
  await this._fetchAllFlags();
18254
18331
  this._initialized = true;
18255
18332
  this._cache.clear();
18256
18333
  this._wsManager = this._ensureWs();
18257
18334
  this._wsManager.on("flag_changed", this._handleFlagChanged);
18258
18335
  this._wsManager.on("flag_deleted", this._handleFlagDeleted);
18336
+ this._flagFlushTimer = setInterval(() => {
18337
+ void this._flushFlags();
18338
+ }, FLAG_REGISTRATION_FLUSH_INTERVAL_MS);
18259
18339
  }
18260
18340
  // ------------------------------------------------------------------
18261
18341
  // Internal: event handlers (called by SharedWebSocket)
@@ -18331,6 +18411,23 @@ var FlagsClient = class {
18331
18411
  }
18332
18412
  }
18333
18413
  // ------------------------------------------------------------------
18414
+ // Internal: flag registration flush
18415
+ // ------------------------------------------------------------------
18416
+ async _flushFlags() {
18417
+ const batch = this._flagBuffer.drain();
18418
+ if (batch.length === 0) return;
18419
+ debug("registration", `flushing ${batch.length} flag(s) to bulk-register endpoint`);
18420
+ try {
18421
+ await this._http.POST("/api/v1/flags/bulk", {
18422
+ body: { flags: batch }
18423
+ });
18424
+ } catch (err) {
18425
+ console.warn(
18426
+ `[smplkit] Failed to bulk-register flags: ${err instanceof Error ? err.message : String(err)}`
18427
+ );
18428
+ }
18429
+ }
18430
+ // ------------------------------------------------------------------
18334
18431
  // Internal: context flush
18335
18432
  // ------------------------------------------------------------------
18336
18433
  async _flushContexts() {
@@ -18554,6 +18651,26 @@ var LogGroup = class {
18554
18651
 
18555
18652
  // src/logging/client.ts
18556
18653
  var LOGGING_BASE_URL = "https://logging.smplkit.com";
18654
+ var LoggerRegistrationBuffer = class {
18655
+ _seen = /* @__PURE__ */ new Set();
18656
+ _pending = [];
18657
+ add(id, level, resolvedLevel, service, environment) {
18658
+ if (this._seen.has(id)) return;
18659
+ this._seen.add(id);
18660
+ const item = { id, level, resolved_level: resolvedLevel };
18661
+ if (service) item.service = service;
18662
+ if (environment) item.environment = environment;
18663
+ this._pending.push(item);
18664
+ }
18665
+ drain() {
18666
+ const batch = this._pending;
18667
+ this._pending = [];
18668
+ return batch;
18669
+ }
18670
+ get pendingCount() {
18671
+ return this._pending.length;
18672
+ }
18673
+ };
18557
18674
  async function checkError3(response, _context) {
18558
18675
  const body = await response.text().catch(() => "");
18559
18676
  throwForStatus(response.status, body);
@@ -18624,6 +18741,8 @@ var LoggingClient = class {
18624
18741
  _keyListeners = /* @__PURE__ */ new Map();
18625
18742
  _adapters = [];
18626
18743
  _explicitAdapters = false;
18744
+ _loggerBuffer = new LoggerRegistrationBuffer();
18745
+ _loggerFlushTimer = null;
18627
18746
  /** @internal */
18628
18747
  constructor(apiKey, ensureWs, timeout) {
18629
18748
  this._apiKey = apiKey;
@@ -18869,15 +18988,20 @@ var LoggingClient = class {
18869
18988
  if (!this._explicitAdapters) {
18870
18989
  this._adapters = this._autoLoadAdapters();
18871
18990
  }
18872
- const discovered = [];
18991
+ const service = this._parent?._service ?? null;
18992
+ const environment = this._parent?._environment ?? null;
18993
+ let discoveredCount = 0;
18873
18994
  for (const adapter of this._adapters) {
18874
18995
  try {
18875
18996
  const loggers = adapter.discover();
18876
- discovered.push(...loggers);
18997
+ for (const { name, level } of loggers) {
18998
+ this._loggerBuffer.add(name, level, level, service, environment);
18999
+ discoveredCount++;
19000
+ }
18877
19001
  } catch {
18878
19002
  }
18879
19003
  }
18880
- debug("discovery", `discovered ${discovered.length} logger(s) from adapters`);
19004
+ debug("discovery", `discovered ${discoveredCount} logger(s) from adapters`);
18881
19005
  for (const adapter of this._adapters) {
18882
19006
  try {
18883
19007
  adapter.installHook((name, level) => {
@@ -18887,39 +19011,13 @@ var LoggingClient = class {
18887
19011
  } catch {
18888
19012
  }
18889
19013
  }
18890
- if (discovered.length > 0) {
19014
+ if (discoveredCount > 0) {
18891
19015
  const metrics = this._parent?._metrics;
18892
19016
  if (metrics) {
18893
- metrics.record("logging.loggers_discovered", discovered.length, "loggers");
18894
- }
18895
- }
18896
- if (discovered.length > 0) {
18897
- const service = this._parent?._service ?? null;
18898
- const environment = this._parent?._environment ?? null;
18899
- const loggers = discovered.map(
18900
- ({ name, level }) => ({
18901
- id: name,
18902
- // For Winston/Pino there is no inherited-null distinction — both fields carry the same value.
18903
- level,
18904
- resolved_level: level,
18905
- service: service ?? void 0,
18906
- environment: environment ?? void 0
18907
- })
18908
- );
18909
- debug("registration", `flushing ${loggers.length} logger(s) to bulk-register endpoint`);
18910
- try {
18911
- const result = await this._http.POST("/api/v1/loggers/bulk", {
18912
- body: { loggers }
18913
- });
18914
- if (result.error !== void 0)
18915
- await checkError3(result.response, "Failed to bulk-register loggers");
18916
- debug("registration", `bulk-register complete (${loggers.length} logger(s))`);
18917
- } catch (err) {
18918
- console.warn(
18919
- `[smplkit] Failed to bulk-register loggers: ${err instanceof Error ? err.message : String(err)}`
18920
- );
19017
+ metrics.record("logging.loggers_discovered", discoveredCount, "loggers");
18921
19018
  }
18922
19019
  }
19020
+ await this._flushLoggerBuffer();
18923
19021
  debug("resolution", `starting resolution pass (trigger: start())`);
18924
19022
  try {
18925
19023
  const [serverLoggers, serverGroups] = await Promise.all([
@@ -18938,6 +19036,9 @@ var LoggingClient = class {
18938
19036
  this._wsManager.on("logger_deleted", this._handleLoggerChanged);
18939
19037
  this._wsManager.on("group_changed", this._handleGroupChanged);
18940
19038
  this._wsManager.on("group_deleted", this._handleGroupChanged);
19039
+ this._loggerFlushTimer = setInterval(() => {
19040
+ void this._flushLoggerBuffer();
19041
+ }, 3e4);
18941
19042
  this._started = true;
18942
19043
  }
18943
19044
  // ------------------------------------------------------------------
@@ -18969,6 +19070,10 @@ var LoggingClient = class {
18969
19070
  /** @internal */
18970
19071
  _close() {
18971
19072
  debug("lifecycle", "LoggingClient._close() called");
19073
+ if (this._loggerFlushTimer !== null) {
19074
+ clearInterval(this._loggerFlushTimer);
19075
+ this._loggerFlushTimer = null;
19076
+ }
18972
19077
  for (const adapter of this._adapters) {
18973
19078
  try {
18974
19079
  adapter.uninstallHook();
@@ -19037,12 +19142,34 @@ var LoggingClient = class {
19037
19142
  }
19038
19143
  }
19039
19144
  /** Called by adapter hooks when a new logger is created in the framework. */
19040
- _onAdapterNewLogger(_name, _level) {
19041
- debug("discovery", `new logger intercepted at runtime: ${_name}`);
19042
- const logger = this.management.new(_name, { managed: true });
19043
- logger.setLevel(_level);
19044
- logger.save().catch(() => {
19045
- });
19145
+ _onAdapterNewLogger(name, level) {
19146
+ debug("discovery", `new logger intercepted at runtime: ${name}`);
19147
+ const service = this._parent?._service ?? null;
19148
+ const environment = this._parent?._environment ?? null;
19149
+ this._loggerBuffer.add(name, level, level, service, environment);
19150
+ if (this._loggerBuffer.pendingCount >= 50) {
19151
+ void this._flushLoggerBuffer();
19152
+ }
19153
+ }
19154
+ /** Flush buffered loggers to the bulk-register endpoint. */
19155
+ async _flushLoggerBuffer() {
19156
+ const batch = this._loggerBuffer.drain();
19157
+ if (batch.length === 0) return;
19158
+ debug("registration", `flushing ${batch.length} logger(s) to bulk-register endpoint`);
19159
+ try {
19160
+ const result = await this._http.POST("/api/v1/loggers/bulk", {
19161
+ body: { loggers: batch }
19162
+ });
19163
+ if (result.error !== void 0) {
19164
+ console.warn("[smplkit] Logger bulk registration failed");
19165
+ } else {
19166
+ debug("registration", `bulk-register complete (${batch.length} logger(s))`);
19167
+ }
19168
+ } catch (err) {
19169
+ console.warn(
19170
+ `[smplkit] Logger bulk registration failed: ${err instanceof Error ? err.message : String(err)}`
19171
+ );
19172
+ }
19046
19173
  }
19047
19174
  // ------------------------------------------------------------------
19048
19175
  // Internal: WebSocket handler