stubsmith 0.2.0 → 0.2.1

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
@@ -348,7 +348,7 @@ declare function install(options?: StubsmithOptions): StubsmithClient;
348
348
  /** True while an installed client exists and is not stopped. */
349
349
  declare function isInstalled(): boolean;
350
350
 
351
- declare const VERSION = "0.2.0";
351
+ declare const VERSION = "0.2.1";
352
352
 
353
353
  interface FixturesOptions {
354
354
  /** Only `"status"`: at most one fixture per distinct HTTP status, newest first. */
package/dist/index.d.ts CHANGED
@@ -348,7 +348,7 @@ declare function install(options?: StubsmithOptions): StubsmithClient;
348
348
  /** True while an installed client exists and is not stopped. */
349
349
  declare function isInstalled(): boolean;
350
350
 
351
- declare const VERSION = "0.2.0";
351
+ declare const VERSION = "0.2.1";
352
352
 
353
353
  interface FixturesOptions {
354
354
  /** Only `"status"`: at most one fixture per distinct HTTP status, newest first. */
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/_version.ts
2
- var VERSION = "0.2.0";
2
+ var VERSION = "0.2.1";
3
3
 
4
4
  // src/instrument.ts
5
5
  import http2 from "http";
@@ -86,6 +86,31 @@ function truncateUtf8Bytes(s, n) {
86
86
  // src/client.ts
87
87
  import { gzipSync } from "zlib";
88
88
 
89
+ // src/endpoint.ts
90
+ var LOCAL_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "[::1]"]);
91
+ var InsecureEndpointError = class extends Error {
92
+ constructor(message) {
93
+ super(message);
94
+ this.name = "InsecureEndpointError";
95
+ }
96
+ };
97
+ function checkEndpoint(url) {
98
+ let u;
99
+ try {
100
+ u = new URL(url);
101
+ } catch {
102
+ throw new InsecureEndpointError(
103
+ "stubsmith: the Stubsmith URL is not a valid URL; the SDK sends only to an https URL."
104
+ );
105
+ }
106
+ if (u.protocol === "https:" && u.hostname !== "") return;
107
+ if (u.protocol === "http:" && LOCAL_HOSTS.has(u.hostname)) return;
108
+ const shown = u.hostname ? `${u.protocol}//${u.hostname}` : u.protocol;
109
+ throw new InsecureEndpointError(
110
+ `stubsmith: refusing to send to ${shown}. The SDK sends only over https; plain http is allowed only for localhost, 127.0.0.1 and ::1.`
111
+ );
112
+ }
113
+
89
114
  // src/internal/codepoint.ts
90
115
  function compareCodePoints(a, b) {
91
116
  const la = a.length;
@@ -2621,6 +2646,12 @@ function sendRequest(url, opts) {
2621
2646
  fail(err);
2622
2647
  return;
2623
2648
  }
2649
+ try {
2650
+ checkEndpoint(url);
2651
+ } catch (err) {
2652
+ fail(err);
2653
+ return;
2654
+ }
2624
2655
  const request = u.protocol === "https:" ? ORIGINAL_REQUEST.https : u.protocol === "http:" ? ORIGINAL_REQUEST.http : null;
2625
2656
  if (request === null) {
2626
2657
  fail(new Error(`unsupported protocol ${u.protocol}`));
@@ -2888,9 +2919,22 @@ var StubsmithClient = class _StubsmithClient {
2888
2919
  counters.set(this, this.counts);
2889
2920
  const env2 = readEnv();
2890
2921
  this.apiKey = options.apiKey || env2.apiKey;
2891
- this.enabled = Boolean(this.apiKey);
2892
2922
  this.url = options.url || env2.url || DEFAULT_URL;
2893
2923
  this.backendUrl = options.backendUrl || env2.backendUrl || DEFAULT_BACKEND_URL;
2924
+ let secure = true;
2925
+ if (this.apiKey) {
2926
+ try {
2927
+ checkEndpoint(this.url);
2928
+ checkEndpoint(this.backendUrl);
2929
+ } catch (err) {
2930
+ secure = false;
2931
+ try {
2932
+ console.warn(`${err instanceof Error ? err.message : String(err)} Capture is off.`);
2933
+ } catch {
2934
+ }
2935
+ }
2936
+ }
2937
+ this.enabled = Boolean(this.apiKey) && secure;
2894
2938
  this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
2895
2939
  const sr = options.sampleRate ?? 1;
2896
2940
  this.sampleRate = Number.isNaN(sr) ? 1 : Math.max(0, Math.min(1, sr));
@@ -3136,7 +3180,7 @@ var StubsmithClient = class _StubsmithClient {
3136
3180
  if (this.inflight === t) this.inflight = null;
3137
3181
  }
3138
3182
  reportIngestRefusal(res.body, res.status, this.debug);
3139
- if (res.status >= 400) {
3183
+ if (res.status >= 300) {
3140
3184
  this.sendFailures++;
3141
3185
  debugLog(`send failed - status=${res.status} url=${this.url}`);
3142
3186
  }
@@ -3854,6 +3898,7 @@ async function fixturesBundle(spec, opts = {}) {
3854
3898
  const params = new URLSearchParams({ method: method.toUpperCase(), path: path3 });
3855
3899
  if (opts.distinct !== void 0) params.set("distinct", opts.distinct);
3856
3900
  if (opts.limit !== void 0) params.set("limit", String(opts.limit));
3901
+ checkEndpoint(base);
3857
3902
  const url = `${base.replace(/\/+$/, "")}/v1/fixtures?${params.toString()}`;
3858
3903
  let res;
3859
3904
  try {
@@ -3928,6 +3973,7 @@ async function fetchOne(apiUrl, apiKey, method, p, samples) {
3928
3973
  if (method) params.set("method", method);
3929
3974
  if (p) params.set("path", p);
3930
3975
  if (samples !== null) params.set("samples", samples);
3976
+ checkEndpoint(apiUrl);
3931
3977
  const qs = [...params].length > 0 ? `?${params.toString()}` : "";
3932
3978
  const url = `${apiUrl.replace(/\/+$/, "")}/v1/replay/bundle${qs}`;
3933
3979
  let res;