@smplkit/sdk 1.6.0 → 1.6.2

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/README.md CHANGED
@@ -34,28 +34,49 @@ const client3 = new SmplClient();
34
34
 
35
35
  ## Configuration
36
36
 
37
- The API key is resolved using the following priority:
37
+ All settings are resolved from three sources, in order of precedence:
38
38
 
39
- 1. **Explicit argument:** Pass `apiKey` in the constructor options.
40
- 2. **Environment variable:** Set `SMPLKIT_API_KEY`.
41
- 3. **Configuration file:** Add `api_key` under `[default]` in `~/.smplkit`:
39
+ 1. **Constructor options** highest priority, always wins.
40
+ 2. **Environment variables** e.g. `SMPLKIT_API_KEY`, `SMPLKIT_ENVIRONMENT`.
41
+ 3. **Configuration file** (`~/.smplkit`) INI-format with profile support.
42
+ 4. **Defaults** — built-in SDK defaults.
43
+
44
+ ### Configuration File
45
+
46
+ The `~/.smplkit` file supports a `[common]` section (applied to all profiles) and named profiles:
42
47
 
43
48
  ```ini
44
- # ~/.smplkit
49
+ [common]
50
+ environment = production
51
+ service = my-app
45
52
 
46
53
  [default]
47
- api_key = sk_api_your_key_here
54
+ api_key = sk_api_abc123
55
+
56
+ [local]
57
+ base_domain = localhost
58
+ scheme = http
59
+ api_key = sk_api_local_xyz
60
+ environment = development
61
+ debug = true
48
62
  ```
49
63
 
50
- If none of these are set, the SDK throws `SmplError` with a message listing all three methods.
64
+ ### Constructor Examples
51
65
 
52
66
  ```typescript
67
+ // Use a named profile
68
+ const client = new SmplClient({ profile: "local" });
69
+
70
+ // Or configure explicitly
53
71
  const client = new SmplClient({
54
72
  apiKey: "sk_api_...",
55
- timeout: 30_000, // default (ms)
73
+ environment: "production",
74
+ service: "my-service",
56
75
  });
57
76
  ```
58
77
 
78
+ For the complete configuration reference, see the [Configuration Guide](https://docs.smplkit.com/getting-started/configuration).
79
+
59
80
  ## Config
60
81
 
61
82
  ### Runtime (resolve config values)
package/dist/index.cjs CHANGED
@@ -17084,7 +17084,7 @@ var ConfigClient = class {
17084
17084
  /** @internal */
17085
17085
  _apiKey;
17086
17086
  /** @internal */
17087
- _baseUrl = BASE_URL;
17087
+ _baseUrl;
17088
17088
  /** @internal */
17089
17089
  _http;
17090
17090
  /** @internal — returns the shared WebSocket for real-time updates. */
@@ -17099,9 +17099,11 @@ var ConfigClient = class {
17099
17099
  /** @internal */
17100
17100
  constructor(apiKey, timeout, baseUrl) {
17101
17101
  this._apiKey = apiKey;
17102
+ const resolvedBaseUrl = baseUrl ?? BASE_URL;
17103
+ this._baseUrl = resolvedBaseUrl;
17102
17104
  const ms = timeout ?? 3e4;
17103
17105
  this._http = (0, import_openapi_fetch.default)({
17104
- baseUrl: baseUrl ?? BASE_URL,
17106
+ baseUrl: resolvedBaseUrl,
17105
17107
  headers: {
17106
17108
  Authorization: `Bearer ${apiKey}`,
17107
17109
  Accept: "application/json"
@@ -17791,7 +17793,7 @@ var FlagsClient = class {
17791
17793
  /** @internal */
17792
17794
  _apiKey;
17793
17795
  /** @internal */
17794
- _baseUrl = FLAGS_BASE_URL;
17796
+ _baseUrl;
17795
17797
  /** @internal */
17796
17798
  _http;
17797
17799
  /** @internal */
@@ -17819,6 +17821,9 @@ var FlagsClient = class {
17819
17821
  constructor(apiKey, ensureWs, timeout, flagsBaseUrl, appBaseUrl) {
17820
17822
  this._apiKey = apiKey;
17821
17823
  this._ensureWs = ensureWs;
17824
+ const resolvedBaseUrl = flagsBaseUrl ?? FLAGS_BASE_URL;
17825
+ const resolvedAppBaseUrl = appBaseUrl ?? APP_BASE_URL;
17826
+ this._baseUrl = resolvedBaseUrl;
17822
17827
  const ms = timeout ?? 3e4;
17823
17828
  const fetchWithTimeout = async (request) => {
17824
17829
  const controller = new AbortController();
@@ -17835,7 +17840,7 @@ var FlagsClient = class {
17835
17840
  }
17836
17841
  };
17837
17842
  this._http = (0, import_openapi_fetch2.default)({
17838
- baseUrl: flagsBaseUrl ?? FLAGS_BASE_URL,
17843
+ baseUrl: resolvedBaseUrl,
17839
17844
  headers: {
17840
17845
  Authorization: `Bearer ${apiKey}`,
17841
17846
  Accept: "application/json"
@@ -17843,7 +17848,7 @@ var FlagsClient = class {
17843
17848
  fetch: fetchWithTimeout
17844
17849
  });
17845
17850
  this._appHttp = (0, import_openapi_fetch2.default)({
17846
- baseUrl: appBaseUrl ?? APP_BASE_URL,
17851
+ baseUrl: resolvedAppBaseUrl,
17847
17852
  headers: {
17848
17853
  Authorization: `Bearer ${apiKey}`,
17849
17854
  Accept: "application/json"
@@ -18727,7 +18732,7 @@ var LoggingClient = class {
18727
18732
  /** @internal */
18728
18733
  _apiKey;
18729
18734
  /** @internal */
18730
- _baseUrl = LOGGING_BASE_URL;
18735
+ _baseUrl;
18731
18736
  /** @internal */
18732
18737
  _http;
18733
18738
  /** @internal — set by SmplClient after construction. */
@@ -18747,9 +18752,11 @@ var LoggingClient = class {
18747
18752
  constructor(apiKey, ensureWs, timeout, baseUrl) {
18748
18753
  this._apiKey = apiKey;
18749
18754
  this._ensureWs = ensureWs;
18755
+ const resolvedBaseUrl = baseUrl ?? LOGGING_BASE_URL;
18756
+ this._baseUrl = resolvedBaseUrl;
18750
18757
  const ms = timeout ?? 3e4;
18751
18758
  this._http = (0, import_openapi_fetch3.default)({
18752
- baseUrl: baseUrl ?? LOGGING_BASE_URL,
18759
+ baseUrl: resolvedBaseUrl,
18753
18760
  headers: {
18754
18761
  Authorization: `Bearer ${apiKey}`,
18755
18762
  Accept: "application/json"
@@ -19422,59 +19429,117 @@ var SharedWebSocket = class {
19422
19429
  }
19423
19430
  };
19424
19431
 
19425
- // src/resolve.ts
19432
+ // src/config.ts
19426
19433
  var import_node_fs = require("fs");
19427
19434
  var import_node_os = require("os");
19428
19435
  var import_node_path = require("path");
19429
- function noApiKeyMessage(environment) {
19430
- return `No API key provided. Set one of:
19431
- 1. Pass apiKey to the constructor
19432
- 2. Set the SMPLKIT_API_KEY environment variable
19433
- 3. Create a ~/.smplkit file with:
19434
- [${environment}]
19435
- api_key = your_key_here`;
19436
- }
19437
- function readApiKeyFromConfig(environment) {
19438
- const configPath = (0, import_node_path.join)((0, import_node_os.homedir)(), ".smplkit");
19439
- try {
19440
- const content = (0, import_node_fs.readFileSync)(configPath, "utf-8");
19441
- let currentSection = null;
19442
- let envKey;
19443
- let defaultKey;
19444
- for (const line of content.split("\n")) {
19445
- const trimmed = line.trim();
19446
- if (trimmed === "" || trimmed.startsWith("#")) continue;
19447
- if (trimmed.startsWith("[")) {
19448
- const sectionName = trimmed.slice(1, trimmed.indexOf("]")).toLowerCase();
19449
- currentSection = sectionName;
19450
- continue;
19451
- }
19452
- if (currentSection && trimmed.startsWith("api_key")) {
19453
- const eqIndex = trimmed.indexOf("=");
19454
- if (eqIndex !== -1) {
19455
- const value = trimmed.slice(eqIndex + 1).trim();
19456
- if (value) {
19457
- if (currentSection === environment.toLowerCase()) {
19458
- envKey = value;
19459
- } else if (currentSection === "default") {
19460
- defaultKey = value;
19461
- }
19462
- }
19463
- }
19436
+ var CONFIG_KEYS = {
19437
+ api_key: "SMPLKIT_API_KEY",
19438
+ base_domain: "SMPLKIT_BASE_DOMAIN",
19439
+ scheme: "SMPLKIT_SCHEME",
19440
+ environment: "SMPLKIT_ENVIRONMENT",
19441
+ service: "SMPLKIT_SERVICE",
19442
+ debug: "SMPLKIT_DEBUG",
19443
+ disable_telemetry: "SMPLKIT_DISABLE_TELEMETRY"
19444
+ };
19445
+ function parseIniFile(content, profile) {
19446
+ const common = {};
19447
+ const profileValues = {};
19448
+ const sections = /* @__PURE__ */ new Set();
19449
+ let currentSection = null;
19450
+ const lowerProfile = profile.toLowerCase();
19451
+ for (const line of content.split("\n")) {
19452
+ const trimmed = line.trim();
19453
+ if (trimmed === "" || trimmed.startsWith("#") || trimmed.startsWith(";")) continue;
19454
+ if (trimmed.startsWith("[")) {
19455
+ const closeBracket = trimmed.indexOf("]");
19456
+ if (closeBracket === -1) continue;
19457
+ currentSection = trimmed.slice(1, closeBracket).trim().toLowerCase();
19458
+ if (currentSection !== "common") {
19459
+ sections.add(currentSection);
19464
19460
  }
19461
+ continue;
19465
19462
  }
19466
- return envKey ?? defaultKey;
19467
- } catch {
19468
- return void 0;
19463
+ if (currentSection === null) continue;
19464
+ const eqIndex = trimmed.indexOf("=");
19465
+ if (eqIndex === -1) continue;
19466
+ const key = trimmed.slice(0, eqIndex).trim();
19467
+ const value = trimmed.slice(eqIndex + 1).trim();
19468
+ if (!key || !value) continue;
19469
+ if (currentSection === "common") {
19470
+ common[key] = value;
19471
+ } else if (currentSection === lowerProfile) {
19472
+ profileValues[key] = value;
19473
+ }
19474
+ }
19475
+ if (lowerProfile !== "default" && sections.size > 0 && !sections.has(lowerProfile) && Object.keys(profileValues).length === 0) {
19476
+ const available = [...sections].sort().join(", ");
19477
+ throw new SmplError(
19478
+ `Configuration profile "${profile}" not found in ~/.smplkit. Available profiles: ${available}`
19479
+ );
19469
19480
  }
19481
+ return { ...common, ...profileValues };
19470
19482
  }
19471
- function resolveApiKey(explicit, environment) {
19472
- if (explicit) return explicit;
19473
- const envVal = process.env.SMPLKIT_API_KEY;
19474
- if (envVal) return envVal;
19475
- const fileKey = readApiKeyFromConfig(environment);
19476
- if (fileKey) return fileKey;
19477
- throw new SmplError(noApiKeyMessage(environment));
19483
+ function parseBool(value, key) {
19484
+ const lower = value.toLowerCase();
19485
+ if (lower === "true" || lower === "1" || lower === "yes") return true;
19486
+ if (lower === "false" || lower === "0" || lower === "no") return false;
19487
+ throw new SmplError(
19488
+ `Invalid boolean value "${value}" for ${key}. Expected true/false, 1/0, or yes/no.`
19489
+ );
19490
+ }
19491
+ function serviceUrl(scheme, subdomain, baseDomain) {
19492
+ return `${scheme}://${subdomain}.${baseDomain}`;
19493
+ }
19494
+ var NO_API_KEY_MESSAGE = "No API key provided. Set one of:\n 1. Pass apiKey to the constructor\n 2. Set the SMPLKIT_API_KEY environment variable\n 3. Add api_key to your ~/.smplkit config file";
19495
+ 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\n 3. Add environment to your ~/.smplkit config file";
19496
+ var NO_SERVICE_MESSAGE = "No service provided. Set one of:\n 1. Pass service in options\n 2. Set the SMPLKIT_SERVICE environment variable\n 3. Add service to your ~/.smplkit config file";
19497
+ function resolveConfig(options) {
19498
+ const merged = {
19499
+ scheme: "https",
19500
+ base_domain: "smplkit.com",
19501
+ debug: "false",
19502
+ disable_telemetry: "false"
19503
+ };
19504
+ const profile = options.profile ?? process.env.SMPLKIT_PROFILE ?? "default";
19505
+ try {
19506
+ const configPath = (0, import_node_path.join)((0, import_node_os.homedir)(), ".smplkit");
19507
+ const content = (0, import_node_fs.readFileSync)(configPath, "utf-8");
19508
+ const fileValues = parseIniFile(content, profile);
19509
+ for (const key of Object.keys(CONFIG_KEYS)) {
19510
+ if (fileValues[key]) {
19511
+ merged[key] = fileValues[key];
19512
+ }
19513
+ }
19514
+ } catch (e) {
19515
+ if (e instanceof SmplError) throw e;
19516
+ }
19517
+ for (const [key, envVar] of Object.entries(CONFIG_KEYS)) {
19518
+ const envVal = process.env[envVar];
19519
+ if (envVal) {
19520
+ merged[key] = envVal;
19521
+ }
19522
+ }
19523
+ if (options.apiKey !== void 0) merged.api_key = options.apiKey;
19524
+ if (options.baseDomain !== void 0) merged.base_domain = options.baseDomain;
19525
+ if (options.scheme !== void 0) merged.scheme = options.scheme;
19526
+ if (options.environment !== void 0) merged.environment = options.environment;
19527
+ if (options.service !== void 0) merged.service = options.service;
19528
+ if (options.debug !== void 0) merged.debug = String(options.debug);
19529
+ if (options.disableTelemetry !== void 0)
19530
+ merged.disable_telemetry = String(options.disableTelemetry);
19531
+ if (!merged.api_key) throw new SmplError(NO_API_KEY_MESSAGE);
19532
+ if (!merged.environment) throw new SmplError(NO_ENVIRONMENT_MESSAGE);
19533
+ if (!merged.service) throw new SmplError(NO_SERVICE_MESSAGE);
19534
+ return {
19535
+ apiKey: merged.api_key,
19536
+ baseDomain: merged.base_domain,
19537
+ scheme: merged.scheme,
19538
+ environment: merged.environment,
19539
+ service: merged.service,
19540
+ debug: parseBool(merged.debug, "debug"),
19541
+ disableTelemetry: parseBool(merged.disable_telemetry, "disable_telemetry")
19542
+ };
19478
19543
  }
19479
19544
 
19480
19545
  // src/_metrics.ts
@@ -19617,8 +19682,6 @@ var MetricsReporter = class {
19617
19682
  };
19618
19683
 
19619
19684
  // src/client.ts
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";
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";
19622
19685
  var SmplClient = class {
19623
19686
  /** Client for config management and runtime. */
19624
19687
  config;
@@ -19638,55 +19701,50 @@ var SmplClient = class {
19638
19701
  _appBaseUrl;
19639
19702
  _appHttp;
19640
19703
  constructor(options = {}) {
19641
- const environment = options.environment || process.env.SMPLKIT_ENVIRONMENT;
19642
- if (!environment) {
19643
- throw new SmplError(NO_ENVIRONMENT_MESSAGE);
19644
- }
19645
- this._environment = environment;
19646
- const service = options.service || process.env.SMPLKIT_SERVICE;
19647
- if (!service) {
19648
- throw new SmplError(NO_SERVICE_MESSAGE);
19649
- }
19650
- this._service = service;
19651
- const apiKey = resolveApiKey(options.apiKey, environment);
19652
- this._apiKey = apiKey;
19704
+ const cfg = resolveConfig(options);
19705
+ this._apiKey = cfg.apiKey;
19706
+ this._environment = cfg.environment;
19707
+ this._service = cfg.service;
19653
19708
  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}`;
19709
+ const appBaseUrl = serviceUrl(cfg.scheme, "app", cfg.baseDomain);
19710
+ const configBaseUrl = serviceUrl(cfg.scheme, "config", cfg.baseDomain);
19711
+ const flagsBaseUrl = serviceUrl(cfg.scheme, "flags", cfg.baseDomain);
19712
+ const loggingBaseUrl = serviceUrl(cfg.scheme, "logging", cfg.baseDomain);
19660
19713
  this._appBaseUrl = appBaseUrl;
19661
- const maskedKey = apiKey.length > 14 ? apiKey.slice(0, 10) + "..." + apiKey.slice(-4) : apiKey.slice(0, Math.min(4, apiKey.length)) + "...";
19714
+ const maskedKey = cfg.apiKey.length > 14 ? cfg.apiKey.slice(0, 10) + "..." + cfg.apiKey.slice(-4) : cfg.apiKey.slice(0, Math.min(4, cfg.apiKey.length)) + "...";
19662
19715
  debug(
19663
19716
  "lifecycle",
19664
- `SmplClient created (api_key=${maskedKey}, environment=${environment}, service=${service})`
19717
+ `SmplClient created (api_key=${maskedKey}, environment=${cfg.environment}, service=${cfg.service})`
19665
19718
  );
19666
19719
  this._appHttp = (0, import_openapi_fetch4.default)({
19667
19720
  baseUrl: appBaseUrl,
19668
19721
  headers: {
19669
- Authorization: `Bearer ${apiKey}`,
19722
+ Authorization: `Bearer ${cfg.apiKey}`,
19670
19723
  Accept: "application/json"
19671
19724
  }
19672
19725
  });
19673
- if (!options.disableTelemetry) {
19726
+ if (!cfg.disableTelemetry) {
19674
19727
  this._metrics = new MetricsReporter({
19675
- apiKey,
19728
+ apiKey: cfg.apiKey,
19676
19729
  environment: this._environment,
19677
19730
  service: this._service,
19678
19731
  appBaseUrl
19679
19732
  });
19680
19733
  }
19681
- this.config = new ConfigClient(apiKey, this._timeout, configBaseUrl);
19734
+ this.config = new ConfigClient(cfg.apiKey, this._timeout, configBaseUrl);
19682
19735
  this.flags = new FlagsClient(
19683
- apiKey,
19736
+ cfg.apiKey,
19684
19737
  () => this._ensureWs(),
19685
19738
  this._timeout,
19686
19739
  flagsBaseUrl,
19687
19740
  appBaseUrl
19688
19741
  );
19689
- this.logging = new LoggingClient(apiKey, () => this._ensureWs(), this._timeout, loggingBaseUrl);
19742
+ this.logging = new LoggingClient(
19743
+ cfg.apiKey,
19744
+ () => this._ensureWs(),
19745
+ this._timeout,
19746
+ loggingBaseUrl
19747
+ );
19690
19748
  this.config._getSharedWs = () => this._ensureWs();
19691
19749
  this.flags._parent = this;
19692
19750
  this.config._parent = this;