@patchstack/connect 0.3.22 → 0.3.24

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.cjs CHANGED
@@ -46,6 +46,8 @@ __export(src_exports, {
46
46
  ensureWidgetInHtml: () => ensureWidgetInHtml,
47
47
  findSourceShell: () => findSourceShell,
48
48
  isEmptyStack: () => isEmptyStack,
49
+ persistApiKey: () => persistApiKey,
50
+ persistPulseAuth: () => persistPulseAuth,
49
51
  persistSiteUuid: () => persistSiteUuid,
50
52
  postManifest: () => postManifest,
51
53
  resolveConfig: () => resolveConfig,
@@ -964,6 +966,88 @@ function compareSegments(a, b) {
964
966
  return 0;
965
967
  }
966
968
 
969
+ // src/pulse-token.ts
970
+ var TOKEN_SKEW_MS = 6e4;
971
+ function buildTokenUrl(manifestEndpoint) {
972
+ const url = new URL(manifestEndpoint);
973
+ const path8 = url.pathname.replace(/\/$/, "");
974
+ url.pathname = path8.endsWith("/manifest") ? `${path8.slice(0, -"/manifest".length)}/token` : "/monitor/pulse/token";
975
+ url.search = "";
976
+ url.hash = "";
977
+ return url.toString();
978
+ }
979
+ function parsePulseAuth(credential) {
980
+ const index = credential.lastIndexOf("-");
981
+ if (index <= 0 || index === credential.length - 1) return null;
982
+ const clientId = credential.slice(index + 1);
983
+ if (!/^\d+$/.test(clientId)) return null;
984
+ return { clientId, clientSecret: credential.slice(0, index) };
985
+ }
986
+ var cached = null;
987
+ var inflight = null;
988
+ function clearPulseToken() {
989
+ cached = null;
990
+ }
991
+ async function getPulseToken(config, fetchImpl = fetch) {
992
+ if (typeof config.pulseAuth !== "string" || config.pulseAuth.length === 0) return null;
993
+ if (cached !== null && Date.now() < cached.expiresAt - TOKEN_SKEW_MS) {
994
+ return cached.token;
995
+ }
996
+ if (inflight !== null) return inflight;
997
+ const credentials = parsePulseAuth(config.pulseAuth);
998
+ if (credentials === null) return null;
999
+ inflight = (async () => {
1000
+ try {
1001
+ const response = await fetchImpl(buildTokenUrl(config.endpoint), {
1002
+ method: "POST",
1003
+ headers: {
1004
+ "Content-Type": "application/json",
1005
+ Accept: "application/json",
1006
+ "User-Agent": "@patchstack/connect"
1007
+ },
1008
+ body: JSON.stringify({
1009
+ grant_type: "client_credentials",
1010
+ client_id: credentials.clientId,
1011
+ client_secret: credentials.clientSecret
1012
+ }),
1013
+ signal: AbortSignal.timeout(config.timeoutMs)
1014
+ });
1015
+ if (!response.ok) return null;
1016
+ const body = await response.json();
1017
+ if (typeof body.access_token !== "string" || body.access_token.length === 0) return null;
1018
+ const expiresIn = Number(body.expires_in);
1019
+ const ttlMs = Number.isFinite(expiresIn) && expiresIn > 0 ? expiresIn * 1e3 : 36e5;
1020
+ cached = { token: body.access_token, expiresAt: Date.now() + ttlMs };
1021
+ return body.access_token;
1022
+ } catch {
1023
+ return null;
1024
+ } finally {
1025
+ inflight = null;
1026
+ }
1027
+ })();
1028
+ return inflight;
1029
+ }
1030
+ async function pulseAuthHeader(config, fetchImpl = fetch) {
1031
+ const token = await getPulseToken(config, fetchImpl);
1032
+ return token === null ? {} : { Authorization: `Bearer ${token}` };
1033
+ }
1034
+ async function pulseFetch(config, url, init, fetchImpl = fetch) {
1035
+ const send = async () => {
1036
+ const auth = await pulseAuthHeader(config, fetchImpl);
1037
+ const response = await fetchImpl(url, {
1038
+ ...init,
1039
+ headers: { ...init.headers, ...auth }
1040
+ });
1041
+ return { response, authenticated: auth.Authorization !== void 0 };
1042
+ };
1043
+ const first = await send();
1044
+ if (first.response.status === 401 && first.authenticated) {
1045
+ clearPulseToken();
1046
+ return (await send()).response;
1047
+ }
1048
+ return first.response;
1049
+ }
1050
+
967
1051
  // src/client.ts
968
1052
  var DEFAULT_ENDPOINT = "https://api.patchstack.com/monitor/pulse/manifest";
969
1053
  var DEFAULT_TIMEOUT_MS = 3e4;
@@ -980,7 +1064,7 @@ async function postManifest(config, payload) {
980
1064
  const timeoutMs = config.timeoutMs;
981
1065
  let response;
982
1066
  try {
983
- response = await fetch(url, {
1067
+ response = await pulseFetch(config, url, {
984
1068
  method: "POST",
985
1069
  headers: {
986
1070
  "Content-Type": "application/json",
@@ -1072,8 +1156,12 @@ async function resolveConfig(options) {
1072
1156
  "CONFIG_MISSING"
1073
1157
  );
1074
1158
  }
1159
+ const apiKeyRaw = fromEnv.apiKey ?? fromFile.apiKey ?? null;
1160
+ const pulseAuthRaw = fromEnv.pulseAuth ?? fromFile.pulseAuth ?? apiKeyRaw;
1075
1161
  return {
1076
1162
  siteUuid: siteUuid === null || siteUuid.length === 0 ? null : siteUuid,
1163
+ apiKey: apiKeyRaw === null || apiKeyRaw.length === 0 ? null : apiKeyRaw,
1164
+ pulseAuth: pulseAuthRaw === null || pulseAuthRaw.length === 0 ? null : pulseAuthRaw,
1077
1165
  endpoint,
1078
1166
  timeoutMs,
1079
1167
  environment,
@@ -1090,6 +1178,14 @@ async function persistSiteUuid(cwd, siteUuid) {
1090
1178
  const existing = await readConfigFile(cwd);
1091
1179
  return writeConfigFile(cwd, { ...existing, siteUuid });
1092
1180
  }
1181
+ async function persistApiKey(cwd, apiKey) {
1182
+ const existing = await readConfigFile(cwd);
1183
+ return writeConfigFile(cwd, { ...existing, apiKey });
1184
+ }
1185
+ async function persistPulseAuth(cwd, pulseAuth) {
1186
+ const existing = await readConfigFile(cwd);
1187
+ return writeConfigFile(cwd, { ...existing, pulseAuth });
1188
+ }
1093
1189
  async function readConfigFile(cwd) {
1094
1190
  const target = import_node_path6.default.join(cwd, CONFIG_FILENAME);
1095
1191
  let raw;
@@ -1131,6 +1227,8 @@ function readEnv() {
1131
1227
  const environmentRaw = process.env.PATCHSTACK_ENVIRONMENT;
1132
1228
  return {
1133
1229
  siteUuid: process.env.PATCHSTACK_SITE_UUID ?? void 0,
1230
+ apiKey: process.env.PATCHSTACK_API_KEY ?? void 0,
1231
+ pulseAuth: process.env.PATCHSTACK_PULSE_AUTH ?? void 0,
1134
1232
  endpoint: process.env.PATCHSTACK_ENDPOINT ?? void 0,
1135
1233
  timeoutMs,
1136
1234
  environment: environmentRaw !== void 0 && environmentRaw.length > 0 ? environmentRaw : void 0
@@ -1299,6 +1397,10 @@ async function scanAndReport(options = {}) {
1299
1397
  if (config.siteUuid === null && response.uuid !== void 0 && response.uuid.length > 0) {
1300
1398
  await persistSiteUuid(cwd, response.uuid);
1301
1399
  }
1400
+ if (typeof response.api_key === "string" && response.api_key.length > 0) {
1401
+ await persistApiKey(cwd, response.api_key);
1402
+ await persistPulseAuth(cwd, response.api_key);
1403
+ }
1302
1404
  return {
1303
1405
  manifest,
1304
1406
  response,
@@ -1325,6 +1427,8 @@ async function scanAndReport(options = {}) {
1325
1427
  ensureWidgetInHtml,
1326
1428
  findSourceShell,
1327
1429
  isEmptyStack,
1430
+ persistApiKey,
1431
+ persistPulseAuth,
1328
1432
  persistSiteUuid,
1329
1433
  postManifest,
1330
1434
  resolveConfig,