@smplkit/sdk 1.5.7 → 1.6.0

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
@@ -17097,11 +17097,11 @@ var ConfigClient = class {
17097
17097
  _initialized = false;
17098
17098
  _listeners = [];
17099
17099
  /** @internal */
17100
- constructor(apiKey, timeout) {
17100
+ constructor(apiKey, timeout, baseUrl) {
17101
17101
  this._apiKey = apiKey;
17102
17102
  const ms = timeout ?? 3e4;
17103
17103
  this._http = (0, import_openapi_fetch.default)({
17104
- baseUrl: BASE_URL,
17104
+ baseUrl: baseUrl ?? BASE_URL,
17105
17105
  headers: {
17106
17106
  Authorization: `Bearer ${apiKey}`,
17107
17107
  Accept: "application/json"
@@ -17816,7 +17816,7 @@ var FlagsClient = class {
17816
17816
  /** Management API — CRUD operations on Flag models. */
17817
17817
  management;
17818
17818
  /** @internal */
17819
- constructor(apiKey, ensureWs, timeout) {
17819
+ constructor(apiKey, ensureWs, timeout, flagsBaseUrl, appBaseUrl) {
17820
17820
  this._apiKey = apiKey;
17821
17821
  this._ensureWs = ensureWs;
17822
17822
  const ms = timeout ?? 3e4;
@@ -17835,7 +17835,7 @@ var FlagsClient = class {
17835
17835
  }
17836
17836
  };
17837
17837
  this._http = (0, import_openapi_fetch2.default)({
17838
- baseUrl: FLAGS_BASE_URL,
17838
+ baseUrl: flagsBaseUrl ?? FLAGS_BASE_URL,
17839
17839
  headers: {
17840
17840
  Authorization: `Bearer ${apiKey}`,
17841
17841
  Accept: "application/json"
@@ -17843,7 +17843,7 @@ var FlagsClient = class {
17843
17843
  fetch: fetchWithTimeout
17844
17844
  });
17845
17845
  this._appHttp = (0, import_openapi_fetch2.default)({
17846
- baseUrl: APP_BASE_URL,
17846
+ baseUrl: appBaseUrl ?? APP_BASE_URL,
17847
17847
  headers: {
17848
17848
  Authorization: `Bearer ${apiKey}`,
17849
17849
  Accept: "application/json"
@@ -18651,6 +18651,26 @@ var LogGroup = class {
18651
18651
 
18652
18652
  // src/logging/client.ts
18653
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
+ };
18654
18674
  async function checkError3(response, _context) {
18655
18675
  const body = await response.text().catch(() => "");
18656
18676
  throwForStatus(response.status, body);
@@ -18721,13 +18741,15 @@ var LoggingClient = class {
18721
18741
  _keyListeners = /* @__PURE__ */ new Map();
18722
18742
  _adapters = [];
18723
18743
  _explicitAdapters = false;
18744
+ _loggerBuffer = new LoggerRegistrationBuffer();
18745
+ _loggerFlushTimer = null;
18724
18746
  /** @internal */
18725
- constructor(apiKey, ensureWs, timeout) {
18747
+ constructor(apiKey, ensureWs, timeout, baseUrl) {
18726
18748
  this._apiKey = apiKey;
18727
18749
  this._ensureWs = ensureWs;
18728
18750
  const ms = timeout ?? 3e4;
18729
18751
  this._http = (0, import_openapi_fetch3.default)({
18730
- baseUrl: LOGGING_BASE_URL,
18752
+ baseUrl: baseUrl ?? LOGGING_BASE_URL,
18731
18753
  headers: {
18732
18754
  Authorization: `Bearer ${apiKey}`,
18733
18755
  Accept: "application/json"
@@ -18966,15 +18988,20 @@ var LoggingClient = class {
18966
18988
  if (!this._explicitAdapters) {
18967
18989
  this._adapters = this._autoLoadAdapters();
18968
18990
  }
18969
- const discovered = [];
18991
+ const service = this._parent?._service ?? null;
18992
+ const environment = this._parent?._environment ?? null;
18993
+ let discoveredCount = 0;
18970
18994
  for (const adapter of this._adapters) {
18971
18995
  try {
18972
18996
  const loggers = adapter.discover();
18973
- discovered.push(...loggers);
18997
+ for (const { name, level } of loggers) {
18998
+ this._loggerBuffer.add(name, level, level, service, environment);
18999
+ discoveredCount++;
19000
+ }
18974
19001
  } catch {
18975
19002
  }
18976
19003
  }
18977
- debug("discovery", `discovered ${discovered.length} logger(s) from adapters`);
19004
+ debug("discovery", `discovered ${discoveredCount} logger(s) from adapters`);
18978
19005
  for (const adapter of this._adapters) {
18979
19006
  try {
18980
19007
  adapter.installHook((name, level) => {
@@ -18984,39 +19011,13 @@ var LoggingClient = class {
18984
19011
  } catch {
18985
19012
  }
18986
19013
  }
18987
- if (discovered.length > 0) {
19014
+ if (discoveredCount > 0) {
18988
19015
  const metrics = this._parent?._metrics;
18989
19016
  if (metrics) {
18990
- metrics.record("logging.loggers_discovered", discovered.length, "loggers");
18991
- }
18992
- }
18993
- if (discovered.length > 0) {
18994
- const service = this._parent?._service ?? null;
18995
- const environment = this._parent?._environment ?? null;
18996
- const loggers = discovered.map(
18997
- ({ name, level }) => ({
18998
- id: name,
18999
- // For Winston/Pino there is no inherited-null distinction — both fields carry the same value.
19000
- level,
19001
- resolved_level: level,
19002
- service: service ?? void 0,
19003
- environment: environment ?? void 0
19004
- })
19005
- );
19006
- debug("registration", `flushing ${loggers.length} logger(s) to bulk-register endpoint`);
19007
- try {
19008
- const result = await this._http.POST("/api/v1/loggers/bulk", {
19009
- body: { loggers }
19010
- });
19011
- if (result.error !== void 0)
19012
- await checkError3(result.response, "Failed to bulk-register loggers");
19013
- debug("registration", `bulk-register complete (${loggers.length} logger(s))`);
19014
- } catch (err) {
19015
- console.warn(
19016
- `[smplkit] Failed to bulk-register loggers: ${err instanceof Error ? err.message : String(err)}`
19017
- );
19017
+ metrics.record("logging.loggers_discovered", discoveredCount, "loggers");
19018
19018
  }
19019
19019
  }
19020
+ await this._flushLoggerBuffer();
19020
19021
  debug("resolution", `starting resolution pass (trigger: start())`);
19021
19022
  try {
19022
19023
  const [serverLoggers, serverGroups] = await Promise.all([
@@ -19035,6 +19036,9 @@ var LoggingClient = class {
19035
19036
  this._wsManager.on("logger_deleted", this._handleLoggerChanged);
19036
19037
  this._wsManager.on("group_changed", this._handleGroupChanged);
19037
19038
  this._wsManager.on("group_deleted", this._handleGroupChanged);
19039
+ this._loggerFlushTimer = setInterval(() => {
19040
+ void this._flushLoggerBuffer();
19041
+ }, 3e4);
19038
19042
  this._started = true;
19039
19043
  }
19040
19044
  // ------------------------------------------------------------------
@@ -19066,6 +19070,10 @@ var LoggingClient = class {
19066
19070
  /** @internal */
19067
19071
  _close() {
19068
19072
  debug("lifecycle", "LoggingClient._close() called");
19073
+ if (this._loggerFlushTimer !== null) {
19074
+ clearInterval(this._loggerFlushTimer);
19075
+ this._loggerFlushTimer = null;
19076
+ }
19069
19077
  for (const adapter of this._adapters) {
19070
19078
  try {
19071
19079
  adapter.uninstallHook();
@@ -19134,12 +19142,34 @@ var LoggingClient = class {
19134
19142
  }
19135
19143
  }
19136
19144
  /** Called by adapter hooks when a new logger is created in the framework. */
19137
- _onAdapterNewLogger(_name, _level) {
19138
- debug("discovery", `new logger intercepted at runtime: ${_name}`);
19139
- const logger = this.management.new(_name, { managed: true });
19140
- logger.setLevel(_level);
19141
- logger.save().catch(() => {
19142
- });
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
+ }
19143
19173
  }
19144
19174
  // ------------------------------------------------------------------
19145
19175
  // Internal: WebSocket handler
@@ -19461,6 +19491,7 @@ var MetricsReporter = class {
19461
19491
  _environment;
19462
19492
  _service;
19463
19493
  _flushInterval;
19494
+ _appBaseUrl;
19464
19495
  _counters = /* @__PURE__ */ new Map();
19465
19496
  _gauges = /* @__PURE__ */ new Map();
19466
19497
  _timer = null;
@@ -19470,6 +19501,7 @@ var MetricsReporter = class {
19470
19501
  this._environment = options.environment;
19471
19502
  this._service = options.service;
19472
19503
  this._flushInterval = options.flushInterval ?? 60;
19504
+ this._appBaseUrl = options.appBaseUrl ?? APP_BASE_URL2;
19473
19505
  }
19474
19506
  // ------------------------------------------------------------------
19475
19507
  // Public recording API
@@ -19546,7 +19578,7 @@ var MetricsReporter = class {
19546
19578
  if (counters.size === 0 && gauges.size === 0) return;
19547
19579
  const payload = this._buildPayload(counters, gauges);
19548
19580
  try {
19549
- fetch(`${APP_BASE_URL2}/api/v1/metrics/bulk`, {
19581
+ fetch(`${this._appBaseUrl}/api/v1/metrics/bulk`, {
19550
19582
  method: "POST",
19551
19583
  headers: {
19552
19584
  Authorization: `Bearer ${this._apiKey}`,
@@ -19585,7 +19617,6 @@ var MetricsReporter = class {
19585
19617
  };
19586
19618
 
19587
19619
  // src/client.ts
19588
- var APP_BASE_URL3 = "https://app.smplkit.com";
19589
19620
  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";
19590
19621
  var NO_SERVICE_MESSAGE = "No service provided. Set one of:\n 1. Pass service in options\n 2. Set the SMPLKIT_SERVICE environment variable";
19591
19622
  var SmplClient = class {
@@ -19604,6 +19635,7 @@ var SmplClient = class {
19604
19635
  /** @internal */
19605
19636
  _metrics = null;
19606
19637
  _timeout;
19638
+ _appBaseUrl;
19607
19639
  _appHttp;
19608
19640
  constructor(options = {}) {
19609
19641
  const environment = options.environment || process.env.SMPLKIT_ENVIRONMENT;
@@ -19619,13 +19651,20 @@ var SmplClient = class {
19619
19651
  const apiKey = resolveApiKey(options.apiKey, environment);
19620
19652
  this._apiKey = apiKey;
19621
19653
  this._timeout = options.timeout ?? 3e4;
19654
+ const baseDomain = options.baseDomain ?? "smplkit.com";
19655
+ const scheme = options.scheme ?? "https";
19656
+ const appBaseUrl = `${scheme}://app.${baseDomain}`;
19657
+ const configBaseUrl = `${scheme}://config.${baseDomain}`;
19658
+ const flagsBaseUrl = `${scheme}://flags.${baseDomain}`;
19659
+ const loggingBaseUrl = `${scheme}://logging.${baseDomain}`;
19660
+ this._appBaseUrl = appBaseUrl;
19622
19661
  const maskedKey = apiKey.length > 14 ? apiKey.slice(0, 10) + "..." + apiKey.slice(-4) : apiKey.slice(0, Math.min(4, apiKey.length)) + "...";
19623
19662
  debug(
19624
19663
  "lifecycle",
19625
19664
  `SmplClient created (api_key=${maskedKey}, environment=${environment}, service=${service})`
19626
19665
  );
19627
19666
  this._appHttp = (0, import_openapi_fetch4.default)({
19628
- baseUrl: APP_BASE_URL3,
19667
+ baseUrl: appBaseUrl,
19629
19668
  headers: {
19630
19669
  Authorization: `Bearer ${apiKey}`,
19631
19670
  Accept: "application/json"
@@ -19635,12 +19674,19 @@ var SmplClient = class {
19635
19674
  this._metrics = new MetricsReporter({
19636
19675
  apiKey,
19637
19676
  environment: this._environment,
19638
- service: this._service
19677
+ service: this._service,
19678
+ appBaseUrl
19639
19679
  });
19640
19680
  }
19641
- this.config = new ConfigClient(apiKey, this._timeout);
19642
- this.flags = new FlagsClient(apiKey, () => this._ensureWs(), this._timeout);
19643
- this.logging = new LoggingClient(apiKey, () => this._ensureWs(), this._timeout);
19681
+ this.config = new ConfigClient(apiKey, this._timeout, configBaseUrl);
19682
+ this.flags = new FlagsClient(
19683
+ apiKey,
19684
+ () => this._ensureWs(),
19685
+ this._timeout,
19686
+ flagsBaseUrl,
19687
+ appBaseUrl
19688
+ );
19689
+ this.logging = new LoggingClient(apiKey, () => this._ensureWs(), this._timeout, loggingBaseUrl);
19644
19690
  this.config._getSharedWs = () => this._ensureWs();
19645
19691
  this.flags._parent = this;
19646
19692
  this.config._parent = this;
@@ -19671,7 +19717,7 @@ var SmplClient = class {
19671
19717
  /** Lazily create and start the shared WebSocket. @internal */
19672
19718
  _ensureWs() {
19673
19719
  if (this._wsManager === null) {
19674
- this._wsManager = new SharedWebSocket(APP_BASE_URL3, this._apiKey, this._metrics);
19720
+ this._wsManager = new SharedWebSocket(this._appBaseUrl, this._apiKey, this._metrics);
19675
19721
  this._wsManager.start();
19676
19722
  }
19677
19723
  return this._wsManager;