@duffcloudservices/cli 0.1.1 → 0.1.2

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.js CHANGED
@@ -5,7 +5,7 @@ import { Command } from "commander";
5
5
  import chalk8 from "chalk";
6
6
 
7
7
  // package.json
8
- var version = "0.1.1";
8
+ var version = "0.1.2";
9
9
 
10
10
  // src/commands/auth.ts
11
11
  import chalk2 from "chalk";
@@ -441,181 +441,198 @@ import chalk4 from "chalk";
441
441
  import ora2 from "ora";
442
442
 
443
443
  // src/templates/site-deploy.yml
444
- var site_deploy_default = `# Site Deployment Workflow\r
445
- # Generated by @duffcloudservices/cli\r
446
- # This workflow deploys the site to Azure Static Web Apps using OIDC authentication.\r
447
- \r
448
- name: Site Deployment\r
449
- \r
450
- on:\r
451
- push:\r
452
- branches:\r
453
- - 'release/**'\r
454
- - 'master'\r
455
- workflow_dispatch:\r
456
- inputs:\r
457
- trigger_reason:\r
458
- description: 'Reason for manual deployment'\r
459
- required: false\r
460
- type: string\r
461
- default: 'Manual deployment'\r
462
- \r
463
- # Prevent concurrent deployments for the same branch\r
464
- concurrency:\r
465
- group: site-deploy-\${{ github.ref }}\r
466
- cancel-in-progress: false\r
467
- \r
468
- permissions:\r
469
- contents: read\r
470
- issues: write\r
471
- pull-requests: read\r
472
- id-token: write # Required for Azure OIDC authentication\r
473
- \r
474
- jobs:\r
475
- deploy:\r
476
- name: Deploy to Azure Static Web App\r
477
- runs-on: ubuntu-latest\r
478
- # Skip deployment on branch creation events\r
479
- if: github.event.before != '0000000000000000000000000000000000000000'\r
480
- \r
481
- steps:\r
482
- - name: Determine deployment environment\r
483
- id: environment\r
484
- run: |\r
485
- BRANCH="\${{ github.ref_name }}"\r
486
- echo "Branch: $BRANCH"\r
487
- \r
488
- if [ "$BRANCH" == "master" ]; then\r
489
- echo "environment=Production" >> $GITHUB_OUTPUT\r
490
- echo "environment_name=Production" >> $GITHUB_OUTPUT\r
491
- echo "swa_environment=" >> $GITHUB_OUTPUT\r
492
- else\r
493
- echo "environment=preview" >> $GITHUB_OUTPUT\r
494
- echo "environment_name=Preview" >> $GITHUB_OUTPUT\r
495
- echo "swa_environment=preview" >> $GITHUB_OUTPUT\r
496
- fi\r
497
- \r
498
- - name: Checkout repository\r
499
- uses: actions/checkout@v4\r
500
- \r
501
- - name: Read site configuration\r
502
- id: config\r
503
- run: |\r
504
- CONFIG_FILE=".dcs/site.yaml"\r
505
- \r
506
- if [ ! -f "$CONFIG_FILE" ]; then\r
507
- echo "::error::Missing DCS site configuration file: $CONFIG_FILE"\r
508
- exit 1\r
509
- fi\r
510
- \r
511
- SITE_NAME=$(grep -E '^site_name:' "$CONFIG_FILE" | sed 's/site_name:\\s*//' | tr -d '"' | tr -d "'")\r
512
- SITE_SLUG=$(grep -E '^site_slug:' "$CONFIG_FILE" | sed 's/site_slug:\\s*//' | tr -d '"' | tr -d "'")\r
513
- SWA_RESOURCE_ID=$(grep -E '^swa_resource_id:' "$CONFIG_FILE" | sed 's/swa_resource_id:\\s*//' | tr -d '"' | tr -d "'")\r
514
- PORTAL_API_URL=$(grep -E '^portal_api_url:' "$CONFIG_FILE" | sed 's/portal_api_url:\\s*//' | tr -d '"' | tr -d "'")\r
515
- PORTAL_API_URL="\${PORTAL_API_URL:-https://portal.duffcloudservices.com}"\r
516
- GOOGLE_ANALYTICS_ID=$(grep -E '^google_analytics_id:' "$CONFIG_FILE" | sed 's/google_analytics_id:\\s*//' | tr -d '"' | tr -d "'")\r
517
- \r
518
- # Azure Authentication\r
519
- AZURE_CLIENT_ID=$(grep -E '^\\s*client_id:' "$CONFIG_FILE" | sed 's/.*client_id:\\s*//' | tr -d '"' | tr -d "'")\r
520
- AZURE_TENANT_ID=$(grep -E '^\\s*tenant_id:' "$CONFIG_FILE" | sed 's/.*tenant_id:\\s*//' | tr -d '"' | tr -d "'")\r
521
- AZURE_SUBSCRIPTION_ID=$(grep -E '^\\s*subscription_id:' "$CONFIG_FILE" | sed 's/.*subscription_id:\\s*//' | tr -d '"' | tr -d "'")\r
522
- \r
523
- # Build Configuration\r
524
- APP_LOCATION=$(grep -E '^\\s*app_location:' "$CONFIG_FILE" | sed 's/.*app_location:\\s*//' | tr -d '"' | tr -d "'")\r
525
- OUTPUT_LOCATION=$(grep -E '^\\s*output_location:' "$CONFIG_FILE" | sed 's/.*output_location:\\s*//' | tr -d '"' | tr -d "'")\r
526
- \r
527
- APP_LOCATION="\${APP_LOCATION:-dist}"\r
528
- OUTPUT_LOCATION="\${OUTPUT_LOCATION:-.}"\r
529
- \r
530
- if [ -z "$SWA_RESOURCE_ID" ]; then\r
531
- echo "::error::Missing swa_resource_id in $CONFIG_FILE"\r
532
- exit 1\r
533
- fi\r
534
- \r
535
- echo "site_name=$SITE_NAME" >> $GITHUB_OUTPUT\r
536
- echo "site_slug=$SITE_SLUG" >> $GITHUB_OUTPUT\r
537
- echo "swa_resource_id=$SWA_RESOURCE_ID" >> $GITHUB_OUTPUT\r
538
- echo "app_location=$APP_LOCATION" >> $GITHUB_OUTPUT\r
539
- echo "output_location=$OUTPUT_LOCATION" >> $GITHUB_OUTPUT\r
540
- echo "azure_client_id=$AZURE_CLIENT_ID" >> $GITHUB_OUTPUT\r
541
- echo "azure_tenant_id=$AZURE_TENANT_ID" >> $GITHUB_OUTPUT\r
542
- echo "azure_subscription_id=$AZURE_SUBSCRIPTION_ID" >> $GITHUB_OUTPUT\r
543
- echo "portal_api_url=$PORTAL_API_URL" >> $GITHUB_OUTPUT\r
544
- echo "google_analytics_id=$GOOGLE_ANALYTICS_ID" >> $GITHUB_OUTPUT\r
545
- \r
546
- - name: Azure login via OIDC\r
547
- uses: azure/login@v2\r
548
- with:\r
549
- client-id: \${{ steps.config.outputs.azure_client_id || vars.AZURE_CLIENT_ID }}\r
550
- tenant-id: \${{ steps.config.outputs.azure_tenant_id || vars.AZURE_TENANT_ID }}\r
551
- subscription-id: \${{ steps.config.outputs.azure_subscription_id || vars.AZURE_SUBSCRIPTION_ID }}\r
552
- \r
553
- - name: Get deployment tokens\r
554
- id: tokens\r
555
- run: |\r
556
- # Get access token for DCS API\r
557
- ACCESS_TOKEN=$(az account get-access-token --resource "api://304711b9-8532-4659-beb7-c85726de9ae5" --query accessToken -o tsv)\r
558
- \r
559
- SITE_NAME="\${{ steps.config.outputs.site_name }}"\r
560
- SITE_SLUG="\${{ steps.config.outputs.site_slug }}"\r
561
- PORTAL_API_URL="\${{ steps.config.outputs.portal_api_url }}"\r
562
- \r
563
- RESPONSE=$(curl -s -X POST "\${PORTAL_API_URL}/api/v1/sites/deployment-tokens" \\\r
564
- -H "Authorization: Bearer $ACCESS_TOKEN" \\\r
565
- -H "Content-Type: application/json" \\\r
566
- -d "{\\"siteName\\": \\"$SITE_NAME\\", \\"siteSlug\\": \\"$SITE_SLUG\\"}")\r
567
- \r
568
- SWA_TOKEN=$(echo $RESPONSE | jq -r .swaToken)\r
569
- \r
570
- if [ -z "$SWA_TOKEN" ] || [ "$SWA_TOKEN" == "null" ]; then\r
571
- echo "::error::Failed to retrieve SWA token from DCS API"\r
572
- echo "Response: $RESPONSE"\r
573
- exit 1\r
574
- fi\r
575
- \r
576
- echo "::add-mask::$SWA_TOKEN"\r
577
- echo "swa_token=$SWA_TOKEN" >> $GITHUB_OUTPUT\r
578
- \r
579
- - name: Setup Node.js\r
580
- uses: actions/setup-node@v4\r
581
- with:\r
582
- node-version: "22"\r
583
- \r
584
- - name: Install pnpm\r
585
- uses: pnpm/action-setup@v4\r
586
- \r
587
- - name: Setup pnpm cache\r
588
- uses: actions/cache@v4\r
589
- with:\r
590
- path: ~/.pnpm-store\r
591
- key: \${{ runner.os }}-pnpm-\${{ hashFiles('**/pnpm-lock.yaml') }}\r
592
- restore-keys: |\r
593
- \${{ runner.os }}-pnpm-\r
594
- \r
595
- - name: Install dependencies\r
596
- run: pnpm install --frozen-lockfile\r
597
- \r
598
- - name: Create environment file\r
599
- run: |\r
600
- cat > .env << EOF\r
601
- VITE_BACKEND_URI="\${{ steps.config.outputs.portal_api_url }}"\r
602
- VITE_SITE_SLUG="\${{ steps.config.outputs.site_slug }}"\r
603
- VITE_GOOGLE_ANALYTICS_ID="\${{ steps.config.outputs.google_analytics_id }}"\r
604
- EOF\r
605
- \r
606
- - name: Build site\r
607
- run: pnpm run build\r
608
- \r
609
- - name: Deploy to Azure Static Web Apps\r
610
- uses: Azure/static-web-apps-deploy@v1\r
611
- with:\r
612
- azure_static_web_apps_api_token: \${{ steps.tokens.outputs.swa_token }}\r
613
- repo_token: \${{ secrets.GITHUB_TOKEN }}\r
614
- action: "upload"\r
615
- app_location: \${{ steps.config.outputs.app_location }}\r
616
- output_location: \${{ steps.config.outputs.output_location }}\r
617
- skip_app_build: true\r
618
- deployment_environment: \${{ steps.environment.outputs.swa_environment != '' && steps.environment.outputs.swa_environment || '' }}\r
444
+ var site_deploy_default = `# Site Deployment Workflow
445
+ # Generated by @duffcloudservices/cli
446
+ # This workflow deploys the site to Azure Static Web Apps using OIDC authentication.
447
+
448
+ name: Site Deployment
449
+
450
+ on:
451
+ push:
452
+ branches:
453
+ - 'release/**'
454
+ - 'master'
455
+ workflow_dispatch:
456
+ inputs:
457
+ trigger_reason:
458
+ description: 'Reason for manual deployment'
459
+ required: false
460
+ type: string
461
+ default: 'Manual deployment'
462
+
463
+ # Prevent concurrent deployments for the same branch
464
+ concurrency:
465
+ group: site-deploy-\${{ github.ref }}
466
+ cancel-in-progress: false
467
+
468
+ permissions:
469
+ contents: read
470
+ issues: write
471
+ pull-requests: read
472
+ id-token: write # Required for Azure OIDC authentication
473
+
474
+ jobs:
475
+ deploy:
476
+ name: Deploy to Azure Static Web App
477
+ runs-on: ubuntu-latest
478
+ # Skip deployment on branch creation events
479
+ if: github.event.before != '0000000000000000000000000000000000000000'
480
+
481
+ steps:
482
+ - name: Determine deployment environment
483
+ id: environment
484
+ run: |
485
+ BRANCH="\${{ github.ref_name }}"
486
+ echo "Branch: $BRANCH"
487
+
488
+ if [ "$BRANCH" == "master" ]; then
489
+ echo "environment=Production" >> $GITHUB_OUTPUT
490
+ echo "environment_name=Production" >> $GITHUB_OUTPUT
491
+ echo "swa_environment=" >> $GITHUB_OUTPUT
492
+ else
493
+ echo "environment=preview" >> $GITHUB_OUTPUT
494
+ echo "environment_name=Preview" >> $GITHUB_OUTPUT
495
+ echo "swa_environment=preview" >> $GITHUB_OUTPUT
496
+ fi
497
+
498
+ - name: Checkout repository
499
+ uses: actions/checkout@v4
500
+
501
+ - name: Read site configuration
502
+ id: config
503
+ run: |
504
+ CONFIG_FILE=".dcs/site.yaml"
505
+
506
+ if [ ! -f "$CONFIG_FILE" ]; then
507
+ echo "::error::Missing DCS site configuration file: $CONFIG_FILE"
508
+ exit 1
509
+ fi
510
+
511
+ SITE_NAME=$(grep -E '^site_name:' "$CONFIG_FILE" | sed 's/site_name:\\s*//' | tr -d '"' | tr -d "'")
512
+ SITE_SLUG=$(grep -E '^site_slug:' "$CONFIG_FILE" | sed 's/site_slug:\\s*//' | tr -d '"' | tr -d "'")
513
+ SWA_RESOURCE_ID=$(grep -E '^swa_resource_id:' "$CONFIG_FILE" | sed 's/swa_resource_id:\\s*//' | tr -d '"' | tr -d "'")
514
+ PORTAL_API_URL=$(grep -E '^portal_api_url:' "$CONFIG_FILE" | sed 's/portal_api_url:\\s*//' | tr -d '"' | tr -d "'")
515
+ PORTAL_API_URL="\${PORTAL_API_URL:-https://portal.duffcloudservices.com}"
516
+ GOOGLE_ANALYTICS_ID=$(grep -E '^google_analytics_id:' "$CONFIG_FILE" | sed 's/google_analytics_id:\\s*//' | tr -d '"' | tr -d "'")
517
+ PROD_APPINSIGHTS_CONNECTION_STRING=$(grep -E '^prod_appinsights_connection_string:' "$CONFIG_FILE" | sed 's/prod_appinsights_connection_string:\\s*//' | tr -d '"' | tr -d "'" || true)
518
+ DEV_APPINSIGHTS_CONNECTION_STRING=$(grep -E '^dev_appinsights_connection_string:' "$CONFIG_FILE" | sed 's/dev_appinsights_connection_string:\\s*//' | tr -d '"' | tr -d "'" || true)
519
+
520
+ # Azure Authentication
521
+ AZURE_CLIENT_ID=$(grep -E '^\\s*client_id:' "$CONFIG_FILE" | sed 's/.*client_id:\\s*//' | tr -d '"' | tr -d "'")
522
+ AZURE_TENANT_ID=$(grep -E '^\\s*tenant_id:' "$CONFIG_FILE" | sed 's/.*tenant_id:\\s*//' | tr -d '"' | tr -d "'")
523
+ AZURE_SUBSCRIPTION_ID=$(grep -E '^\\s*subscription_id:' "$CONFIG_FILE" | sed 's/.*subscription_id:\\s*//' | tr -d '"' | tr -d "'")
524
+
525
+ # Build Configuration
526
+ APP_LOCATION=$(grep -E '^\\s*app_location:' "$CONFIG_FILE" | sed 's/.*app_location:\\s*//' | tr -d '"' | tr -d "'")
527
+ OUTPUT_LOCATION=$(grep -E '^\\s*output_location:' "$CONFIG_FILE" | sed 's/.*output_location:\\s*//' | tr -d '"' | tr -d "'")
528
+
529
+ APP_LOCATION="\${APP_LOCATION:-dist}"
530
+ OUTPUT_LOCATION="\${OUTPUT_LOCATION:-.}"
531
+
532
+ if [ -z "$SWA_RESOURCE_ID" ]; then
533
+ echo "::error::Missing swa_resource_id in $CONFIG_FILE"
534
+ exit 1
535
+ fi
536
+
537
+ echo "site_name=$SITE_NAME" >> $GITHUB_OUTPUT
538
+ echo "site_slug=$SITE_SLUG" >> $GITHUB_OUTPUT
539
+ echo "swa_resource_id=$SWA_RESOURCE_ID" >> $GITHUB_OUTPUT
540
+ echo "app_location=$APP_LOCATION" >> $GITHUB_OUTPUT
541
+ echo "output_location=$OUTPUT_LOCATION" >> $GITHUB_OUTPUT
542
+ echo "azure_client_id=$AZURE_CLIENT_ID" >> $GITHUB_OUTPUT
543
+ echo "azure_tenant_id=$AZURE_TENANT_ID" >> $GITHUB_OUTPUT
544
+ echo "azure_subscription_id=$AZURE_SUBSCRIPTION_ID" >> $GITHUB_OUTPUT
545
+ echo "portal_api_url=$PORTAL_API_URL" >> $GITHUB_OUTPUT
546
+ echo "google_analytics_id=$GOOGLE_ANALYTICS_ID" >> $GITHUB_OUTPUT
547
+ echo "prod_appinsights_connection_string=$PROD_APPINSIGHTS_CONNECTION_STRING" >> $GITHUB_OUTPUT
548
+ echo "dev_appinsights_connection_string=$DEV_APPINSIGHTS_CONNECTION_STRING" >> $GITHUB_OUTPUT
549
+
550
+ - name: Azure login via OIDC
551
+ uses: azure/login@v2
552
+ with:
553
+ client-id: \${{ steps.config.outputs.azure_client_id || vars.AZURE_CLIENT_ID }}
554
+ tenant-id: \${{ steps.config.outputs.azure_tenant_id || vars.AZURE_TENANT_ID }}
555
+ subscription-id: \${{ steps.config.outputs.azure_subscription_id || vars.AZURE_SUBSCRIPTION_ID }}
556
+
557
+ - name: Get deployment tokens
558
+ id: tokens
559
+ run: |
560
+ # Get access token for DCS API
561
+ ACCESS_TOKEN=$(az account get-access-token --resource "api://304711b9-8532-4659-beb7-c85726de9ae5" --query accessToken -o tsv)
562
+
563
+ SITE_NAME="\${{ steps.config.outputs.site_name }}"
564
+ SITE_SLUG="\${{ steps.config.outputs.site_slug }}"
565
+ PORTAL_API_URL="\${{ steps.config.outputs.portal_api_url }}"
566
+
567
+ RESPONSE=$(curl -s -X POST "\${PORTAL_API_URL}/api/v1/sites/deployment-tokens" \\
568
+ -H "Authorization: Bearer $ACCESS_TOKEN" \\
569
+ -H "Content-Type: application/json" \\
570
+ -d "{\\"siteName\\": \\"$SITE_NAME\\", \\"siteSlug\\": \\"$SITE_SLUG\\"}")
571
+
572
+ SWA_TOKEN=$(echo $RESPONSE | jq -r .swaToken)
573
+
574
+ if [ -z "$SWA_TOKEN" ] || [ "$SWA_TOKEN" == "null" ]; then
575
+ echo "::error::Failed to retrieve SWA token from DCS API"
576
+ echo "Response: $RESPONSE"
577
+ exit 1
578
+ fi
579
+
580
+ echo "::add-mask::$SWA_TOKEN"
581
+ echo "swa_token=$SWA_TOKEN" >> $GITHUB_OUTPUT
582
+
583
+ - name: Setup Node.js
584
+ uses: actions/setup-node@v4
585
+ with:
586
+ node-version: "22"
587
+
588
+ - name: Install pnpm
589
+ uses: pnpm/action-setup@v4
590
+
591
+ - name: Setup pnpm cache
592
+ uses: actions/cache@v4
593
+ with:
594
+ path: ~/.pnpm-store
595
+ key: \${{ runner.os }}-pnpm-\${{ hashFiles('**/pnpm-lock.yaml') }}
596
+ restore-keys: |
597
+ \${{ runner.os }}-pnpm-
598
+
599
+ - name: Install dependencies
600
+ run: pnpm install --frozen-lockfile
601
+
602
+ - name: Create environment file
603
+ run: |
604
+ cat > .env << EOF
605
+ VITE_APP_VERSION="\${{ github.sha }}"
606
+ VITE_BACKEND_URI="\${{ steps.config.outputs.portal_api_url }}"
607
+ VITE_SITE_SLUG="\${{ steps.config.outputs.site_slug }}"
608
+ VITE_GOOGLE_ANALYTICS_ID="\${{ steps.config.outputs.google_analytics_id }}"
609
+ VITE_PROD_APPINSIGHTS_CONNECTION_STRING="\${{ steps.config.outputs.prod_appinsights_connection_string }}"
610
+ VITE_DEV_APPINSIGHTS_CONNECTION_STRING="\${{ steps.config.outputs.dev_appinsights_connection_string }}"
611
+ EOF
612
+
613
+ - name: Build site
614
+ run: pnpm run build
615
+
616
+ - name: Inject Google Analytics
617
+ if: steps.config.outputs.google_analytics_id != ''
618
+ run: |
619
+ GA_ID="\${{ steps.config.outputs.google_analytics_id }}"
620
+ OUTPUT_DIR="\${{ steps.config.outputs.app_location }}"
621
+ echo "Injecting Google Analytics tag \${GA_ID} into \${OUTPUT_DIR}..."
622
+ find "$OUTPUT_DIR" -name "*.html" -exec sed -i "s|</head>|<script async src=\\"https://www.googletagmanager.com/gtag/js?id=\${GA_ID}\\"></script><script>window.dataLayer=window.dataLayer\\|\\|[];function gtag(){dataLayer.push(arguments)}gtag('js',new Date());gtag('config','\${GA_ID}')</script>\\n</head>|" {} +
623
+ INJECTED=$(grep -rl "googletagmanager" "$OUTPUT_DIR" --include="*.html" | wc -l)
624
+ echo "Injected GA tag into \${INJECTED} HTML file(s)"
625
+
626
+ - name: Deploy to Azure Static Web Apps
627
+ uses: Azure/static-web-apps-deploy@v1
628
+ with:
629
+ azure_static_web_apps_api_token: \${{ steps.tokens.outputs.swa_token }}
630
+ repo_token: \${{ secrets.GITHUB_TOKEN }}
631
+ action: "upload"
632
+ app_location: \${{ steps.config.outputs.app_location }}
633
+ output_location: \${{ steps.config.outputs.output_location }}
634
+ skip_app_build: true
635
+ deployment_environment: \${{ steps.environment.outputs.swa_environment != '' && steps.environment.outputs.swa_environment || '' }}
619
636
  `;
620
637
 
621
638
  // src/commands/init.ts
@@ -2259,10 +2276,14 @@ async function detectFallbackContentSections(page, existingSections) {
2259
2276
  }
2260
2277
  async function capturePageSnapshot(browser, pageInfo, snapshotConfig, siteSlug, validTextKeys, outputDir, verbose) {
2261
2278
  const context = await browser.newContext({
2262
- viewport: snapshotConfig.viewport
2279
+ viewport: snapshotConfig.viewport,
2280
+ ignoreHTTPSErrors: true
2263
2281
  });
2264
2282
  const page = await context.newPage();
2265
- const url = pageInfo.path.startsWith("http") ? pageInfo.path : `${process.env.SITE_BASE_URL || "http://localhost:5173"}${pageInfo.path}`;
2283
+ const rawUrl = pageInfo.path.startsWith("http") ? pageInfo.path : `${process.env.SITE_BASE_URL || "http://localhost:5173"}${pageInfo.path}`;
2284
+ const pageUrl = new URL(rawUrl);
2285
+ pageUrl.searchParams.set("dcs-hide-ribbon", "");
2286
+ const url = pageUrl.toString();
2266
2287
  if (verbose) {
2267
2288
  console.log(` Capturing: ${pageInfo.slug} (${url})`);
2268
2289
  }
@@ -2300,6 +2321,16 @@ async function capturePageSnapshot(browser, pageInfo, snapshotConfig, siteSlug,
2300
2321
  path: fullPagePath,
2301
2322
  fullPage: snapshotConfig.captureFullPage
2302
2323
  });
2324
+ const thumbnailViewport = { width: 640, height: 360 };
2325
+ await page.setViewportSize(thumbnailViewport);
2326
+ await page.waitForTimeout(200);
2327
+ const thumbnailPath = path4.join(pageOutputDir, "thumbnail.png");
2328
+ await page.screenshot({
2329
+ path: thumbnailPath,
2330
+ fullPage: false
2331
+ });
2332
+ await page.setViewportSize(snapshotConfig.viewport);
2333
+ await page.waitForTimeout(100);
2303
2334
  const sections = [];
2304
2335
  const seenDomPaths = /* @__PURE__ */ new Set();
2305
2336
  const getAccurateBounds = async (el, sectionType) => {
@@ -2508,8 +2539,8 @@ async function capturePageSnapshot(browser, pageInfo, snapshotConfig, siteSlug,
2508
2539
  sections.sort((a, b) => a.bounds.y - b.bounds.y);
2509
2540
  return {
2510
2541
  pageSlug: pageInfo.slug,
2511
- pagePath: pageInfo.path,
2512
- pageType: pageInfo.config.type,
2542
+ path: pageInfo.path,
2543
+ type: pageInfo.config.type,
2513
2544
  deletable: pageInfo.config.deletable,
2514
2545
  title: title || pageInfo.config.title,
2515
2546
  capturedAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -2600,6 +2631,7 @@ async function captureSnapshotsCommand(options) {
2600
2631
  const browser = await chromium.launch();
2601
2632
  spinner.succeed("Browser launched");
2602
2633
  const snapshots = [];
2634
+ const failures = [];
2603
2635
  for (const pageInfo of pagesToCapture) {
2604
2636
  const pageSpinner = ora3(`Capturing ${pageInfo.slug}...`).start();
2605
2637
  try {
@@ -2618,23 +2650,27 @@ async function captureSnapshotsCommand(options) {
2618
2650
  fs4.writeFileSync(snapshotPath, JSON.stringify(snapshot, null, 2));
2619
2651
  pageSpinner.succeed(`${pageInfo.slug}: ${snapshot.sections.length} sections captured`);
2620
2652
  } catch (error) {
2653
+ const message = `${pageInfo.slug}: ${error.message}`;
2654
+ failures.push(message);
2621
2655
  pageSpinner.fail(`${pageInfo.slug}: Failed - ${error.message}`);
2622
2656
  }
2623
2657
  }
2624
2658
  await browser.close();
2625
2659
  const manifest = {
2626
2660
  siteSlug: config2.siteSlug,
2627
- lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
2661
+ capturedAt: (/* @__PURE__ */ new Date()).toISOString(),
2662
+ capturedVersion: process.env.GITHUB_SHA,
2628
2663
  deploymentRef: process.env.GITHUB_REF || "local",
2629
2664
  pagesConfigVersion: config2.version,
2630
2665
  pages: snapshots.map((s) => ({
2631
2666
  pageSlug: s.pageSlug,
2632
- pagePath: s.pagePath,
2633
- pageType: s.pageType,
2667
+ pagePath: s.path,
2668
+ pageType: s.type,
2634
2669
  deletable: s.deletable,
2635
2670
  title: s.title,
2636
2671
  capturedAt: s.capturedAt,
2637
- sectionCount: s.sections.length
2672
+ sectionCount: s.sections.length,
2673
+ hasSnapshot: true
2638
2674
  }))
2639
2675
  };
2640
2676
  const manifestPath = path4.join(outputDir, config2.siteSlug, "manifest.json");
@@ -2642,10 +2678,13 @@ async function captureSnapshotsCommand(options) {
2642
2678
  console.log("");
2643
2679
  console.log(chalk7.green("\u2705 Snapshot capture complete!"));
2644
2680
  console.log(chalk7.gray(` Captured ${snapshots.length} pages`));
2645
- console.log(chalk7.gray(` - Static: ${snapshots.filter((s) => s.pageType === "static").length}`));
2646
- console.log(chalk7.gray(` - Index: ${snapshots.filter((s) => s.pageType === "index").length}`));
2647
- console.log(chalk7.gray(` - Dynamic: ${snapshots.filter((s) => s.pageType === "dynamic").length}`));
2681
+ console.log(chalk7.gray(` - Static: ${snapshots.filter((s) => s.type === "static").length}`));
2682
+ console.log(chalk7.gray(` - Index: ${snapshots.filter((s) => s.type === "index").length}`));
2683
+ console.log(chalk7.gray(` - Dynamic: ${snapshots.filter((s) => s.type === "dynamic").length}`));
2648
2684
  console.log(chalk7.gray(` Output: ${outputDir}`));
2685
+ if (failures.length > 0) {
2686
+ throw new Error(`Snapshot capture failed for ${failures.length} page(s): ${failures.join("; ")}`);
2687
+ }
2649
2688
  }
2650
2689
 
2651
2690
  // src/index.ts