@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.d.cts CHANGED
@@ -13,6 +13,7 @@ declare class MetricsReporter {
13
13
  private readonly _environment;
14
14
  private readonly _service;
15
15
  private readonly _flushInterval;
16
+ private readonly _appBaseUrl;
16
17
  private _counters;
17
18
  private _gauges;
18
19
  private _timer;
@@ -22,6 +23,7 @@ declare class MetricsReporter {
22
23
  environment: string;
23
24
  service: string;
24
25
  flushInterval?: number;
26
+ appBaseUrl?: string;
25
27
  });
26
28
  record(name: string, value?: number, unit?: string | null, dimensions?: Record<string, string>): void;
27
29
  recordGauge(name: string, value: number, unit?: string | null, dimensions?: Record<string, string>): void;
@@ -223,7 +225,7 @@ declare class ConfigClient {
223
225
  private _initialized;
224
226
  private _listeners;
225
227
  /** @internal */
226
- constructor(apiKey: string, timeout?: number);
228
+ constructor(apiKey: string, timeout?: number, baseUrl?: string);
227
229
  /** @internal */
228
230
  _mgNew(id: string, options?: {
229
231
  name?: string;
@@ -922,7 +924,7 @@ declare class FlagsClient {
922
924
  /** Management API — CRUD operations on Flag models. */
923
925
  readonly management: FlagsManagement;
924
926
  /** @internal */
925
- constructor(apiKey: string, ensureWs: () => SharedWebSocket, timeout?: number);
927
+ constructor(apiKey: string, ensureWs: () => SharedWebSocket, timeout?: number, flagsBaseUrl?: string, appBaseUrl?: string);
926
928
  /** @internal */
927
929
  _mgNewBooleanFlag(id: string, options: {
928
930
  default: boolean;
@@ -1270,8 +1272,10 @@ declare class LoggingClient {
1270
1272
  private _keyListeners;
1271
1273
  private _adapters;
1272
1274
  private _explicitAdapters;
1275
+ private _loggerBuffer;
1276
+ private _loggerFlushTimer;
1273
1277
  /** @internal */
1274
- constructor(apiKey: string, ensureWs: () => SharedWebSocket, timeout?: number);
1278
+ constructor(apiKey: string, ensureWs: () => SharedWebSocket, timeout?: number, baseUrl?: string);
1275
1279
  /**
1276
1280
  * Register a logging framework adapter.
1277
1281
  *
@@ -1328,6 +1332,8 @@ declare class LoggingClient {
1328
1332
  private _applyLevels;
1329
1333
  /** Called by adapter hooks when a new logger is created in the framework. */
1330
1334
  private _onAdapterNewLogger;
1335
+ /** Flush buffered loggers to the bulk-register endpoint. */
1336
+ private _flushLoggerBuffer;
1331
1337
  private _handleLoggerChanged;
1332
1338
  private _handleGroupChanged;
1333
1339
  private _loggerToModel;
@@ -1371,6 +1377,18 @@ interface SmplClientOptions {
1371
1377
  * @default false
1372
1378
  */
1373
1379
  disableTelemetry?: boolean;
1380
+ /**
1381
+ * Base domain for all smplkit service URLs.
1382
+ * Override for local development (e.g. `"localhost"`).
1383
+ * @default "smplkit.com"
1384
+ */
1385
+ baseDomain?: string;
1386
+ /**
1387
+ * URL scheme used when constructing service URLs.
1388
+ * Override for local development (e.g. `"http"`).
1389
+ * @default "https"
1390
+ */
1391
+ scheme?: string;
1374
1392
  }
1375
1393
  /**
1376
1394
  * Entry point for the smplkit TypeScript SDK.
@@ -1410,6 +1428,7 @@ declare class SmplClient {
1410
1428
  /** @internal */
1411
1429
  readonly _metrics: MetricsReporter | null;
1412
1430
  private readonly _timeout;
1431
+ private readonly _appBaseUrl;
1413
1432
  private readonly _appHttp;
1414
1433
  constructor(options?: SmplClientOptions);
1415
1434
  /** @internal */
package/dist/index.d.ts CHANGED
@@ -13,6 +13,7 @@ declare class MetricsReporter {
13
13
  private readonly _environment;
14
14
  private readonly _service;
15
15
  private readonly _flushInterval;
16
+ private readonly _appBaseUrl;
16
17
  private _counters;
17
18
  private _gauges;
18
19
  private _timer;
@@ -22,6 +23,7 @@ declare class MetricsReporter {
22
23
  environment: string;
23
24
  service: string;
24
25
  flushInterval?: number;
26
+ appBaseUrl?: string;
25
27
  });
26
28
  record(name: string, value?: number, unit?: string | null, dimensions?: Record<string, string>): void;
27
29
  recordGauge(name: string, value: number, unit?: string | null, dimensions?: Record<string, string>): void;
@@ -223,7 +225,7 @@ declare class ConfigClient {
223
225
  private _initialized;
224
226
  private _listeners;
225
227
  /** @internal */
226
- constructor(apiKey: string, timeout?: number);
228
+ constructor(apiKey: string, timeout?: number, baseUrl?: string);
227
229
  /** @internal */
228
230
  _mgNew(id: string, options?: {
229
231
  name?: string;
@@ -922,7 +924,7 @@ declare class FlagsClient {
922
924
  /** Management API — CRUD operations on Flag models. */
923
925
  readonly management: FlagsManagement;
924
926
  /** @internal */
925
- constructor(apiKey: string, ensureWs: () => SharedWebSocket, timeout?: number);
927
+ constructor(apiKey: string, ensureWs: () => SharedWebSocket, timeout?: number, flagsBaseUrl?: string, appBaseUrl?: string);
926
928
  /** @internal */
927
929
  _mgNewBooleanFlag(id: string, options: {
928
930
  default: boolean;
@@ -1270,8 +1272,10 @@ declare class LoggingClient {
1270
1272
  private _keyListeners;
1271
1273
  private _adapters;
1272
1274
  private _explicitAdapters;
1275
+ private _loggerBuffer;
1276
+ private _loggerFlushTimer;
1273
1277
  /** @internal */
1274
- constructor(apiKey: string, ensureWs: () => SharedWebSocket, timeout?: number);
1278
+ constructor(apiKey: string, ensureWs: () => SharedWebSocket, timeout?: number, baseUrl?: string);
1275
1279
  /**
1276
1280
  * Register a logging framework adapter.
1277
1281
  *
@@ -1328,6 +1332,8 @@ declare class LoggingClient {
1328
1332
  private _applyLevels;
1329
1333
  /** Called by adapter hooks when a new logger is created in the framework. */
1330
1334
  private _onAdapterNewLogger;
1335
+ /** Flush buffered loggers to the bulk-register endpoint. */
1336
+ private _flushLoggerBuffer;
1331
1337
  private _handleLoggerChanged;
1332
1338
  private _handleGroupChanged;
1333
1339
  private _loggerToModel;
@@ -1371,6 +1377,18 @@ interface SmplClientOptions {
1371
1377
  * @default false
1372
1378
  */
1373
1379
  disableTelemetry?: boolean;
1380
+ /**
1381
+ * Base domain for all smplkit service URLs.
1382
+ * Override for local development (e.g. `"localhost"`).
1383
+ * @default "smplkit.com"
1384
+ */
1385
+ baseDomain?: string;
1386
+ /**
1387
+ * URL scheme used when constructing service URLs.
1388
+ * Override for local development (e.g. `"http"`).
1389
+ * @default "https"
1390
+ */
1391
+ scheme?: string;
1374
1392
  }
1375
1393
  /**
1376
1394
  * Entry point for the smplkit TypeScript SDK.
@@ -1410,6 +1428,7 @@ declare class SmplClient {
1410
1428
  /** @internal */
1411
1429
  readonly _metrics: MetricsReporter | null;
1412
1430
  private readonly _timeout;
1431
+ private readonly _appBaseUrl;
1413
1432
  private readonly _appHttp;
1414
1433
  constructor(options?: SmplClientOptions);
1415
1434
  /** @internal */
package/dist/index.js CHANGED
@@ -17056,11 +17056,11 @@ var ConfigClient = class {
17056
17056
  _initialized = false;
17057
17057
  _listeners = [];
17058
17058
  /** @internal */
17059
- constructor(apiKey, timeout) {
17059
+ constructor(apiKey, timeout, baseUrl) {
17060
17060
  this._apiKey = apiKey;
17061
17061
  const ms = timeout ?? 3e4;
17062
17062
  this._http = createClient({
17063
- baseUrl: BASE_URL,
17063
+ baseUrl: baseUrl ?? BASE_URL,
17064
17064
  headers: {
17065
17065
  Authorization: `Bearer ${apiKey}`,
17066
17066
  Accept: "application/json"
@@ -17775,7 +17775,7 @@ var FlagsClient = class {
17775
17775
  /** Management API — CRUD operations on Flag models. */
17776
17776
  management;
17777
17777
  /** @internal */
17778
- constructor(apiKey, ensureWs, timeout) {
17778
+ constructor(apiKey, ensureWs, timeout, flagsBaseUrl, appBaseUrl) {
17779
17779
  this._apiKey = apiKey;
17780
17780
  this._ensureWs = ensureWs;
17781
17781
  const ms = timeout ?? 3e4;
@@ -17794,7 +17794,7 @@ var FlagsClient = class {
17794
17794
  }
17795
17795
  };
17796
17796
  this._http = createClient2({
17797
- baseUrl: FLAGS_BASE_URL,
17797
+ baseUrl: flagsBaseUrl ?? FLAGS_BASE_URL,
17798
17798
  headers: {
17799
17799
  Authorization: `Bearer ${apiKey}`,
17800
17800
  Accept: "application/json"
@@ -17802,7 +17802,7 @@ var FlagsClient = class {
17802
17802
  fetch: fetchWithTimeout
17803
17803
  });
17804
17804
  this._appHttp = createClient2({
17805
- baseUrl: APP_BASE_URL,
17805
+ baseUrl: appBaseUrl ?? APP_BASE_URL,
17806
17806
  headers: {
17807
17807
  Authorization: `Bearer ${apiKey}`,
17808
17808
  Accept: "application/json"
@@ -18610,6 +18610,26 @@ var LogGroup = class {
18610
18610
 
18611
18611
  // src/logging/client.ts
18612
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
+ };
18613
18633
  async function checkError3(response, _context) {
18614
18634
  const body = await response.text().catch(() => "");
18615
18635
  throwForStatus(response.status, body);
@@ -18680,13 +18700,15 @@ var LoggingClient = class {
18680
18700
  _keyListeners = /* @__PURE__ */ new Map();
18681
18701
  _adapters = [];
18682
18702
  _explicitAdapters = false;
18703
+ _loggerBuffer = new LoggerRegistrationBuffer();
18704
+ _loggerFlushTimer = null;
18683
18705
  /** @internal */
18684
- constructor(apiKey, ensureWs, timeout) {
18706
+ constructor(apiKey, ensureWs, timeout, baseUrl) {
18685
18707
  this._apiKey = apiKey;
18686
18708
  this._ensureWs = ensureWs;
18687
18709
  const ms = timeout ?? 3e4;
18688
18710
  this._http = createClient3({
18689
- baseUrl: LOGGING_BASE_URL,
18711
+ baseUrl: baseUrl ?? LOGGING_BASE_URL,
18690
18712
  headers: {
18691
18713
  Authorization: `Bearer ${apiKey}`,
18692
18714
  Accept: "application/json"
@@ -18925,15 +18947,20 @@ var LoggingClient = class {
18925
18947
  if (!this._explicitAdapters) {
18926
18948
  this._adapters = this._autoLoadAdapters();
18927
18949
  }
18928
- const discovered = [];
18950
+ const service = this._parent?._service ?? null;
18951
+ const environment = this._parent?._environment ?? null;
18952
+ let discoveredCount = 0;
18929
18953
  for (const adapter of this._adapters) {
18930
18954
  try {
18931
18955
  const loggers = adapter.discover();
18932
- discovered.push(...loggers);
18956
+ for (const { name, level } of loggers) {
18957
+ this._loggerBuffer.add(name, level, level, service, environment);
18958
+ discoveredCount++;
18959
+ }
18933
18960
  } catch {
18934
18961
  }
18935
18962
  }
18936
- debug("discovery", `discovered ${discovered.length} logger(s) from adapters`);
18963
+ debug("discovery", `discovered ${discoveredCount} logger(s) from adapters`);
18937
18964
  for (const adapter of this._adapters) {
18938
18965
  try {
18939
18966
  adapter.installHook((name, level) => {
@@ -18943,39 +18970,13 @@ var LoggingClient = class {
18943
18970
  } catch {
18944
18971
  }
18945
18972
  }
18946
- if (discovered.length > 0) {
18973
+ if (discoveredCount > 0) {
18947
18974
  const metrics = this._parent?._metrics;
18948
18975
  if (metrics) {
18949
- metrics.record("logging.loggers_discovered", discovered.length, "loggers");
18950
- }
18951
- }
18952
- if (discovered.length > 0) {
18953
- const service = this._parent?._service ?? null;
18954
- const environment = this._parent?._environment ?? null;
18955
- const loggers = discovered.map(
18956
- ({ name, level }) => ({
18957
- id: name,
18958
- // For Winston/Pino there is no inherited-null distinction — both fields carry the same value.
18959
- level,
18960
- resolved_level: level,
18961
- service: service ?? void 0,
18962
- environment: environment ?? void 0
18963
- })
18964
- );
18965
- debug("registration", `flushing ${loggers.length} logger(s) to bulk-register endpoint`);
18966
- try {
18967
- const result = await this._http.POST("/api/v1/loggers/bulk", {
18968
- body: { loggers }
18969
- });
18970
- if (result.error !== void 0)
18971
- await checkError3(result.response, "Failed to bulk-register loggers");
18972
- debug("registration", `bulk-register complete (${loggers.length} logger(s))`);
18973
- } catch (err) {
18974
- console.warn(
18975
- `[smplkit] Failed to bulk-register loggers: ${err instanceof Error ? err.message : String(err)}`
18976
- );
18976
+ metrics.record("logging.loggers_discovered", discoveredCount, "loggers");
18977
18977
  }
18978
18978
  }
18979
+ await this._flushLoggerBuffer();
18979
18980
  debug("resolution", `starting resolution pass (trigger: start())`);
18980
18981
  try {
18981
18982
  const [serverLoggers, serverGroups] = await Promise.all([
@@ -18994,6 +18995,9 @@ var LoggingClient = class {
18994
18995
  this._wsManager.on("logger_deleted", this._handleLoggerChanged);
18995
18996
  this._wsManager.on("group_changed", this._handleGroupChanged);
18996
18997
  this._wsManager.on("group_deleted", this._handleGroupChanged);
18998
+ this._loggerFlushTimer = setInterval(() => {
18999
+ void this._flushLoggerBuffer();
19000
+ }, 3e4);
18997
19001
  this._started = true;
18998
19002
  }
18999
19003
  // ------------------------------------------------------------------
@@ -19025,6 +19029,10 @@ var LoggingClient = class {
19025
19029
  /** @internal */
19026
19030
  _close() {
19027
19031
  debug("lifecycle", "LoggingClient._close() called");
19032
+ if (this._loggerFlushTimer !== null) {
19033
+ clearInterval(this._loggerFlushTimer);
19034
+ this._loggerFlushTimer = null;
19035
+ }
19028
19036
  for (const adapter of this._adapters) {
19029
19037
  try {
19030
19038
  adapter.uninstallHook();
@@ -19093,12 +19101,34 @@ var LoggingClient = class {
19093
19101
  }
19094
19102
  }
19095
19103
  /** Called by adapter hooks when a new logger is created in the framework. */
19096
- _onAdapterNewLogger(_name, _level) {
19097
- debug("discovery", `new logger intercepted at runtime: ${_name}`);
19098
- const logger = this.management.new(_name, { managed: true });
19099
- logger.setLevel(_level);
19100
- logger.save().catch(() => {
19101
- });
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
+ }
19102
19132
  }
19103
19133
  // ------------------------------------------------------------------
19104
19134
  // Internal: WebSocket handler
@@ -19420,6 +19450,7 @@ var MetricsReporter = class {
19420
19450
  _environment;
19421
19451
  _service;
19422
19452
  _flushInterval;
19453
+ _appBaseUrl;
19423
19454
  _counters = /* @__PURE__ */ new Map();
19424
19455
  _gauges = /* @__PURE__ */ new Map();
19425
19456
  _timer = null;
@@ -19429,6 +19460,7 @@ var MetricsReporter = class {
19429
19460
  this._environment = options.environment;
19430
19461
  this._service = options.service;
19431
19462
  this._flushInterval = options.flushInterval ?? 60;
19463
+ this._appBaseUrl = options.appBaseUrl ?? APP_BASE_URL2;
19432
19464
  }
19433
19465
  // ------------------------------------------------------------------
19434
19466
  // Public recording API
@@ -19505,7 +19537,7 @@ var MetricsReporter = class {
19505
19537
  if (counters.size === 0 && gauges.size === 0) return;
19506
19538
  const payload = this._buildPayload(counters, gauges);
19507
19539
  try {
19508
- fetch(`${APP_BASE_URL2}/api/v1/metrics/bulk`, {
19540
+ fetch(`${this._appBaseUrl}/api/v1/metrics/bulk`, {
19509
19541
  method: "POST",
19510
19542
  headers: {
19511
19543
  Authorization: `Bearer ${this._apiKey}`,
@@ -19544,7 +19576,6 @@ var MetricsReporter = class {
19544
19576
  };
19545
19577
 
19546
19578
  // src/client.ts
19547
- var APP_BASE_URL3 = "https://app.smplkit.com";
19548
19579
  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";
19549
19580
  var NO_SERVICE_MESSAGE = "No service provided. Set one of:\n 1. Pass service in options\n 2. Set the SMPLKIT_SERVICE environment variable";
19550
19581
  var SmplClient = class {
@@ -19563,6 +19594,7 @@ var SmplClient = class {
19563
19594
  /** @internal */
19564
19595
  _metrics = null;
19565
19596
  _timeout;
19597
+ _appBaseUrl;
19566
19598
  _appHttp;
19567
19599
  constructor(options = {}) {
19568
19600
  const environment = options.environment || process.env.SMPLKIT_ENVIRONMENT;
@@ -19578,13 +19610,20 @@ var SmplClient = class {
19578
19610
  const apiKey = resolveApiKey(options.apiKey, environment);
19579
19611
  this._apiKey = apiKey;
19580
19612
  this._timeout = options.timeout ?? 3e4;
19613
+ const baseDomain = options.baseDomain ?? "smplkit.com";
19614
+ const scheme = options.scheme ?? "https";
19615
+ const appBaseUrl = `${scheme}://app.${baseDomain}`;
19616
+ const configBaseUrl = `${scheme}://config.${baseDomain}`;
19617
+ const flagsBaseUrl = `${scheme}://flags.${baseDomain}`;
19618
+ const loggingBaseUrl = `${scheme}://logging.${baseDomain}`;
19619
+ this._appBaseUrl = appBaseUrl;
19581
19620
  const maskedKey = apiKey.length > 14 ? apiKey.slice(0, 10) + "..." + apiKey.slice(-4) : apiKey.slice(0, Math.min(4, apiKey.length)) + "...";
19582
19621
  debug(
19583
19622
  "lifecycle",
19584
19623
  `SmplClient created (api_key=${maskedKey}, environment=${environment}, service=${service})`
19585
19624
  );
19586
19625
  this._appHttp = createClient4({
19587
- baseUrl: APP_BASE_URL3,
19626
+ baseUrl: appBaseUrl,
19588
19627
  headers: {
19589
19628
  Authorization: `Bearer ${apiKey}`,
19590
19629
  Accept: "application/json"
@@ -19594,12 +19633,19 @@ var SmplClient = class {
19594
19633
  this._metrics = new MetricsReporter({
19595
19634
  apiKey,
19596
19635
  environment: this._environment,
19597
- service: this._service
19636
+ service: this._service,
19637
+ appBaseUrl
19598
19638
  });
19599
19639
  }
19600
- this.config = new ConfigClient(apiKey, this._timeout);
19601
- this.flags = new FlagsClient(apiKey, () => this._ensureWs(), this._timeout);
19602
- this.logging = new LoggingClient(apiKey, () => this._ensureWs(), this._timeout);
19640
+ this.config = new ConfigClient(apiKey, this._timeout, configBaseUrl);
19641
+ this.flags = new FlagsClient(
19642
+ apiKey,
19643
+ () => this._ensureWs(),
19644
+ this._timeout,
19645
+ flagsBaseUrl,
19646
+ appBaseUrl
19647
+ );
19648
+ this.logging = new LoggingClient(apiKey, () => this._ensureWs(), this._timeout, loggingBaseUrl);
19603
19649
  this.config._getSharedWs = () => this._ensureWs();
19604
19650
  this.flags._parent = this;
19605
19651
  this.config._parent = this;
@@ -19630,7 +19676,7 @@ var SmplClient = class {
19630
19676
  /** Lazily create and start the shared WebSocket. @internal */
19631
19677
  _ensureWs() {
19632
19678
  if (this._wsManager === null) {
19633
- this._wsManager = new SharedWebSocket(APP_BASE_URL3, this._apiKey, this._metrics);
19679
+ this._wsManager = new SharedWebSocket(this._appBaseUrl, this._apiKey, this._metrics);
19634
19680
  this._wsManager.start();
19635
19681
  }
19636
19682
  return this._wsManager;