@xcelera/cli 2.2.1 → 2.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/README.md CHANGED
@@ -4,6 +4,38 @@ A CLI for running Lighthouse performance audits using xcelera.dev
4
4
 
5
5
  ## Usage
6
6
 
7
+ ### CLI Usage
8
+
9
+ ```bash
10
+ # Basic audit
11
+ xcelera audit --url https://example.com --token your-api-token
12
+ ```
13
+
14
+ ### Authenticated Pages
15
+
16
+ For pages behind login, you can pass authentication credentials:
17
+
18
+ ```bash
19
+ # With session cookie
20
+ xcelera audit --url https://myapp.com/dashboard --cookie "session=abc123"
21
+
22
+ # With bearer token header
23
+ xcelera audit --url https://api.myapp.com/admin \
24
+ --header "Authorization: Bearer eyJhbG..."
25
+
26
+ # Multiple cookies
27
+ xcelera audit --url https://myapp.com/dashboard \
28
+ --cookie "session=abc123" --cookie "csrf=xyz"
29
+
30
+ # Full auth JSON (multiple cookies and custom headers)
31
+ xcelera audit --url https://myapp.com/dashboard \
32
+ --auth '{"cookies":[{"name":"session","value":"abc123"}],"headers":{"X-Custom":"value"}}'
33
+
34
+ # Using environment variable
35
+ export XCELERA_AUTH='{"cookies":[{"name":"session","value":"abc123"}]}'
36
+ xcelera audit --url https://myapp.com/dashboard
37
+ ```
38
+
7
39
  ### GitHub Action Usage
8
40
 
9
41
  ```yaml
@@ -14,6 +46,34 @@ A CLI for running Lighthouse performance audits using xcelera.dev
14
46
  token: ${{ secrets.XCELERA_TOKEN }}
15
47
  ```
16
48
 
49
+ For authenticated pages in CI:
50
+
51
+ ```yaml
52
+ # With session cookie
53
+ - name: Lighthouse Audit (Cookie Auth)
54
+ uses: xcelera/cli@v1
55
+ with:
56
+ url: https://example.com/dashboard
57
+ token: ${{ secrets.XCELERA_TOKEN }}
58
+ cookie: "session=value"
59
+
60
+ # With bearer token header
61
+ - name: Lighthouse Audit (Bearer Auth)
62
+ uses: xcelera/cli@v1
63
+ with:
64
+ url: https://example.com/admin
65
+ token: ${{ secrets.XCELERA_TOKEN }}
66
+ header: "Authorization: Bearer eybDfd..."
67
+
68
+ # With full auth JSON (multiple cookies/headers)
69
+ - name: Lighthouse Audit (Full Auth)
70
+ uses: xcelera/cli@v1
71
+ with:
72
+ url: https://example.com/dashboard
73
+ token: ${{ secrets.XCELERA_TOKEN }}
74
+ auth: '{"cookies":[{"name":"session","value":"session_value"},{"name":"csrf","value":"csrf_value"}]}'
75
+ ```
76
+
17
77
  ## Setup
18
78
 
19
79
  ### 1. Get Your API Token
package/dist/action.js CHANGED
@@ -27303,7 +27303,7 @@ function isNetworkError(error) {
27303
27303
  return errorMessages.has(message);
27304
27304
  }
27305
27305
 
27306
- async function requestAudit(url, token, context) {
27306
+ async function requestAudit(url, token, context, auth) {
27307
27307
  const apiUrl = `${getApiBaseUrl()}/api/v1/audit`;
27308
27308
  try {
27309
27309
  const response = await fetch(apiUrl, {
@@ -27314,7 +27314,8 @@ async function requestAudit(url, token, context) {
27314
27314
  },
27315
27315
  body: JSON.stringify({
27316
27316
  url,
27317
- context
27317
+ context,
27318
+ ...(auth && { auth })
27318
27319
  })
27319
27320
  });
27320
27321
  if (!response.ok) {
@@ -27346,6 +27347,7 @@ async function requestAudit(url, token, context) {
27346
27347
  throw error;
27347
27348
  }
27348
27349
  }
27350
+ /* istanbul ignore next */
27349
27351
  function getApiBaseUrl() {
27350
27352
  if (process.env.NODE_ENV === 'development') {
27351
27353
  return 'http://localhost:3000';
@@ -36538,24 +36540,21 @@ async function isGitRepository() {
36538
36540
  return simpleGit().checkIsRepo();
36539
36541
  }
36540
36542
  async function getCommit(hash = 'HEAD') {
36541
- // format: %H: commit hash, %s: subject, %an: author name, %ae: author email, %ai: author date
36543
+ // format: %H: commit hash, %s: subject, %an: author name, %ai: author date
36542
36544
  // Use null byte (%x00) as delimiter - cannot appear in commit data
36543
36545
  const commit = await simpleGit().show([
36544
36546
  hash,
36545
36547
  '--no-patch',
36546
- '--format=%H%x00%s%x00%an%x00%ae%x00%ai'
36548
+ '--format=%H%x00%s%x00%an%x00%ai'
36547
36549
  ]);
36548
- const [resolvedHash, message, author_name, author_email, date] = commit
36549
- .trim()
36550
- .split('\0');
36550
+ const [resolvedHash, message, author_name, date] = commit.trim().split('\0');
36551
36551
  if (!resolvedHash) {
36552
36552
  throw new Error(`No commit found for ${hash}`);
36553
36553
  }
36554
36554
  return {
36555
36555
  hash: resolvedHash,
36556
36556
  message: message,
36557
- author: author_name || 'Unknown',
36558
- email: author_email || '',
36557
+ author: author_name || 'Unknown Author',
36559
36558
  date: date ? new Date(date).toISOString() : new Date().toISOString()
36560
36559
  };
36561
36560
  }
@@ -36576,13 +36575,18 @@ async function inferBuildContext() {
36576
36575
  };
36577
36576
  }
36578
36577
 
36579
- async function runAuditCommand(url, token) {
36578
+ async function runAuditCommand(url, token, authOptions) {
36580
36579
  const output = [];
36581
36580
  const errors = [];
36582
36581
  try {
36583
36582
  const buildContext = await inferBuildContext();
36584
36583
  output.push(...formatBuildContext(buildContext));
36585
- const response = await requestAudit(url, token, buildContext);
36584
+ const auth = parseAuthCredentials(authOptions);
36585
+ if (auth) {
36586
+ output.push('🔐 Authentication credentials detected');
36587
+ output.push('');
36588
+ }
36589
+ const response = await requestAudit(url, token, buildContext, auth);
36586
36590
  if (!response.success) {
36587
36591
  const { message, details } = response.error;
36588
36592
  errors.push('❌ Unable to schedule audit :(');
@@ -36674,13 +36678,67 @@ function formatGitHubIntegrationStatus(context) {
36674
36678
  }
36675
36679
  return { output, errors };
36676
36680
  }
36681
+ function parseAuthCredentials(options) {
36682
+ const envAuth = process.env.XCELERA_AUTH;
36683
+ if (envAuth) {
36684
+ try {
36685
+ return JSON.parse(envAuth);
36686
+ }
36687
+ catch {
36688
+ throw new Error('XCELERA_AUTH environment variable contains invalid JSON');
36689
+ }
36690
+ }
36691
+ // Check for --auth JSON option
36692
+ if (options?.authJson) {
36693
+ try {
36694
+ return JSON.parse(options.authJson);
36695
+ }
36696
+ catch {
36697
+ throw new Error('--auth option contains invalid JSON');
36698
+ }
36699
+ }
36700
+ // Build auth from --cookie and --header options
36701
+ const hasCookies = options?.cookies && options.cookies.length > 0;
36702
+ const hasHeaders = options?.headers && options.headers.length > 0;
36703
+ if (!hasCookies && !hasHeaders) {
36704
+ return undefined;
36705
+ }
36706
+ const auth = {};
36707
+ if (hasCookies && options.cookies) {
36708
+ auth.cookies = options.cookies.map(parseCookie);
36709
+ }
36710
+ if (hasHeaders && options.headers) {
36711
+ auth.headers = {};
36712
+ for (const header of options.headers) {
36713
+ const colonIndex = header.indexOf(':');
36714
+ if (colonIndex === -1) {
36715
+ throw new Error(`Invalid header format: "${header}". Expected "Name: Value"`);
36716
+ }
36717
+ const name = header.slice(0, colonIndex).trim();
36718
+ const value = header.slice(colonIndex + 1).trim();
36719
+ auth.headers[name] = value;
36720
+ }
36721
+ }
36722
+ return auth;
36723
+ }
36724
+ function parseCookie(cookie) {
36725
+ const equalsIndex = cookie.indexOf('=');
36726
+ if (equalsIndex === -1) {
36727
+ throw new Error(`Invalid cookie format: "${cookie}". Expected "name=value"`);
36728
+ }
36729
+ return {
36730
+ name: cookie.slice(0, equalsIndex),
36731
+ value: cookie.slice(equalsIndex + 1)
36732
+ };
36733
+ }
36677
36734
 
36678
36735
  /* istanbul ignore file */
36679
36736
  run();
36680
36737
  async function run() {
36681
36738
  const url = coreExports.getInput('url', { required: true });
36682
36739
  const token = coreExports.getInput('token', { required: true });
36683
- const result = await runAuditCommand(url, token);
36740
+ const authOptions = parseAuthInputs();
36741
+ const result = await runAuditCommand(url, token, authOptions);
36684
36742
  result.output.forEach((line) => coreExports.info(line));
36685
36743
  result.errors.forEach((line) => coreExports.error(line));
36686
36744
  if (result.exitCode !== 0) {
@@ -36691,4 +36749,17 @@ async function run() {
36691
36749
  coreExports.setOutput('status', 'success');
36692
36750
  }
36693
36751
  }
36752
+ function parseAuthInputs() {
36753
+ const auth = coreExports.getInput('auth');
36754
+ const cookie = coreExports.getInput('cookie');
36755
+ const header = coreExports.getInput('header');
36756
+ if (!auth && !cookie && !header) {
36757
+ return undefined;
36758
+ }
36759
+ return {
36760
+ authJson: auth || undefined,
36761
+ cookies: cookie ? [cookie] : undefined,
36762
+ headers: header ? [header] : undefined
36763
+ };
36764
+ }
36694
36765
  //# sourceMappingURL=action.js.map