@smplkit/sdk 1.2.6 → 1.3.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
@@ -483,9 +483,9 @@ declare class Rule {
483
483
  * FlagsClient — management + prescriptive runtime for Smpl Flags.
484
484
  *
485
485
  * Uses the generated OpenAPI types (`src/generated/flags.d.ts`) via
486
- * `openapi-fetch` for all HTTP calls. Context type management and
487
- * context registration use direct HTTP via the Transport class since
488
- * these endpoints are on the flags service but not in the generated spec.
486
+ * `openapi-fetch` for all flag HTTP calls. Context type management and
487
+ * context registration use the generated app service types
488
+ * (`src/generated/app.d.ts`) via a separate `openapi-fetch` client.
489
489
  */
490
490
 
491
491
  /** Describes a flag definition change. */
@@ -552,7 +552,7 @@ declare class FlagsClient {
552
552
  /** @internal */
553
553
  private readonly _http;
554
554
  /** @internal */
555
- private readonly _transport;
555
+ private readonly _appHttp;
556
556
  private _environment;
557
557
  private _flagStore;
558
558
  private _connected;
@@ -609,6 +609,8 @@ declare class FlagsClient {
609
609
  }): Promise<ContextType>;
610
610
  /** Update a context type (merge attributes). */
611
611
  updateContextType(ctId: string, options: {
612
+ key: string;
613
+ name: string;
612
614
  attributes: Record<string, any>;
613
615
  }): Promise<ContextType>;
614
616
  /** List all context types. */
@@ -760,6 +762,7 @@ declare class SmplClient {
760
762
  readonly _service: string | null;
761
763
  private _connected;
762
764
  private readonly _timeout;
765
+ private readonly _appHttp;
763
766
  constructor(options?: SmplClientOptions);
764
767
  /**
765
768
  * Connect to the smplkit platform.
package/dist/index.d.ts CHANGED
@@ -483,9 +483,9 @@ declare class Rule {
483
483
  * FlagsClient — management + prescriptive runtime for Smpl Flags.
484
484
  *
485
485
  * Uses the generated OpenAPI types (`src/generated/flags.d.ts`) via
486
- * `openapi-fetch` for all HTTP calls. Context type management and
487
- * context registration use direct HTTP via the Transport class since
488
- * these endpoints are on the flags service but not in the generated spec.
486
+ * `openapi-fetch` for all flag HTTP calls. Context type management and
487
+ * context registration use the generated app service types
488
+ * (`src/generated/app.d.ts`) via a separate `openapi-fetch` client.
489
489
  */
490
490
 
491
491
  /** Describes a flag definition change. */
@@ -552,7 +552,7 @@ declare class FlagsClient {
552
552
  /** @internal */
553
553
  private readonly _http;
554
554
  /** @internal */
555
- private readonly _transport;
555
+ private readonly _appHttp;
556
556
  private _environment;
557
557
  private _flagStore;
558
558
  private _connected;
@@ -609,6 +609,8 @@ declare class FlagsClient {
609
609
  }): Promise<ContextType>;
610
610
  /** Update a context type (merge attributes). */
611
611
  updateContextType(ctId: string, options: {
612
+ key: string;
613
+ name: string;
612
614
  attributes: Record<string, any>;
613
615
  }): Promise<ContextType>;
614
616
  /** List all context types. */
@@ -760,6 +762,7 @@ declare class SmplClient {
760
762
  readonly _service: string | null;
761
763
  private _connected;
762
764
  private readonly _timeout;
765
+ private readonly _appHttp;
763
766
  constructor(options?: SmplClientOptions);
764
767
  /**
765
768
  * Connect to the smplkit platform.
package/dist/index.js CHANGED
@@ -1,3 +1,6 @@
1
+ // src/client.ts
2
+ import createClient3 from "openapi-fetch";
3
+
1
4
  // src/config/client.ts
2
5
  import createClient from "openapi-fetch";
3
6
 
@@ -664,135 +667,6 @@ var ConfigClient = class {
664
667
  // src/flags/client.ts
665
668
  import createClient2 from "openapi-fetch";
666
669
 
667
- // src/auth.ts
668
- function buildAuthHeader(apiKey) {
669
- return `Bearer ${apiKey}`;
670
- }
671
-
672
- // src/transport.ts
673
- var SDK_VERSION = "0.0.0";
674
- var DEFAULT_TIMEOUT_MS = 3e4;
675
- var Transport = class {
676
- apiKey;
677
- timeout;
678
- constructor(options) {
679
- this.apiKey = options.apiKey;
680
- this.timeout = options.timeout ?? DEFAULT_TIMEOUT_MS;
681
- }
682
- /**
683
- * Send a GET request.
684
- *
685
- * @param url - Fully-qualified URL (e.g. `https://config.smplkit.com/api/v1/configs`).
686
- * @param params - Optional query parameters.
687
- * @returns Parsed JSON response body.
688
- */
689
- async get(url, params) {
690
- return this.request("GET", url, void 0, params);
691
- }
692
- /**
693
- * Send a POST request with a JSON body.
694
- *
695
- * @param url - Fully-qualified URL.
696
- * @param body - JSON-serializable request body.
697
- * @returns Parsed JSON response body.
698
- */
699
- async post(url, body) {
700
- return this.request("POST", url, body);
701
- }
702
- /**
703
- * Send a PUT request with a JSON body.
704
- *
705
- * @param url - Fully-qualified URL.
706
- * @param body - JSON-serializable request body.
707
- * @returns Parsed JSON response body.
708
- */
709
- async put(url, body) {
710
- return this.request("PUT", url, body);
711
- }
712
- /**
713
- * Send a DELETE request.
714
- *
715
- * @param url - Fully-qualified URL.
716
- * @returns Parsed JSON response body (empty object for 204 responses).
717
- */
718
- async delete(url) {
719
- return this.request("DELETE", url);
720
- }
721
- /**
722
- * Core request method. Handles headers, timeouts, and error mapping.
723
- */
724
- async request(method, url, body, params) {
725
- if (params) {
726
- const searchParams = new URLSearchParams(params);
727
- url += `?${searchParams.toString()}`;
728
- }
729
- const headers = {
730
- Authorization: buildAuthHeader(this.apiKey),
731
- "User-Agent": `smplkit-typescript-sdk/${SDK_VERSION}`,
732
- Accept: "application/vnd.api+json"
733
- };
734
- if (body !== void 0) {
735
- headers["Content-Type"] = "application/vnd.api+json";
736
- }
737
- const controller = new AbortController();
738
- const timeoutId = setTimeout(() => controller.abort(), this.timeout);
739
- let response;
740
- try {
741
- response = await fetch(url, {
742
- method,
743
- headers,
744
- body: body !== void 0 ? JSON.stringify(body) : void 0,
745
- signal: controller.signal
746
- });
747
- } catch (error) {
748
- clearTimeout(timeoutId);
749
- if (error instanceof DOMException && error.name === "AbortError") {
750
- throw new SmplTimeoutError(`Request timed out after ${this.timeout}ms`);
751
- }
752
- if (error instanceof TypeError) {
753
- throw new SmplConnectionError(`Network error: ${error.message}`);
754
- }
755
- throw new SmplConnectionError(
756
- `Request failed: ${error instanceof Error ? error.message : String(error)}`
757
- );
758
- } finally {
759
- clearTimeout(timeoutId);
760
- }
761
- if (response.status === 204) {
762
- return {};
763
- }
764
- const responseText = await response.text();
765
- if (!response.ok) {
766
- this.throwForStatus(response.status, responseText);
767
- }
768
- try {
769
- return JSON.parse(responseText);
770
- } catch {
771
- throw new SmplError(`Invalid JSON response: ${responseText}`, response.status, responseText);
772
- }
773
- }
774
- /**
775
- * Map HTTP error status codes to typed SDK exceptions.
776
- *
777
- * @throws {SmplNotFoundError} On 404.
778
- * @throws {SmplConflictError} On 409.
779
- * @throws {SmplValidationError} On 422.
780
- * @throws {SmplError} On any other non-2xx status.
781
- */
782
- throwForStatus(status, body) {
783
- switch (status) {
784
- case 404:
785
- throw new SmplNotFoundError(body, 404, body);
786
- case 409:
787
- throw new SmplConflictError(body, 409, body);
788
- case 422:
789
- throw new SmplValidationError(body, 422, body);
790
- default:
791
- throw new SmplError(`HTTP ${status}: ${body}`, status, body);
792
- }
793
- }
794
- };
795
-
796
670
  // src/flags/models.ts
797
671
  var Flag = class {
798
672
  /** UUID of the flag. */
@@ -1144,8 +1018,8 @@ var ContextRegistrationBuffer = class {
1144
1018
  }
1145
1019
  this._seen.set(cacheKey, ctx.attributes);
1146
1020
  this._pending.push({
1147
- id: `${ctx.type}:${ctx.key}`,
1148
- name: ctx.name ?? ctx.key,
1021
+ type: ctx.type,
1022
+ key: ctx.key,
1149
1023
  attributes: { ...ctx.attributes }
1150
1024
  });
1151
1025
  }
@@ -1168,7 +1042,7 @@ var FlagsClient = class {
1168
1042
  /** @internal */
1169
1043
  _http;
1170
1044
  /** @internal */
1171
- _transport;
1045
+ _appHttp;
1172
1046
  // Runtime state
1173
1047
  _environment = null;
1174
1048
  _flagStore = {};
@@ -1188,28 +1062,36 @@ var FlagsClient = class {
1188
1062
  this._apiKey = apiKey;
1189
1063
  this._ensureWs = ensureWs;
1190
1064
  const ms = timeout ?? 3e4;
1065
+ const fetchWithTimeout = async (request) => {
1066
+ const controller = new AbortController();
1067
+ const timer = setTimeout(() => controller.abort(), ms);
1068
+ try {
1069
+ return await fetch(new Request(request, { signal: controller.signal }));
1070
+ } catch (err) {
1071
+ if (err instanceof DOMException && err.name === "AbortError") {
1072
+ throw new SmplTimeoutError(`Request timed out after ${ms}ms`);
1073
+ }
1074
+ throw err;
1075
+ } finally {
1076
+ clearTimeout(timer);
1077
+ }
1078
+ };
1191
1079
  this._http = createClient2({
1192
1080
  baseUrl: FLAGS_BASE_URL,
1193
1081
  headers: {
1194
1082
  Authorization: `Bearer ${apiKey}`,
1195
1083
  Accept: "application/json"
1196
1084
  },
1197
- fetch: async (request) => {
1198
- const controller = new AbortController();
1199
- const timer = setTimeout(() => controller.abort(), ms);
1200
- try {
1201
- return await fetch(new Request(request, { signal: controller.signal }));
1202
- } catch (err) {
1203
- if (err instanceof DOMException && err.name === "AbortError") {
1204
- throw new SmplTimeoutError(`Request timed out after ${ms}ms`);
1205
- }
1206
- throw err;
1207
- } finally {
1208
- clearTimeout(timer);
1209
- }
1210
- }
1085
+ fetch: fetchWithTimeout
1086
+ });
1087
+ this._appHttp = createClient2({
1088
+ baseUrl: APP_BASE_URL,
1089
+ headers: {
1090
+ Authorization: `Bearer ${apiKey}`,
1091
+ Accept: "application/json"
1092
+ },
1093
+ fetch: fetchWithTimeout
1211
1094
  });
1212
- this._transport = new Transport({ apiKey, timeout: ms });
1213
1095
  }
1214
1096
  // ------------------------------------------------------------------
1215
1097
  // Management methods
@@ -1324,40 +1206,87 @@ var FlagsClient = class {
1324
1206
  return this._resourceToModel(data.data);
1325
1207
  }
1326
1208
  // ------------------------------------------------------------------
1327
- // Context type management (direct HTTP not in generated spec)
1209
+ // Context type management (via generated app client)
1328
1210
  // ------------------------------------------------------------------
1329
1211
  /** Create a context type. */
1330
1212
  async createContextType(key, options) {
1331
- const resp = await this._transport.post(`${APP_BASE_URL}/api/v1/context_types`, {
1332
- data: { type: "context_type", attributes: { key, name: options.name } }
1333
- });
1334
- const data = resp.data ?? {};
1335
- return this._parseContextType(data);
1213
+ let data;
1214
+ try {
1215
+ const result = await this._appHttp.POST("/api/v1/context_types", {
1216
+ body: {
1217
+ data: { type: "context_type", attributes: { key, name: options.name } }
1218
+ }
1219
+ });
1220
+ if (result.error !== void 0)
1221
+ await checkError2(result.response, "Failed to create context type");
1222
+ data = result.data;
1223
+ } catch (err) {
1224
+ wrapFetchError2(err);
1225
+ }
1226
+ if (!data || !data.data) throw new SmplValidationError("Failed to create context type");
1227
+ return this._parseContextType(data.data);
1336
1228
  }
1337
1229
  /** Update a context type (merge attributes). */
1338
1230
  async updateContextType(ctId, options) {
1339
- const resp = await this._transport.put(`${APP_BASE_URL}/api/v1/context_types/${ctId}`, {
1340
- data: { type: "context_type", attributes: { attributes: options.attributes } }
1341
- });
1342
- const data = resp.data ?? {};
1343
- return this._parseContextType(data);
1231
+ let data;
1232
+ try {
1233
+ const result = await this._appHttp.PUT("/api/v1/context_types/{id}", {
1234
+ params: { path: { id: ctId } },
1235
+ body: {
1236
+ data: {
1237
+ type: "context_type",
1238
+ attributes: { key: options.key, name: options.name, attributes: options.attributes }
1239
+ }
1240
+ }
1241
+ });
1242
+ if (result.error !== void 0)
1243
+ await checkError2(result.response, `Failed to update context type ${ctId}`);
1244
+ data = result.data;
1245
+ } catch (err) {
1246
+ wrapFetchError2(err);
1247
+ }
1248
+ if (!data || !data.data) throw new SmplValidationError(`Failed to update context type ${ctId}`);
1249
+ return this._parseContextType(data.data);
1344
1250
  }
1345
1251
  /** List all context types. */
1346
1252
  async listContextTypes() {
1347
- const resp = await this._transport.get(`${APP_BASE_URL}/api/v1/context_types`);
1348
- const items = resp.data ?? [];
1349
- return items.map((item) => this._parseContextType(item));
1253
+ let data;
1254
+ try {
1255
+ const result = await this._appHttp.GET("/api/v1/context_types");
1256
+ if (result.error !== void 0)
1257
+ await checkError2(result.response, "Failed to list context types");
1258
+ data = result.data;
1259
+ } catch (err) {
1260
+ wrapFetchError2(err);
1261
+ }
1262
+ if (!data || !data.data) throw new SmplValidationError("Failed to list context types");
1263
+ return data.data.map((item) => this._parseContextType(item));
1350
1264
  }
1351
1265
  /** Delete a context type. */
1352
1266
  async deleteContextType(ctId) {
1353
- await this._transport.delete(`${APP_BASE_URL}/api/v1/context_types/${ctId}`);
1267
+ try {
1268
+ const result = await this._appHttp.DELETE("/api/v1/context_types/{id}", {
1269
+ params: { path: { id: ctId } }
1270
+ });
1271
+ if (result.error !== void 0 && result.response.status !== 204)
1272
+ await checkError2(result.response, `Failed to delete context type ${ctId}`);
1273
+ } catch (err) {
1274
+ wrapFetchError2(err);
1275
+ }
1354
1276
  }
1355
1277
  /** List context instances filtered by context type key. */
1356
1278
  async listContexts(options) {
1357
- const resp = await this._transport.get(`${APP_BASE_URL}/api/v1/contexts`, {
1358
- "filter[context_type]": options.contextTypeKey
1359
- });
1360
- return resp.data ?? [];
1279
+ let data;
1280
+ try {
1281
+ const result = await this._appHttp.GET("/api/v1/contexts", {
1282
+ params: { query: { "filter[context_type_id]": options.contextTypeKey } }
1283
+ });
1284
+ if (result.error !== void 0) await checkError2(result.response, "Failed to list contexts");
1285
+ data = result.data;
1286
+ } catch (err) {
1287
+ wrapFetchError2(err);
1288
+ }
1289
+ return data?.data ?? [];
1361
1290
  }
1362
1291
  // ------------------------------------------------------------------
1363
1292
  // Runtime: typed flag handles
@@ -1647,8 +1576,13 @@ var FlagsClient = class {
1647
1576
  const batch = this._contextBuffer.drain();
1648
1577
  if (batch.length === 0) return;
1649
1578
  try {
1650
- await this._transport.post(`${APP_BASE_URL}/api/v1/contexts/bulk`, {
1651
- contexts: batch
1579
+ await this._appHttp.POST("/api/v1/contexts/bulk", {
1580
+ body: {
1581
+ contexts: batch.map((ctx) => ({
1582
+ id: `${ctx.type}:${ctx.key}`,
1583
+ attributes: ctx.attributes
1584
+ }))
1585
+ }
1652
1586
  });
1653
1587
  } catch {
1654
1588
  }
@@ -1894,6 +1828,7 @@ var SmplClient = class {
1894
1828
  _service;
1895
1829
  _connected = false;
1896
1830
  _timeout;
1831
+ _appHttp;
1897
1832
  constructor(options = {}) {
1898
1833
  const apiKey = resolveApiKey(options.apiKey);
1899
1834
  this._apiKey = apiKey;
@@ -1904,6 +1839,28 @@ var SmplClient = class {
1904
1839
  this._environment = environment;
1905
1840
  this._service = options.service || process.env.SMPLKIT_SERVICE || null;
1906
1841
  this._timeout = options.timeout ?? 3e4;
1842
+ const ms = this._timeout;
1843
+ this._appHttp = createClient3({
1844
+ baseUrl: APP_BASE_URL2,
1845
+ headers: {
1846
+ Authorization: `Bearer ${apiKey}`,
1847
+ Accept: "application/json"
1848
+ },
1849
+ fetch: async (request) => {
1850
+ const controller = new AbortController();
1851
+ const timer = setTimeout(() => controller.abort(), ms);
1852
+ try {
1853
+ return await fetch(new Request(request, { signal: controller.signal }));
1854
+ } catch (err) {
1855
+ if (err instanceof DOMException && err.name === "AbortError") {
1856
+ throw new SmplTimeoutError(`Request timed out after ${ms}ms`);
1857
+ }
1858
+ throw err;
1859
+ } finally {
1860
+ clearTimeout(timer);
1861
+ }
1862
+ }
1863
+ });
1907
1864
  this.config = new ConfigClient(apiKey, this._timeout);
1908
1865
  this.flags = new FlagsClient(apiKey, () => this._ensureWs(), this._timeout);
1909
1866
  this.config._getSharedWs = () => this._ensureWs();
@@ -1930,21 +1887,15 @@ var SmplClient = class {
1930
1887
  /** @internal */
1931
1888
  async _registerServiceContext() {
1932
1889
  try {
1933
- await fetch(`${APP_BASE_URL2}/api/v1/contexts/bulk`, {
1934
- method: "PUT",
1935
- headers: {
1936
- Authorization: `Bearer ${this._apiKey}`,
1937
- "Content-Type": "application/json"
1938
- },
1939
- body: JSON.stringify({
1890
+ await this._appHttp.POST("/api/v1/contexts/bulk", {
1891
+ body: {
1940
1892
  contexts: [
1941
1893
  {
1942
- type: "service",
1943
- key: this._service,
1894
+ id: `service:${this._service}`,
1944
1895
  attributes: { name: this._service }
1945
1896
  }
1946
1897
  ]
1947
- })
1898
+ }
1948
1899
  });
1949
1900
  } catch {
1950
1901
  }