@smplkit/sdk 3.0.49 → 3.0.51

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.js CHANGED
@@ -17527,6 +17527,9 @@ function _parseDebugEnv(value) {
17527
17527
  return v === "1" || v === "true" || v === "yes";
17528
17528
  }
17529
17529
  var _DEBUG_ENABLED = _parseDebugEnv(process.env.SMPLKIT_DEBUG ?? "");
17530
+ function isDebugEnabled() {
17531
+ return _DEBUG_ENABLED;
17532
+ }
17530
17533
  function enableDebug() {
17531
17534
  _DEBUG_ENABLED = true;
17532
17535
  }
@@ -19218,12 +19221,10 @@ var LoggerChangeEvent = class {
19218
19221
  id;
19219
19222
  source;
19220
19223
  level;
19221
- deleted;
19222
19224
  constructor(fields) {
19223
19225
  this.id = fields.id;
19224
19226
  this.source = fields.source;
19225
- if (fields.level !== void 0) this.level = fields.level;
19226
- if (fields.deleted) this.deleted = fields.deleted;
19227
+ this.level = fields.level;
19227
19228
  Object.freeze(this);
19228
19229
  }
19229
19230
  };
@@ -21704,6 +21705,72 @@ var FlagsClient = class {
21704
21705
 
21705
21706
  // src/logging/client.ts
21706
21707
  import createClient5 from "openapi-fetch";
21708
+
21709
+ // src/logging/_resolution.ts
21710
+ var FALLBACK_LEVEL = "INFO";
21711
+ function resolveLevel(loggerId, environment, loggers, groups) {
21712
+ const direct = _resolveForEntry(loggerId, environment, loggers, groups);
21713
+ if (direct !== null) {
21714
+ if (isDebugEnabled()) {
21715
+ const source = _findResolutionSource(loggerId, environment, loggers, groups);
21716
+ debug("resolution", `${loggerId} -> ${direct} (source: ${source})`);
21717
+ }
21718
+ return direct;
21719
+ }
21720
+ const parts = loggerId.split(".");
21721
+ for (let i = parts.length - 1; i > 0; i--) {
21722
+ const ancestorId = parts.slice(0, i).join(".");
21723
+ const result = _resolveForEntry(ancestorId, environment, loggers, groups);
21724
+ if (result !== null) {
21725
+ debug("resolution", `${loggerId} -> ${result} (source: ancestor "${ancestorId}")`);
21726
+ return result;
21727
+ }
21728
+ }
21729
+ debug("resolution", `${loggerId} -> ${FALLBACK_LEVEL} (source: system default)`);
21730
+ return FALLBACK_LEVEL;
21731
+ }
21732
+ function _resolveForEntry(loggerId, environment, loggers, groups) {
21733
+ const entry = loggers[loggerId];
21734
+ if (entry === void 0) return null;
21735
+ const envLevel = _envLevel(entry.environments, environment);
21736
+ if (envLevel !== null) return envLevel;
21737
+ if (entry.level !== null && entry.level !== void 0) return entry.level;
21738
+ return _resolveGroupChain(entry.group, environment, groups);
21739
+ }
21740
+ function _findResolutionSource(loggerId, environment, loggers, groups) {
21741
+ const entry = loggers[loggerId];
21742
+ if (entry === void 0) return "not found";
21743
+ const envLevel = _envLevel(entry.environments, environment);
21744
+ if (envLevel !== null) return `env override "${environment}"`;
21745
+ if (entry.level !== null && entry.level !== void 0) return "base level";
21746
+ const groupResult = _resolveGroupChain(entry.group, environment, groups);
21747
+ if (groupResult !== null) return `group "${entry.group}"`;
21748
+ return "unknown";
21749
+ }
21750
+ function _resolveGroupChain(groupId, environment, groups) {
21751
+ const visited = /* @__PURE__ */ new Set();
21752
+ let currentId = groupId;
21753
+ while (currentId !== null && currentId !== void 0 && !visited.has(currentId)) {
21754
+ visited.add(currentId);
21755
+ const group = groups[currentId];
21756
+ if (group === void 0) break;
21757
+ const envLevel = _envLevel(group.environments, environment);
21758
+ if (envLevel !== null) return envLevel;
21759
+ if (group.level !== null && group.level !== void 0) return group.level;
21760
+ currentId = group.group;
21761
+ }
21762
+ return null;
21763
+ }
21764
+ function _envLevel(envs, environment) {
21765
+ if (!envs || typeof envs !== "object") return null;
21766
+ const envData = envs[environment];
21767
+ if (!envData || typeof envData !== "object") return null;
21768
+ const level = envData.level;
21769
+ if (level === null || level === void 0) return null;
21770
+ return level;
21771
+ }
21772
+
21773
+ // src/logging/client.ts
21707
21774
  var LOGGING_BASE_URL = "https://logging.smplkit.com";
21708
21775
  var LoggerRegistrationBuffer2 = class {
21709
21776
  _seen = /* @__PURE__ */ new Set();
@@ -21745,11 +21812,20 @@ var LoggingClient = class {
21745
21812
  _explicitAdapters = false;
21746
21813
  _loggerBuffer = new LoggerRegistrationBuffer2();
21747
21814
  _loggerFlushTimer = null;
21748
- // Local stores for diff-based listener firing
21749
- _loggerStore = {};
21750
- // key -> level
21751
- _groupStore = {};
21752
- // key -> level
21815
+ // Caches consulted by the resolution algorithm. The runtime client mutates
21816
+ // these from install(), refresh(), and the WebSocket handlers; resolveLevel
21817
+ // reads them on every apply.
21818
+ _loggersCache = {};
21819
+ _groupsCache = {};
21820
+ // Resolved-level snapshot, used to decide whether to fire change listeners.
21821
+ // Keyed by logger id; storing the *resolved* level (not raw `logger.level`)
21822
+ // means a group-driven level shift fires listeners even when the logger's
21823
+ // own level is untouched.
21824
+ _resolvedLevelStore = {};
21825
+ // Adapter-known logger names. The adapter knows loggers by their original
21826
+ // name (e.g. `com.acme.payments`); the cache is keyed by the same string
21827
+ // since the TypeScript SDK does not normalize.
21828
+ _knownLoggerNames = /* @__PURE__ */ new Set();
21753
21829
  /** @internal */
21754
21830
  constructor(apiKey, ensureWs, timeout, baseUrl, extraHeaders) {
21755
21831
  this._apiKey = apiKey;
@@ -21942,6 +22018,7 @@ var LoggingClient = class {
21942
22018
  const loggers = adapter.discover();
21943
22019
  for (const { name, level } of loggers) {
21944
22020
  this._loggerBuffer.add(name, level, level, service, environment);
22021
+ this._knownLoggerNames.add(name);
21945
22022
  discoveredCount++;
21946
22023
  }
21947
22024
  } catch {
@@ -21974,13 +22051,9 @@ var LoggingClient = class {
21974
22051
  "api",
21975
22052
  `fetched ${serverLoggers.length} logger(s) and ${serverGroups.length} group(s) from server`
21976
22053
  );
21977
- this._applyLevels(serverLoggers);
21978
- for (const l of serverLoggers) {
21979
- this._loggerStore[l.id] = l.level;
21980
- }
21981
- for (const g of serverGroups) {
21982
- this._groupStore[g.id] = g.level;
21983
- }
22054
+ this._loggersCache = this._buildLoggersCache(serverLoggers);
22055
+ this._groupsCache = this._buildGroupsCache(serverGroups);
22056
+ this._applyLevels();
21984
22057
  } catch {
21985
22058
  }
21986
22059
  this._wsManager = this._ensureWs();
@@ -22072,41 +22145,104 @@ var LoggingClient = class {
22072
22145
  }
22073
22146
  return adapters;
22074
22147
  }
22075
- /** Apply resolved levels from server loggers to all adapters. */
22076
- _applyLevels(serverLoggers) {
22077
- for (const logger of serverLoggers) {
22078
- if (!logger.level) continue;
22079
- const env = this._parent?._environment;
22080
- let effectiveLevel = logger.level;
22081
- if (env && logger.environments) {
22082
- const envOverride = logger.environments[env];
22083
- if (envOverride?.level) {
22084
- effectiveLevel = envOverride.level;
22085
- }
22086
- }
22087
- debug("resolution", `${logger.id} -> ${effectiveLevel}`);
22148
+ /**
22149
+ * Refresh resolved levels and apply them to adapter-known loggers.
22150
+ *
22151
+ * Walks every adapter-known logger name through {@link resolveLevel}
22152
+ * (env override → base → group chain → dot-notation ancestry → fallback)
22153
+ * and pushes the result to every registered adapter. Loggers whose own
22154
+ * `level` is `null` are still applied — that's the whole point of group
22155
+ * inheritance and dot-notation ancestry.
22156
+ *
22157
+ * The resolved-level snapshot lives in `_resolvedLevelStore` so callers
22158
+ * can diff pre-vs-post and fire change listeners on actual effective-
22159
+ * level deltas. The store is scoped to adapter-known names: listener
22160
+ * fanout pairs 1:1 with `adapter.applyLevel` calls, so a logger that
22161
+ * the adapter doesn't know about has no apply and no listener fire.
22162
+ * @internal
22163
+ */
22164
+ _applyLevels() {
22165
+ const environment = this._parent?._environment ?? "";
22166
+ const newResolved = {};
22167
+ for (const name of this._knownLoggerNames) {
22168
+ newResolved[name] = resolveLevel(name, environment, this._loggersCache, this._groupsCache);
22169
+ }
22170
+ this._resolvedLevelStore = newResolved;
22171
+ for (const name of this._knownLoggerNames) {
22172
+ const resolved = newResolved[name];
22088
22173
  const metrics = this._parent?._metrics;
22089
22174
  if (metrics) {
22090
- metrics.record("logging.level_changes", 1, "changes", { logger: logger.id });
22175
+ metrics.record("logging.level_changes", 1, "changes", { logger: name });
22091
22176
  }
22092
22177
  for (const adapter of this._adapters) {
22093
22178
  try {
22094
- debug("adapter", `setLevel(${logger.id}, ${effectiveLevel})`);
22095
- adapter.applyLevel(logger.id, effectiveLevel);
22179
+ debug("adapter", `setLevel(${name}, ${resolved})`);
22180
+ adapter.applyLevel(name, resolved);
22096
22181
  } catch {
22097
22182
  }
22098
22183
  }
22099
22184
  }
22100
22185
  }
22186
+ /** @internal Convert a server Logger list into the resolution cache. */
22187
+ _buildLoggersCache(loggers) {
22188
+ const out = {};
22189
+ for (const l of loggers) {
22190
+ if (l.id === null) continue;
22191
+ out[l.id] = this._loggerToCacheEntry(l);
22192
+ }
22193
+ return out;
22194
+ }
22195
+ /** @internal Convert a server LogGroup list into the resolution cache. */
22196
+ _buildGroupsCache(groups) {
22197
+ const out = {};
22198
+ for (const g of groups) {
22199
+ if (g.id === null) continue;
22200
+ out[g.id] = this._groupToCacheEntry(g);
22201
+ }
22202
+ return out;
22203
+ }
22204
+ /** @internal */
22205
+ _loggerToCacheEntry(l) {
22206
+ return {
22207
+ level: l.level ?? null,
22208
+ group: l.group ?? null,
22209
+ managed: l.managed ?? null,
22210
+ environments: l.environments ?? null
22211
+ };
22212
+ }
22213
+ /** @internal */
22214
+ _groupToCacheEntry(g) {
22215
+ return {
22216
+ level: g.level ?? null,
22217
+ group: g.group ?? null,
22218
+ environments: g.environments ?? null
22219
+ };
22220
+ }
22101
22221
  /** Called by adapter hooks when a new logger is created in the framework. */
22102
22222
  _onAdapterNewLogger(name, level) {
22103
22223
  debug("discovery", `new logger intercepted at runtime: ${name}`);
22104
22224
  const service = this._parent?._service ?? null;
22105
22225
  const environment = this._parent?._environment ?? null;
22106
22226
  this._loggerBuffer.add(name, level, level, service, environment);
22227
+ this._knownLoggerNames.add(name);
22107
22228
  if (this._loggerBuffer.pendingCount >= 50) {
22108
22229
  void this._flushLoggerBuffer();
22109
22230
  }
22231
+ if (this._started) {
22232
+ const resolved = resolveLevel(
22233
+ name,
22234
+ this._parent?._environment ?? "",
22235
+ this._loggersCache,
22236
+ this._groupsCache
22237
+ );
22238
+ this._resolvedLevelStore[name] = resolved;
22239
+ for (const adapter of this._adapters) {
22240
+ try {
22241
+ adapter.applyLevel(name, resolved);
22242
+ } catch {
22243
+ }
22244
+ }
22245
+ }
22110
22246
  }
22111
22247
  /** Flush buffered loggers to the bulk-register endpoint. */
22112
22248
  async _flushLoggerBuffer() {
@@ -22140,33 +22276,14 @@ var LoggingClient = class {
22140
22276
  const id = data.id;
22141
22277
  if (!id) return;
22142
22278
  void this._fetchSingleLogger(id).then((logger) => {
22143
- const oldLevel = this._loggerStore[id] ?? null;
22144
- const newLevel = logger?.level ?? null;
22145
- if (oldLevel === newLevel) return;
22146
- this._loggerStore[id] = newLevel;
22147
- if (logger) {
22148
- this._applyLevels([logger]);
22149
- }
22150
- const event = new LoggerChangeEvent({
22151
- id,
22152
- level: newLevel,
22153
- source: "websocket"
22154
- });
22155
- for (const cb of this._globalListeners) {
22156
- try {
22157
- cb(event);
22158
- } catch {
22159
- }
22160
- }
22161
- const idCallbacks = this._keyListeners.get(id);
22162
- if (idCallbacks) {
22163
- for (const cb of idCallbacks) {
22164
- try {
22165
- cb(event);
22166
- } catch {
22167
- }
22168
- }
22279
+ const preResolved = { ...this._resolvedLevelStore };
22280
+ if (logger !== null) {
22281
+ this._loggersCache[id] = this._loggerToCacheEntry(logger);
22282
+ } else {
22283
+ delete this._loggersCache[id];
22169
22284
  }
22285
+ this._applyLevels();
22286
+ this._fireDeltas(preResolved, this._resolvedLevelStore, "websocket");
22170
22287
  }).catch((err) => {
22171
22288
  debug(
22172
22289
  "websocket",
@@ -22178,50 +22295,24 @@ var LoggingClient = class {
22178
22295
  debug("websocket", `logger_deleted event received: ${JSON.stringify(data)}`);
22179
22296
  const id = data.id;
22180
22297
  if (!id) return;
22181
- delete this._loggerStore[id];
22182
- const event = new LoggerChangeEvent({
22183
- id,
22184
- level: null,
22185
- source: "websocket",
22186
- deleted: true
22187
- });
22188
- for (const cb of this._globalListeners) {
22189
- try {
22190
- cb(event);
22191
- } catch (err) {
22192
- debug(
22193
- "websocket",
22194
- `logger_deleted listener error: ${err instanceof Error ? err.message : String(err)}`
22195
- );
22196
- }
22197
- }
22198
- const idCallbacks = this._keyListeners.get(id);
22199
- if (idCallbacks) {
22200
- for (const cb of idCallbacks) {
22201
- try {
22202
- cb(event);
22203
- } catch (err) {
22204
- debug(
22205
- "websocket",
22206
- `logger_deleted key listener error: ${err instanceof Error ? err.message : String(err)}`
22207
- );
22208
- }
22209
- }
22210
- }
22298
+ delete this._loggersCache[id];
22299
+ const preResolved = { ...this._resolvedLevelStore };
22300
+ this._applyLevels();
22301
+ this._fireDeltas(preResolved, this._resolvedLevelStore, "websocket", /* @__PURE__ */ new Set([id]));
22211
22302
  };
22212
22303
  _handleGroupChanged = (data) => {
22213
22304
  debug("websocket", `group_changed event received: ${JSON.stringify(data)}`);
22214
22305
  const id = data.id;
22215
22306
  if (!id) return;
22216
22307
  void this._fetchSingleGroup(id).then((group) => {
22217
- const oldLevel = this._groupStore[id] ?? null;
22218
- const newLevel = group?.level ?? null;
22219
- if (oldLevel === newLevel) return;
22220
- this._groupStore[id] = newLevel;
22221
- void this._listLoggers().then((loggers) => {
22222
- this._applyLevels(loggers);
22223
- }).catch(() => {
22224
- });
22308
+ const preResolved = { ...this._resolvedLevelStore };
22309
+ if (group !== null) {
22310
+ this._groupsCache[id] = this._groupToCacheEntry(group);
22311
+ } else {
22312
+ delete this._groupsCache[id];
22313
+ }
22314
+ this._applyLevels();
22315
+ this._fireDeltas(preResolved, this._resolvedLevelStore, "websocket");
22225
22316
  }).catch((err) => {
22226
22317
  debug(
22227
22318
  "websocket",
@@ -22233,47 +22324,63 @@ var LoggingClient = class {
22233
22324
  debug("websocket", `group_deleted event received: ${JSON.stringify(data)}`);
22234
22325
  const id = data.id;
22235
22326
  if (!id) return;
22236
- delete this._groupStore[id];
22237
- const event = new LoggerChangeEvent({
22238
- id,
22239
- level: null,
22240
- source: "websocket",
22241
- deleted: true
22242
- });
22243
- for (const cb of this._globalListeners) {
22244
- try {
22245
- cb(event);
22246
- } catch (err) {
22247
- debug(
22248
- "websocket",
22249
- `group_deleted listener error: ${err instanceof Error ? err.message : String(err)}`
22250
- );
22251
- }
22252
- }
22253
- const idCallbacks = this._keyListeners.get(id);
22254
- if (idCallbacks) {
22255
- for (const cb of idCallbacks) {
22327
+ delete this._groupsCache[id];
22328
+ const preResolved = { ...this._resolvedLevelStore };
22329
+ this._applyLevels();
22330
+ this._fireDeltas(preResolved, this._resolvedLevelStore, "websocket", /* @__PURE__ */ new Set([id]));
22331
+ };
22332
+ /**
22333
+ * @internal Fire change listeners for every logger whose effective level
22334
+ * changed between `pre` and `post`.
22335
+ *
22336
+ * Contract:
22337
+ * - Iterates loggers present in `post` (the post-apply resolved store).
22338
+ * A key in `pre` but not in `post` is a cache eviction — that key
22339
+ * itself fires nothing. Dependents that re-resolved to a new value
22340
+ * are still in `post` and fire normally.
22341
+ * - For every changed logger, fires every global listener once with
22342
+ * that logger's own payload (no "summary" event), then fires every
22343
+ * key-scoped listener registered for that id.
22344
+ * - One adapter.applyLevel call ↔ one listener notification per
22345
+ * subscriber. A trigger that moves N loggers fires the global
22346
+ * listener N times, not once.
22347
+ * - `suppressIds` is the deletion-event escape hatch: a deleted id
22348
+ * fires nothing for itself even when its adapter-known resolved
22349
+ * level moved (e.g. fell back to INFO).
22350
+ */
22351
+ _fireDeltas(pre, post, source, suppressIds) {
22352
+ for (const id of Object.keys(post)) {
22353
+ if (suppressIds?.has(id)) continue;
22354
+ const next = post[id];
22355
+ if (pre[id] === next) continue;
22356
+ const event = new LoggerChangeEvent({ id, source, level: next });
22357
+ for (const cb of this._globalListeners) {
22256
22358
  try {
22257
22359
  cb(event);
22258
- } catch (err) {
22259
- debug(
22260
- "websocket",
22261
- `group_deleted key listener error: ${err instanceof Error ? err.message : String(err)}`
22262
- );
22360
+ } catch {
22361
+ }
22362
+ }
22363
+ const keyCallbacks = this._keyListeners.get(id);
22364
+ if (keyCallbacks) {
22365
+ for (const cb of keyCallbacks) {
22366
+ try {
22367
+ cb(event);
22368
+ } catch {
22369
+ }
22263
22370
  }
22264
22371
  }
22265
22372
  }
22266
- };
22373
+ }
22267
22374
  _handleLoggersChanged = (_data) => {
22268
22375
  debug("websocket", `loggers_changed event received`);
22269
22376
  void this._resolveAndFire("websocket").catch(() => {
22270
22377
  });
22271
22378
  };
22272
22379
  /**
22273
- * Full refetch of loggers + log_groups, apply resolved levels to
22274
- * adapters, diff against local stores and fire change listeners
22275
- * (global once, per-key for each changed id). Shared between the
22276
- * `loggers_changed` WS handler and the public `refresh()` method.
22380
+ * Full refetch of loggers + log_groups, rebuild the caches, re-resolve
22381
+ * every adapter-known logger, and fire change listeners on resolved-level
22382
+ * deltas. Shared between the `loggers_changed` WS handler and the
22383
+ * public `refresh()` method.
22277
22384
  * @internal
22278
22385
  */
22279
22386
  async _resolveAndFire(source) {
@@ -22282,58 +22389,12 @@ var LoggingClient = class {
22282
22389
  this._listLogGroups()
22283
22390
  ]);
22284
22391
  debug("resolution", `resolution pass (trigger: ${source})`);
22285
- const changedLoggerIds = /* @__PURE__ */ new Set();
22286
- const newLoggerKeys = new Set(serverLoggers.map((l) => l.id));
22287
- for (const logger of serverLoggers) {
22288
- const key = logger.id;
22289
- const oldLevel = this._loggerStore[key] ?? null;
22290
- const newLevel = logger.level ?? null;
22291
- if (oldLevel !== newLevel || !(key in this._loggerStore)) {
22292
- changedLoggerIds.add(key);
22293
- this._loggerStore[key] = newLevel;
22294
- }
22295
- }
22296
- for (const key of Object.keys(this._loggerStore)) {
22297
- if (!newLoggerKeys.has(key)) {
22298
- changedLoggerIds.add(key);
22299
- delete this._loggerStore[key];
22300
- }
22301
- }
22302
- for (const group of serverGroups) {
22303
- this._groupStore[group.id] = group.level ?? null;
22304
- }
22305
- this._applyLevels(serverLoggers);
22306
- if (changedLoggerIds.size === 0) return;
22307
- const [firstKey] = changedLoggerIds;
22308
- const firstLogger = serverLoggers.find((l) => l.id === firstKey);
22309
- const globalEvent = new LoggerChangeEvent({
22310
- id: firstKey,
22311
- level: firstLogger?.level ?? null,
22312
- source
22313
- });
22314
- for (const cb of this._globalListeners) {
22315
- try {
22316
- cb(globalEvent);
22317
- } catch {
22318
- }
22319
- }
22320
- for (const key of changedLoggerIds) {
22321
- const keyCallbacks = this._keyListeners.get(key);
22322
- if (keyCallbacks) {
22323
- const l = serverLoggers.find((x) => x.id === key);
22324
- const keyEvent = new LoggerChangeEvent({
22325
- id: key,
22326
- level: l?.level ?? null,
22327
- source
22328
- });
22329
- for (const cb of keyCallbacks) {
22330
- try {
22331
- cb(keyEvent);
22332
- } catch {
22333
- }
22334
- }
22335
- }
22336
- }
22392
+ const preResolved = { ...this._resolvedLevelStore };
22393
+ this._loggersCache = this._buildLoggersCache(serverLoggers);
22394
+ this._groupsCache = this._buildGroupsCache(serverGroups);
22395
+ this._resolvedLevelStore = {};
22396
+ this._applyLevels();
22397
+ this._fireDeltas(preResolved, this._resolvedLevelStore, source);
22337
22398
  }
22338
22399
  // ------------------------------------------------------------------
22339
22400
  // Internal: single-resource fetchers