@testsmith/api-spector 0.7.4 → 0.7.6

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.
@@ -3,7 +3,7 @@ const promises = require("node:fs/promises");
3
3
  const path = require("node:path");
4
4
  const crypto = require("node:crypto");
5
5
  const undici = require("undici");
6
- const requestExec = require("./request-exec-CcLHtkwG.js");
6
+ const requestExec = require("./request-exec-CwJ8_OKC.js");
7
7
  const Ajv = require("ajv");
8
8
  const jsYaml = require("js-yaml");
9
9
  require("node:http");
@@ -73,6 +73,7 @@ const IPC = {
73
73
  // ─── Import ────────────────────────────────────────────────────────────────
74
74
  import: {
75
75
  postman: "import:postman",
76
+ postmanEnvironment: "import:postman-environment",
76
77
  openapi: "import:openapi",
77
78
  openapiUrl: "import:openapi-url",
78
79
  insomnia: "import:insomnia",
@@ -1,8 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const uuid = require("uuid");
4
- const soapHandler = require("./soap-handler-CkoLJHoI.js");
5
- require("./handle-BY_jaVBT.js");
4
+ const soapHandler = require("./soap-handler-DTfFnzNY.js");
5
+ require("./handle-DRmKOL-s.js");
6
6
  require("node:https");
7
7
  require("node:http");
8
8
  require("@xmldom/xmldom");
@@ -24,7 +24,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
24
24
  const undici = require("undici");
25
25
  const promises$1 = require("fs/promises");
26
26
  const nodeTls = require("tls");
27
- const handle = require("./handle-BY_jaVBT.js");
27
+ const handle = require("./handle-DRmKOL-s.js");
28
28
  const fs = require("node:fs");
29
29
  const os = require("node:os");
30
30
  const path = require("node:path");
@@ -114,7 +114,7 @@ function setSecretsConfig(settings) {
114
114
  function getSecretsConfig() {
115
115
  return current;
116
116
  }
117
- function env$3(...names) {
117
+ function env$4(...names) {
118
118
  for (const n of names) {
119
119
  const v = process.env[n];
120
120
  if (v !== void 0 && v !== "") return v;
@@ -128,22 +128,22 @@ function readFileTrim(path2) {
128
128
  return void 0;
129
129
  }
130
130
  }
131
- function resolveConn$3() {
131
+ function resolveConn$4() {
132
132
  const cfg = getSecretsConfig()?.vault ?? {};
133
- const address = env$3("VAULT_ADDR", "API_SPECTOR_VAULT_ADDR") ?? cfg.address;
133
+ const address = env$4("VAULT_ADDR", "API_SPECTOR_VAULT_ADDR") ?? cfg.address;
134
134
  if (!address) {
135
135
  throw new Error("Vault is not configured: set VAULT_ADDR (or settings.secrets.vault.address)");
136
136
  }
137
- const token = env$3("VAULT_TOKEN", "API_SPECTOR_VAULT_TOKEN") ?? readFileTrim(path.join(os.homedir(), ".vault-token"));
138
- const roleId = env$3("VAULT_ROLE_ID", "API_SPECTOR_VAULT_ROLE_ID") ?? cfg.roleId;
139
- const secretId = env$3("VAULT_SECRET_ID", "API_SPECTOR_VAULT_SECRET_ID");
140
- const jwt = env$3("VAULT_JWT", "API_SPECTOR_VAULT_JWT") ?? (env$3("VAULT_JWT_PATH", "API_SPECTOR_VAULT_JWT_PATH") ? readFileTrim(env$3("VAULT_JWT_PATH", "API_SPECTOR_VAULT_JWT_PATH")) : void 0);
141
- const jwtRole = env$3("VAULT_JWT_ROLE", "API_SPECTOR_VAULT_JWT_ROLE") ?? cfg.jwtRole;
142
- const explicit = env$3("VAULT_AUTH_METHOD", "API_SPECTOR_VAULT_AUTH_METHOD") ?? cfg.authMethod;
137
+ const token = env$4("VAULT_TOKEN", "API_SPECTOR_VAULT_TOKEN") ?? readFileTrim(path.join(os.homedir(), ".vault-token"));
138
+ const roleId = env$4("VAULT_ROLE_ID", "API_SPECTOR_VAULT_ROLE_ID") ?? cfg.roleId;
139
+ const secretId = env$4("VAULT_SECRET_ID", "API_SPECTOR_VAULT_SECRET_ID");
140
+ const jwt = env$4("VAULT_JWT", "API_SPECTOR_VAULT_JWT") ?? (env$4("VAULT_JWT_PATH", "API_SPECTOR_VAULT_JWT_PATH") ? readFileTrim(env$4("VAULT_JWT_PATH", "API_SPECTOR_VAULT_JWT_PATH")) : void 0);
141
+ const jwtRole = env$4("VAULT_JWT_ROLE", "API_SPECTOR_VAULT_JWT_ROLE") ?? cfg.jwtRole;
142
+ const explicit = env$4("VAULT_AUTH_METHOD", "API_SPECTOR_VAULT_AUTH_METHOD") ?? cfg.authMethod;
143
143
  const authMethod = explicit ?? (token ? "token" : roleId ? "approle" : jwt ? "jwt" : "token");
144
144
  return {
145
145
  address: address.replace(/\/+$/, ""),
146
- namespace: env$3("VAULT_NAMESPACE", "API_SPECTOR_VAULT_NAMESPACE") ?? cfg.namespace,
146
+ namespace: env$4("VAULT_NAMESPACE", "API_SPECTOR_VAULT_NAMESPACE") ?? cfg.namespace,
147
147
  authMethod,
148
148
  token,
149
149
  roleId,
@@ -151,13 +151,13 @@ function resolveConn$3() {
151
151
  jwt,
152
152
  jwtRole,
153
153
  loginMount: cfg.loginMount,
154
- kvVersion: env$3("VAULT_KV_VERSION", "API_SPECTOR_VAULT_KV_VERSION") ?? cfg.kvVersion ?? "auto",
155
- skipVerify: (env$3("VAULT_SKIP_VERIFY", "API_SPECTOR_VAULT_SKIP_VERIFY") ?? "").toLowerCase() === "true" || cfg.skipVerify === true,
156
- caPath: env$3("VAULT_CACERT", "API_SPECTOR_VAULT_CACERT")
154
+ kvVersion: env$4("VAULT_KV_VERSION", "API_SPECTOR_VAULT_KV_VERSION") ?? cfg.kvVersion ?? "auto",
155
+ skipVerify: (env$4("VAULT_SKIP_VERIFY", "API_SPECTOR_VAULT_SKIP_VERIFY") ?? "").toLowerCase() === "true" || cfg.skipVerify === true,
156
+ caPath: env$4("VAULT_CACERT", "API_SPECTOR_VAULT_CACERT")
157
157
  };
158
158
  }
159
- let cachedToken$1 = null;
160
- const secretCache$1 = /* @__PURE__ */ new Map();
159
+ let cachedToken$2 = null;
160
+ const secretCache$2 = /* @__PURE__ */ new Map();
161
161
  let _dispatcher;
162
162
  let _dispatcherKey = "";
163
163
  async function dispatcherFor(conn) {
@@ -200,7 +200,7 @@ async function clientToken(conn) {
200
200
  return conn.token;
201
201
  }
202
202
  const now = Date.now();
203
- if (cachedToken$1 && cachedToken$1.expiresAt > now + 5e3) return cachedToken$1.value;
203
+ if (cachedToken$2 && cachedToken$2.expiresAt > now + 5e3) return cachedToken$2.value;
204
204
  let mount;
205
205
  let body;
206
206
  if (conn.authMethod === "approle") {
@@ -224,7 +224,7 @@ async function clientToken(conn) {
224
224
  const token = json?.auth?.client_token;
225
225
  if (!token) throw new Error("Vault login did not return a client token");
226
226
  const ttl = Number(json?.auth?.lease_duration ?? 0);
227
- cachedToken$1 = { value: token, expiresAt: ttl > 0 ? now + ttl * 1e3 : now + 5 * 6e4 };
227
+ cachedToken$2 = { value: token, expiresAt: ttl > 0 ? now + ttl * 1e3 : now + 5 * 6e4 };
228
228
  return token;
229
229
  }
230
230
  async function readSecret(refBody) {
@@ -237,11 +237,11 @@ async function readSecret(refBody) {
237
237
  if (!path2 || !key) {
238
238
  throw new Error(`Vault reference must be 'vault:<path>#<key>' (got 'vault:${refBody}')`);
239
239
  }
240
- const conn = resolveConn$3();
240
+ const conn = resolveConn$4();
241
241
  const cacheKey = `${conn.address}|${conn.namespace ?? ""}|${path2}`;
242
242
  const now = Date.now();
243
243
  let bag;
244
- const hit = secretCache$1.get(cacheKey);
244
+ const hit = secretCache$2.get(cacheKey);
245
245
  if (hit && hit.expiresAt > now) {
246
246
  bag = hit.bag;
247
247
  } else {
@@ -250,7 +250,7 @@ async function readSecret(refBody) {
250
250
  bag = isV2 ? json?.data?.data : json?.data;
251
251
  const lease = Number(json?.lease_duration ?? 0);
252
252
  if (bag) {
253
- secretCache$1.set(cacheKey, { bag, expiresAt: lease > 0 ? now + lease * 1e3 : now + 5 * 6e4 });
253
+ secretCache$2.set(cacheKey, { bag, expiresAt: lease > 0 ? now + lease * 1e3 : now + 5 * 6e4 });
254
254
  }
255
255
  }
256
256
  if (!bag || !(key in bag)) {
@@ -293,23 +293,23 @@ function signSigV4(input) {
293
293
  Authorization: `AWS4-HMAC-SHA256 Credential=${input.accessKeyId}/${scope}, SignedHeaders=${signedHeaders}, Signature=${signature}`
294
294
  };
295
295
  }
296
- function env$2(...names) {
296
+ function env$3(...names) {
297
297
  for (const n of names) {
298
298
  const v = process.env[n];
299
299
  if (v !== void 0 && v !== "") return v;
300
300
  }
301
301
  return void 0;
302
302
  }
303
- function resolveConn$2() {
303
+ function resolveConn$3() {
304
304
  const cfg = getSecretsConfig()?.aws ?? {};
305
- const region = env$2("AWS_REGION", "AWS_DEFAULT_REGION", "API_SPECTOR_AWS_REGION") ?? cfg.region;
306
- const accessKeyId = env$2("AWS_ACCESS_KEY_ID");
307
- const secretAccessKey = env$2("AWS_SECRET_ACCESS_KEY");
305
+ const region = env$3("AWS_REGION", "AWS_DEFAULT_REGION", "API_SPECTOR_AWS_REGION") ?? cfg.region;
306
+ const accessKeyId = env$3("AWS_ACCESS_KEY_ID");
307
+ const secretAccessKey = env$3("AWS_SECRET_ACCESS_KEY");
308
308
  if (!region) throw new Error("AWS Secrets Manager: no region (set AWS_REGION or settings.secrets.aws.region)");
309
309
  if (!accessKeyId || !secretAccessKey) {
310
310
  throw new Error("AWS Secrets Manager: no credentials (set AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY)");
311
311
  }
312
- return { region, accessKeyId, secretAccessKey, sessionToken: env$2("AWS_SESSION_TOKEN") };
312
+ return { region, accessKeyId, secretAccessKey, sessionToken: env$3("AWS_SESSION_TOKEN") };
313
313
  }
314
314
  const cache = /* @__PURE__ */ new Map();
315
315
  async function getSecretString(conn, secretId) {
@@ -346,12 +346,12 @@ async function getSecretString(conn, secretId) {
346
346
  cache.set(cacheKey, { raw, expiresAt: now + 5 * 6e4 });
347
347
  return raw;
348
348
  }
349
- async function resolve$2(refBody) {
349
+ async function resolve$3(refBody) {
350
350
  const hash = refBody.lastIndexOf("#");
351
351
  const secretId = hash < 0 ? refBody : refBody.slice(0, hash);
352
352
  const key = hash < 0 ? null : refBody.slice(hash + 1);
353
353
  if (!secretId) throw new Error(`AWS reference must be 'aws:<secretId>[#<key>]' (got 'aws:${refBody}')`);
354
- const raw = await getSecretString(resolveConn$2(), secretId);
354
+ const raw = await getSecretString(resolveConn$3(), secretId);
355
355
  if (key === null) return raw;
356
356
  let parsed;
357
357
  try {
@@ -364,30 +364,30 @@ async function resolve$2(refBody) {
364
364
  }
365
365
  const awsSecretsProvider = {
366
366
  scheme: "aws",
367
- resolve: (refBody) => resolve$2(refBody)
367
+ resolve: (refBody) => resolve$3(refBody)
368
368
  };
369
- function env$1(...names) {
369
+ function env$2(...names) {
370
370
  for (const n of names) {
371
371
  const v = process.env[n];
372
372
  if (v !== void 0 && v !== "") return v;
373
373
  }
374
374
  return void 0;
375
375
  }
376
- function resolveConn$1() {
376
+ function resolveConn$2() {
377
377
  const cfg = getSecretsConfig()?.azure ?? {};
378
- const tenantId = env$1("AZURE_TENANT_ID", "API_SPECTOR_AZURE_TENANT_ID") ?? cfg.tenantId;
379
- const clientId = env$1("AZURE_CLIENT_ID", "API_SPECTOR_AZURE_CLIENT_ID") ?? cfg.clientId;
380
- const clientSecret = env$1("AZURE_CLIENT_SECRET", "API_SPECTOR_AZURE_CLIENT_SECRET");
378
+ const tenantId = env$2("AZURE_TENANT_ID", "API_SPECTOR_AZURE_TENANT_ID") ?? cfg.tenantId;
379
+ const clientId = env$2("AZURE_CLIENT_ID", "API_SPECTOR_AZURE_CLIENT_ID") ?? cfg.clientId;
380
+ const clientSecret = env$2("AZURE_CLIENT_SECRET", "API_SPECTOR_AZURE_CLIENT_SECRET");
381
381
  if (!tenantId || !clientId || !clientSecret) {
382
382
  throw new Error("Azure Key Vault: set AZURE_TENANT_ID, AZURE_CLIENT_ID and AZURE_CLIENT_SECRET");
383
383
  }
384
384
  return { tenantId, clientId, clientSecret, defaultVault: cfg.vault };
385
385
  }
386
- let cachedToken = null;
387
- const secretCache = /* @__PURE__ */ new Map();
388
- async function accessToken(conn) {
386
+ let cachedToken$1 = null;
387
+ const secretCache$1 = /* @__PURE__ */ new Map();
388
+ async function accessToken$1(conn) {
389
389
  const now = Date.now();
390
- if (cachedToken && cachedToken.expiresAt > now + 3e4) return cachedToken.value;
390
+ if (cachedToken$1 && cachedToken$1.expiresAt > now + 3e4) return cachedToken$1.value;
391
391
  const form = new URLSearchParams({
392
392
  grant_type: "client_credentials",
393
393
  client_id: conn.clientId,
@@ -407,7 +407,7 @@ async function accessToken(conn) {
407
407
  const token = json?.access_token;
408
408
  if (!token) throw new Error("Azure AD did not return an access token");
409
409
  const ttl = Number(json?.expires_in ?? 0);
410
- cachedToken = { value: token, expiresAt: ttl > 0 ? now + ttl * 1e3 : now + 5 * 6e4 };
410
+ cachedToken$1 = { value: token, expiresAt: ttl > 0 ? now + ttl * 1e3 : now + 5 * 6e4 };
411
411
  return token;
412
412
  }
413
413
  function secretUrl(refBody, defaultVault) {
@@ -430,13 +430,13 @@ function secretUrl(refBody, defaultVault) {
430
430
  const path2 = version ? `secrets/${name}/${version}` : `secrets/${name}`;
431
431
  return `${base}/${path2}?api-version=7.4`;
432
432
  }
433
- async function resolve$1(refBody) {
434
- const conn = resolveConn$1();
433
+ async function resolve$2(refBody) {
434
+ const conn = resolveConn$2();
435
435
  const url = secretUrl(refBody, conn.defaultVault);
436
436
  const now = Date.now();
437
- const hit = secretCache.get(url);
437
+ const hit = secretCache$1.get(url);
438
438
  if (hit && hit.expiresAt > now) return hit.value;
439
- const res = await fetch(url, { headers: { authorization: `Bearer ${await accessToken(conn)}` } });
439
+ const res = await fetch(url, { headers: { authorization: `Bearer ${await accessToken$1(conn)}` } });
440
440
  if (!res.ok) {
441
441
  const text = await res.text().catch(() => "");
442
442
  throw new Error(`Azure Key Vault ${res.status} for '${refBody}': ${text.slice(0, 300)}`);
@@ -444,24 +444,24 @@ async function resolve$1(refBody) {
444
444
  const json = await res.json();
445
445
  const value = json?.value;
446
446
  if (value === void 0) throw new Error(`Azure Key Vault secret '${refBody}' has no value`);
447
- secretCache.set(url, { value, expiresAt: now + 5 * 6e4 });
447
+ secretCache$1.set(url, { value, expiresAt: now + 5 * 6e4 });
448
448
  return value;
449
449
  }
450
450
  const azureKeyVaultProvider = {
451
451
  scheme: "azure",
452
- resolve: (refBody) => resolve$1(refBody)
452
+ resolve: (refBody) => resolve$2(refBody)
453
453
  };
454
- function env(...names) {
454
+ function env$1(...names) {
455
455
  for (const n of names) {
456
456
  const v = process.env[n];
457
457
  if (v !== void 0 && v !== "") return v;
458
458
  }
459
459
  return void 0;
460
460
  }
461
- function resolveConn() {
461
+ function resolveConn$1() {
462
462
  const cfg = getSecretsConfig()?.onePassword ?? {};
463
- const host = env("OP_CONNECT_HOST", "API_SPECTOR_OP_CONNECT_HOST") ?? cfg.connectHost;
464
- const token = env("OP_CONNECT_TOKEN", "API_SPECTOR_OP_CONNECT_TOKEN");
463
+ const host = env$1("OP_CONNECT_HOST", "API_SPECTOR_OP_CONNECT_HOST") ?? cfg.connectHost;
464
+ const token = env$1("OP_CONNECT_TOKEN", "API_SPECTOR_OP_CONNECT_TOKEN");
465
465
  if (!host || !token) {
466
466
  throw new Error("1Password: set OP_CONNECT_HOST and OP_CONNECT_TOKEN (1Password Connect)");
467
467
  }
@@ -500,7 +500,7 @@ async function itemId(conn, vid, item) {
500
500
  idCache.set(cacheKey, id);
501
501
  return id;
502
502
  }
503
- async function resolve(refBody) {
503
+ async function resolve$1(refBody) {
504
504
  const parts = refBody.replace(/^\/\//, "").replace(/^\/+/, "").split("/").filter(Boolean);
505
505
  if (parts.length < 3) {
506
506
  throw new Error(`1Password reference must be 'op://<vault>/<item>/<field>' (got 'op:${refBody}')`);
@@ -508,7 +508,7 @@ async function resolve(refBody) {
508
508
  const [vault, item, ...rest] = parts;
509
509
  const sectionLabel = rest.length >= 2 ? rest[0] : null;
510
510
  const fieldLabel = rest[rest.length - 1];
511
- const conn = resolveConn();
511
+ const conn = resolveConn$1();
512
512
  const vid = await vaultId(conn, vault);
513
513
  const iid = await itemId(conn, vid, item);
514
514
  const cacheKey = `${vid}/${iid}`;
@@ -535,12 +535,158 @@ async function resolve(refBody) {
535
535
  }
536
536
  const onePasswordProvider = {
537
537
  scheme: "op",
538
+ resolve: (refBody) => resolve$1(refBody)
539
+ };
540
+ const TOKEN_URI = "https://oauth2.googleapis.com/token";
541
+ const SCOPE = "https://www.googleapis.com/auth/cloud-platform";
542
+ const API_BASE = "https://secretmanager.googleapis.com/v1";
543
+ function env(...names) {
544
+ for (const n of names) {
545
+ const v = process.env[n];
546
+ if (v !== void 0 && v !== "") return v;
547
+ }
548
+ return void 0;
549
+ }
550
+ function loadServiceAccount() {
551
+ const inline = env("GCP_SERVICE_ACCOUNT_KEY", "GOOGLE_APPLICATION_CREDENTIALS_JSON");
552
+ const path2 = env("GOOGLE_APPLICATION_CREDENTIALS");
553
+ let raw;
554
+ if (inline) {
555
+ raw = inline;
556
+ } else if (path2) {
557
+ try {
558
+ raw = fs.readFileSync(path2, "utf8");
559
+ } catch (e) {
560
+ throw new Error(`GCP Secret Manager: cannot read GOOGLE_APPLICATION_CREDENTIALS '${path2}': ${e.message}`);
561
+ }
562
+ }
563
+ if (!raw) return void 0;
564
+ let key;
565
+ try {
566
+ key = JSON.parse(raw);
567
+ } catch {
568
+ throw new Error("GCP Secret Manager: the service-account key is not valid JSON");
569
+ }
570
+ if (!key.client_email || !key.private_key) {
571
+ throw new Error("GCP Secret Manager: the service-account key is missing client_email or private_key");
572
+ }
573
+ return key;
574
+ }
575
+ function resolveConn() {
576
+ const cfg = getSecretsConfig()?.gcp ?? {};
577
+ const accessToken2 = env("GCP_ACCESS_TOKEN", "GOOGLE_OAUTH_ACCESS_TOKEN");
578
+ const serviceAccount = loadServiceAccount();
579
+ if (!accessToken2 && !serviceAccount) {
580
+ throw new Error(
581
+ "GCP Secret Manager: no credentials (set GCP_ACCESS_TOKEN, or GOOGLE_APPLICATION_CREDENTIALS / GOOGLE_APPLICATION_CREDENTIALS_JSON to a service-account key)"
582
+ );
583
+ }
584
+ const defaultProject = env("GCP_PROJECT", "GOOGLE_CLOUD_PROJECT", "CLOUDSDK_CORE_PROJECT") ?? cfg.project ?? serviceAccount?.project_id;
585
+ return { accessToken: accessToken2, serviceAccount, defaultProject };
586
+ }
587
+ let cachedToken = null;
588
+ const secretCache = /* @__PURE__ */ new Map();
589
+ const b64url = (b) => (Buffer.isBuffer(b) ? b : Buffer.from(b)).toString("base64url");
590
+ async function mintAccessToken(sa) {
591
+ const iat = Math.floor(Date.now() / 1e3);
592
+ const aud = sa.token_uri || TOKEN_URI;
593
+ const header = b64url(JSON.stringify({ alg: "RS256", typ: "JWT" }));
594
+ const claims = b64url(JSON.stringify({ iss: sa.client_email, scope: SCOPE, aud, iat, exp: iat + 3600 }));
595
+ const signingInput = `${header}.${claims}`;
596
+ const signature = b64url(crypto.createSign("RSA-SHA256").update(signingInput).sign(sa.private_key));
597
+ const assertion = `${signingInput}.${signature}`;
598
+ const body = new URLSearchParams({
599
+ grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
600
+ assertion
601
+ });
602
+ const res = await fetch(aud, {
603
+ method: "POST",
604
+ headers: { "content-type": "application/x-www-form-urlencoded" },
605
+ body: body.toString()
606
+ });
607
+ if (!res.ok) {
608
+ const text = await res.text().catch(() => "");
609
+ throw new Error(`GCP token exchange ${res.status}: ${text.slice(0, 300)}`);
610
+ }
611
+ const json = await res.json();
612
+ const token = json?.access_token;
613
+ if (!token) throw new Error("GCP token endpoint did not return an access_token");
614
+ return { token, ttl: Number(json?.expires_in ?? 3600) };
615
+ }
616
+ async function accessToken(conn) {
617
+ if (conn.accessToken) return conn.accessToken;
618
+ const now = Date.now();
619
+ if (cachedToken && cachedToken.expiresAt > now + 3e4) return cachedToken.value;
620
+ const { token, ttl } = await mintAccessToken(conn.serviceAccount);
621
+ cachedToken = { value: token, expiresAt: ttl > 0 ? now + ttl * 1e3 : now + 5 * 6e4 };
622
+ return token;
623
+ }
624
+ function accessUrl(refPath, defaultProject) {
625
+ if (refPath.startsWith("projects/")) {
626
+ const hasVersion = /\/versions\/[^/]+$/.test(refPath);
627
+ const full = hasVersion ? refPath : `${refPath.replace(/\/+$/, "")}/versions/latest`;
628
+ return `${API_BASE}/${full}:access`;
629
+ }
630
+ const parts = refPath.replace(/^\/+/, "").split("/").filter(Boolean);
631
+ let project;
632
+ let secret;
633
+ let version = "latest";
634
+ if (parts.length >= 2) {
635
+ [project, secret, version = "latest"] = parts;
636
+ } else if (parts.length === 1) {
637
+ project = defaultProject;
638
+ secret = parts[0];
639
+ } else {
640
+ throw new Error(`GCP reference must be 'gcp:<project>/<secret>[/<version>]' (got 'gcp:${refPath}')`);
641
+ }
642
+ if (!project) {
643
+ throw new Error(
644
+ `GCP reference 'gcp:${refPath}' has no project and no default is set (set GCP_PROJECT or settings.secrets.gcp.project, or write 'gcp:<project>/<secret>')`
645
+ );
646
+ }
647
+ return `${API_BASE}/projects/${project}/secrets/${secret}/versions/${version}:access`;
648
+ }
649
+ async function resolve(refBody) {
650
+ const hash = refBody.lastIndexOf("#");
651
+ const refPath = hash < 0 ? refBody : refBody.slice(0, hash);
652
+ const key = hash < 0 ? null : refBody.slice(hash + 1);
653
+ const conn = resolveConn();
654
+ const url = accessUrl(refPath, conn.defaultProject);
655
+ const now = Date.now();
656
+ const hit = secretCache.get(url);
657
+ const rawPayload = hit && hit.expiresAt > now ? hit.value : await fetchPayload(conn, url, refBody);
658
+ if (key === null) return rawPayload;
659
+ let parsed;
660
+ try {
661
+ parsed = JSON.parse(rawPayload);
662
+ } catch {
663
+ throw new Error(`GCP secret '${refPath}' is not JSON, so key '${key}' cannot be read (drop the '#${key}')`);
664
+ }
665
+ if (!(key in parsed)) throw new Error(`GCP secret '${refPath}' has no key '${key}'`);
666
+ return String(parsed[key]);
667
+ }
668
+ async function fetchPayload(conn, url, refBody) {
669
+ const res = await fetch(url, { headers: { authorization: `Bearer ${await accessToken(conn)}` } });
670
+ if (!res.ok) {
671
+ const text = await res.text().catch(() => "");
672
+ throw new Error(`GCP Secret Manager ${res.status} for '${refBody}': ${text.slice(0, 300)}`);
673
+ }
674
+ const json = await res.json();
675
+ const data = json?.payload?.data;
676
+ if (data === void 0) throw new Error(`GCP secret '${refBody}' returned no payload data`);
677
+ const value = Buffer.from(data, "base64").toString("utf8");
678
+ secretCache.set(url, { value, expiresAt: Date.now() + 5 * 6e4 });
679
+ return value;
680
+ }
681
+ const gcpSecretManagerProvider = {
682
+ scheme: "gcp",
538
683
  resolve: (refBody) => resolve(refBody)
539
684
  };
540
685
  registerSecretProvider(vaultProvider);
541
686
  registerSecretProvider(awsSecretsProvider);
542
687
  registerSecretProvider(azureKeyVaultProvider);
543
688
  registerSecretProvider(onePasswordProvider);
689
+ registerSecretProvider(gcpSecretManagerProvider);
544
690
  const MASTER_KEY_ENV = "API_SPECTOR_MASTER_KEY";
545
691
  let secretStore = {};
546
692
  let secretStorePath = null;
@@ -741,7 +887,14 @@ async function buildEnvVars(environment) {
741
887
  return vars;
742
888
  }
743
889
  function mergeVars(envVars, collectionVars, globals2, localVars = {}, dynamicVars = {}) {
744
- return { ...dynamicVars, ...globals2, ...collectionVars, ...envVars, ...localVars };
890
+ const out = {};
891
+ for (const src of [dynamicVars, globals2, collectionVars, envVars, localVars]) {
892
+ for (const [k, v] of Object.entries(src ?? {})) {
893
+ if (v == null) continue;
894
+ out[k] = v;
895
+ }
896
+ }
897
+ return out;
745
898
  }
746
899
  function xmlFindAll(node, tag, nth) {
747
900
  const results = [];
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
- const handle = require("./handle-BY_jaVBT.js");
2
+ const handle = require("./handle-DRmKOL-s.js");
3
3
  const https = require("node:https");
4
4
  const http = require("node:http");
5
5
  const xmldom = require("@xmldom/xmldom");
@@ -313,7 +313,7 @@ function registerSoapHandlers(ipc) {
313
313
  handle.handleIpc(ipc, handle.IPC.wsdl.import, async (_event, opts) => {
314
314
  const { validateWsdlImport } = await Promise.resolve().then(() => require("./ipc-validate-sqUFqRmE.js"));
315
315
  validateWsdlImport(opts);
316
- const { importWsdl } = await Promise.resolve().then(() => require("./import-DR_r1DUP.js"));
316
+ const { importWsdl } = await Promise.resolve().then(() => require("./import-B5T5wDu7.js"));
317
317
  const wsdlText = opts.xml ?? (opts.url ? await fetchUrl(opts.url) : "");
318
318
  if (!wsdlText) throw new Error("wsdl:import requires either `url` or `xml`");
319
319
  return importWsdl(wsdlText, { name: opts.name, existingMockPorts: opts.existingMockPorts });
@@ -2,7 +2,7 @@
2
2
  "use strict";
3
3
  const promises = require("node:fs/promises");
4
4
  const path = require("node:path");
5
- const brokerClient = require("./chunks/broker-client-VwFF9sbp.js");
5
+ const brokerClient = require("./chunks/broker-client-5IyCTx40.js");
6
6
  const escape = require("./chunks/escape-DDVAlOvZ.js");
7
7
  const crypto = require("node:crypto");
8
8
  const undici = require("undici");
@@ -11,10 +11,10 @@ const cliCommon = require("./chunks/cli-common-GnihVSjU.js");
11
11
  const node_child_process = require("node:child_process");
12
12
  const jsYaml = require("js-yaml");
13
13
  const environments = require("./chunks/environments-iM3SUM-4.js");
14
- require("./chunks/request-exec-CcLHtkwG.js");
14
+ require("./chunks/request-exec-CwJ8_OKC.js");
15
15
  require("fs/promises");
16
16
  require("tls");
17
- require("./chunks/handle-BY_jaVBT.js");
17
+ require("./chunks/handle-DRmKOL-s.js");
18
18
  require("node:fs");
19
19
  require("dayjs");
20
20
  require("node:vm");
package/out/main/flow.js CHANGED
@@ -2,7 +2,7 @@
2
2
  "use strict";
3
3
  const promises = require("node:fs/promises");
4
4
  const path = require("node:path");
5
- const requestExec = require("./chunks/request-exec-CcLHtkwG.js");
5
+ const requestExec = require("./chunks/request-exec-CwJ8_OKC.js");
6
6
  const requestCollection = require("./chunks/request-collection-KGPuvN7o.js");
7
7
  const environments = require("./chunks/environments-iM3SUM-4.js");
8
8
  const flowEngine = require("./chunks/flow-engine-BVzjz4lJ.js");
@@ -10,7 +10,7 @@ const cliCommon = require("./chunks/cli-common-GnihVSjU.js");
10
10
  require("undici");
11
11
  require("fs/promises");
12
12
  require("tls");
13
- require("./chunks/handle-BY_jaVBT.js");
13
+ require("./chunks/handle-DRmKOL-s.js");
14
14
  require("node:fs");
15
15
  require("node:os");
16
16
  require("node:crypto");
@@ -265,7 +265,7 @@ async function main() {
265
265
  const envName = args.environment ?? args.env;
266
266
  const env = environments.selectEnvironment(workspace, environments$1, envName);
267
267
  const verbose = Boolean(args.verbose);
268
- const version = `v${"0.7.4"}`;
268
+ const version = `v${"0.7.6"}`;
269
269
  console.log("");
270
270
  console.log(cliCommon.color(" API Flow Runner" + (version ? ` ${version}` : ""), cliCommon.C.bold, cliCommon.C.white));
271
271
  console.log(cliCommon.color(` Flow: ${flow.name}`, cliCommon.C.gray));
package/out/main/index.js CHANGED
@@ -22,14 +22,14 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
22
22
  mod
23
23
  ));
24
24
  const electron = require("electron");
25
- const handle = require("./chunks/handle-BY_jaVBT.js");
25
+ const handle = require("./chunks/handle-DRmKOL-s.js");
26
26
  const path = require("node:path");
27
27
  const fs = require("node:fs");
28
28
  const promises = require("node:fs/promises");
29
29
  const crypto = require("node:crypto");
30
30
  const JSZip = require("jszip");
31
31
  const dataFiles = require("./chunks/data-files-BYqhsoPe.js");
32
- const requestExec = require("./chunks/request-exec-CcLHtkwG.js");
32
+ const requestExec = require("./chunks/request-exec-CwJ8_OKC.js");
33
33
  const ipcValidate = require("./chunks/ipc-validate-sqUFqRmE.js");
34
34
  const uuid = require("uuid");
35
35
  const jsYaml = require("js-yaml");
@@ -42,9 +42,9 @@ const WebSocket = require("ws");
42
42
  const os = require("node:os");
43
43
  const grpc = require("@grpc/grpc-js");
44
44
  const protoLoader = require("@grpc/proto-loader");
45
- const soapHandler = require("./chunks/soap-handler-CkoLJHoI.js");
45
+ const soapHandler = require("./chunks/soap-handler-DTfFnzNY.js");
46
46
  const escape = require("./chunks/escape-DDVAlOvZ.js");
47
- const brokerClient = require("./chunks/broker-client-VwFF9sbp.js");
47
+ const brokerClient = require("./chunks/broker-client-5IyCTx40.js");
48
48
  const simpleGit = require("simple-git");
49
49
  const recorder = require("./chunks/recorder-BkO8G_mB.js");
50
50
  const electronUpdater = require("electron-updater");
@@ -1299,6 +1299,29 @@ async function importPostman(filePath) {
1299
1299
  requests
1300
1300
  };
1301
1301
  }
1302
+ async function importPostmanEnvironment(filePath) {
1303
+ const raw = await promises.readFile(filePath, "utf8");
1304
+ let data;
1305
+ try {
1306
+ data = JSON.parse(raw);
1307
+ } catch {
1308
+ throw new Error("Not valid JSON — pick a Postman environment export file.");
1309
+ }
1310
+ if (!Array.isArray(data.values) || data._postman_variable_scope === "collection") {
1311
+ throw new Error('Not a Postman environment file (no "values" array found).');
1312
+ }
1313
+ const variables = data.values.filter((v) => !!v && typeof v.key === "string" && v.key.length > 0).map((v) => ({
1314
+ key: v.key,
1315
+ value: v.value == null ? "" : String(v.value),
1316
+ enabled: v.enabled !== false
1317
+ }));
1318
+ return {
1319
+ version: "1.0",
1320
+ id: uuid.v4(),
1321
+ name: data.name && String(data.name).trim() || "Imported Environment",
1322
+ variables
1323
+ };
1324
+ }
1302
1325
  async function loadSpec(filePath) {
1303
1326
  const raw = await promises.readFile(filePath, "utf8");
1304
1327
  if (filePath.endsWith(".yaml") || filePath.endsWith(".yml")) {
@@ -2041,6 +2064,15 @@ function registerImportHandlers(ipc) {
2041
2064
  if (result.canceled || !result.filePaths[0]) return null;
2042
2065
  return importPostman(result.filePaths[0]);
2043
2066
  });
2067
+ handle.handleIpc(ipc, handle.IPC.import.postmanEnvironment, async () => {
2068
+ const result = await electron.dialog.showOpenDialog({
2069
+ title: "Import Postman Environment",
2070
+ filters: [{ name: "JSON", extensions: ["json"] }],
2071
+ properties: ["openFile"]
2072
+ });
2073
+ if (result.canceled || !result.filePaths[0]) return null;
2074
+ return importPostmanEnvironment(result.filePaths[0]);
2075
+ });
2044
2076
  handle.handleIpc(ipc, handle.IPC.import.openapi, async () => {
2045
2077
  const result = await electron.dialog.showOpenDialog({
2046
2078
  title: "Import OpenAPI Definition",
@@ -6110,7 +6142,7 @@ function isNewer(a, b) {
6110
6142
  return a2 > b2;
6111
6143
  }
6112
6144
  async function checkForUpdate() {
6113
- const current = "0.7.4";
6145
+ const current = "0.7.6";
6114
6146
  try {
6115
6147
  const res = await fetch(REGISTRY_URL, { signal: AbortSignal.timeout(4e3) });
6116
6148
  if (!res.ok) return null;
package/out/main/lib.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const mockServer = require("./chunks/mock-server-C4kTFkod.js");
4
- const requestExec = require("./chunks/request-exec-CcLHtkwG.js");
4
+ const requestExec = require("./chunks/request-exec-CwJ8_OKC.js");
5
5
  const flowEngine = require("./chunks/flow-engine-BVzjz4lJ.js");
6
6
  require("node:http");
7
7
  require("node:crypto");
@@ -11,7 +11,7 @@ require("@xmldom/xmldom");
11
11
  require("undici");
12
12
  require("fs/promises");
13
13
  require("tls");
14
- require("./chunks/handle-BY_jaVBT.js");
14
+ require("./chunks/handle-DRmKOL-s.js");
15
15
  require("node:fs");
16
16
  require("node:os");
17
17
  require("node:path");