snapdrift 0.7.0 → 0.8.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/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.8.1 - 2026-08-06
4
+
5
+ ### Fixes
6
+
7
+ - **The root action can actually be published to the Marketplace** — its description was 130 characters, and GitHub rejects the listing at 125 or more, so ticking "Publish this Action to the GitHub Marketplace" on the `v0.8.0` release failed validation. Shortened to 115 characters, leading with "visual regression" so the listing matches what people search for. `npm run validate:actions` and `tests/marketplace-metadata.test.js` now enforce the limit against the root action; the sub-action descriptions are unaffected, since the rule only applies to the published entry point.
8
+
9
+ ## 0.8.0 - 2026-08-06
10
+
11
+ ### Features
12
+
13
+ - **Root `action.yml` for the GitHub Marketplace** — SnapDrift's nine composite actions all live under `actions/*`, and Marketplace lists exactly one action per repository whose metadata file must sit at the repository root, so none of them were publishable. `ranacseruet/snapdrift@vX` is now a dispatcher action that runs the baseline pipeline with `mode: baseline` and the pull request pipeline with `mode: pr-diff`, forwarding every input and output of the corresponding wrapper. Existing `ranacseruet/snapdrift/actions/*@vX` references are unaffected and stay supported. The dispatcher pins absolute inner refs rather than `uses: ./actions/...`, because a relative `uses:` inside a composite action resolves against the caller's workspace; `tests/marketplace-metadata.test.js` fails the build if those pins drift from the package version. Released versions pin the inner ref to a commit SHA so that a consumer pinning the dispatcher to a SHA gets an immutable chain rather than a mutable tag one level down.
14
+ - **`branding` on every action** — required for the Marketplace listing, and applied to all nine sub-actions so the metadata stays consistent if any of them is ever split into its own repository. `npm run validate:actions` now rejects a missing icon or a color outside GitHub's allowed set, and covers the root action.
15
+
16
+ ### Documentation
17
+
18
+ - Updated the README, Integration Guide, and baseline workflow template action examples to use the current release.
19
+ - Documented the root action as the primary entry point across the README quickstart, the Integration Guide, and the baseline workflow template, with a new "Choosing an entry point" section mapping each `mode` to its equivalent wrapper action. The documentation version contract test now covers root-action references and prose references, not just `uses:` lines for the two wrappers.
20
+
3
21
  ## 0.7.0 - 2026-07-26
4
22
 
5
23
  ### Features
package/README.md CHANGED
@@ -3,6 +3,7 @@
3
3
  ![SnapDrift](assets/snapdrift-logo-banner.png)
4
4
 
5
5
  ![CI](https://github.com/ranacseruet/snapdrift/actions/workflows/ci.yml/badge.svg)
6
+ [![GitHub Marketplace](https://img.shields.io/badge/marketplace-SnapDrift-purple?logo=github)](https://github.com/marketplace/actions/snapdrift)
6
7
  ![Node](https://img.shields.io/badge/node-%3E%3D22-brightgreen)
7
8
  ![License: MIT](https://img.shields.io/badge/license-MIT-blue)
8
9
 
@@ -47,8 +48,9 @@ You keep ownership of checkout, build, startup, readiness, and teardown. SnapDri
47
48
 
48
49
  ```yaml
49
50
  - name: SnapDrift Baseline
50
- uses: ranacseruet/snapdrift/actions/baseline@v0.4.0
51
+ uses: ranacseruet/snapdrift@v0.8.1
51
52
  with:
53
+ mode: baseline
52
54
  repo-config-path: .github/snapdrift.json
53
55
  ```
54
56
 
@@ -56,14 +58,17 @@ You keep ownership of checkout, build, startup, readiness, and teardown. SnapDri
56
58
 
57
59
  ```yaml
58
60
  - name: SnapDrift Report
59
- uses: ranacseruet/snapdrift/actions/pr-diff@v0.4.0
61
+ uses: ranacseruet/snapdrift@v0.8.1
60
62
  with:
63
+ mode: pr-diff
61
64
  github-token: ${{ secrets.GITHUB_TOKEN }}
62
65
  repo-config-path: .github/snapdrift.json
63
66
  ```
64
67
 
65
68
  That is the full integration. See the [Integration Guide](docs/integration-guide.md) for workflow examples, permissions, compatibility notes, advanced overrides, and the hosted Snap backend (`provider: "snap"`).
66
69
 
70
+ The root action dispatches on `mode`. The same pipelines are also published as standalone actions — `ranacseruet/snapdrift/actions/baseline` and `ranacseruet/snapdrift/actions/pr-diff` — along with the lower-level `capture`, `compare`, `scope`, `resolve-baseline`, `stage`, `comment`, and `enforce` actions for custom orchestration.
71
+
67
72
  ## Local CLI
68
73
 
69
74
  SnapDrift ships a `snapdrift` CLI for running captures, diffs, migrations, and config initialization locally against a running app — no GitHub Actions required. Use it during development to validate UI changes before pushing.
package/action.yml ADDED
@@ -0,0 +1,175 @@
1
+ name: SnapDrift
2
+ description: Visual regression for Playwright apps — capture routes, diff against a baseline, and report drift on pull requests.
3
+ author: CodeSamplez
4
+
5
+ branding:
6
+ icon: eye
7
+ color: purple
8
+
9
+ # GitHub Marketplace lists exactly one action per repository, and its metadata file must live at
10
+ # the repository root. This dispatcher is that entry point: it forwards to the wrapper actions
11
+ # under `actions/`, which remain directly usable (and unchanged) for custom orchestration.
12
+ #
13
+ # The inner `uses:` refs are pinned to the current release rather than a relative path because
14
+ # `uses: ./path` inside a composite action resolves against the *caller's* workspace, not the
15
+ # action's own directory. `tests/marketplace-metadata.test.js` fails the build if those pins drift
16
+ # from the root `package.json` version.
17
+ #
18
+ # The pins are commit SHAs, so that a consumer pinning this action to a SHA gets an immutable chain
19
+ # rather than a mutable tag one level down. The SHA is the commit holding the wrapper content this
20
+ # release ships — not the release tag itself, which a release commit cannot reference. It therefore
21
+ # only moves when the wrappers change. The trailing comment records the release the pin was last
22
+ # verified for, and the test requires it to match the package version, so every release has to look
23
+ # at it. The test accepts a plain `@vX.Y.Z` tag too, for unreleased refs.
24
+
25
+ inputs:
26
+ mode:
27
+ description: Which SnapDrift pipeline to run — `pr-diff` for pull request drift detection, or `baseline` to publish a baseline.
28
+ required: false
29
+ default: pr-diff
30
+ repo-config-path:
31
+ description: Path to the SnapDrift config file.
32
+ required: false
33
+ default: .github/snapdrift.json
34
+ route-ids:
35
+ description: Optional comma-separated route ids. In `pr-diff` mode, providing these skips changed-file scope detection.
36
+ required: false
37
+ default: ''
38
+ artifact-retention-days:
39
+ description: Retention period for the uploaded artifact.
40
+ required: false
41
+ default: '30'
42
+ github-token:
43
+ description: Token used to inspect pull request files, fetch baselines, and publish SnapDrift PR reports. Used in `pr-diff` mode only.
44
+ required: false
45
+ default: ${{ github.token }}
46
+
47
+ # `baseline` mode only.
48
+ upload-artifact:
49
+ description: Whether to upload the staged baseline artifact. Used in `baseline` mode only.
50
+ required: false
51
+ default: 'true'
52
+
53
+ # `pr-diff` mode only.
54
+ pr-number:
55
+ description: Optional pull request number override.
56
+ required: false
57
+ default: ''
58
+ force-run:
59
+ description: Whether to force a full run when route ids are not explicitly provided.
60
+ required: false
61
+ default: 'false'
62
+ force-run-reason:
63
+ description: Reason to report when force-run is enabled.
64
+ required: false
65
+ default: forced
66
+ baseline-repository:
67
+ description: Repository in owner/name format that publishes the baseline.
68
+ required: false
69
+ default: ''
70
+ baseline-workflow-id:
71
+ description: Workflow id or filename to search for the baseline.
72
+ required: false
73
+ default: ci.yml
74
+ baseline-branch:
75
+ description: Branch that publishes the baseline.
76
+ required: false
77
+ default: main
78
+ comment-on-pr:
79
+ description: Whether to publish the SnapDrift PR report.
80
+ required: false
81
+ default: 'true'
82
+ max-changed-rows:
83
+ description: Maximum number of changed-route rows shown in the PR comment before truncation.
84
+ required: false
85
+ default: '20'
86
+ max-error-rows:
87
+ description: Maximum number of error rows shown in the PR comment before truncation.
88
+ required: false
89
+ default: '10'
90
+
91
+ outputs:
92
+ # Shared across both modes.
93
+ artifact-name:
94
+ description: Name of the uploaded artifact.
95
+ value: ${{ steps.baseline.outputs.artifact-name || steps.pr-diff.outputs.artifact-name }}
96
+ bundle-dir:
97
+ description: Staged artifact bundle directory.
98
+ value: ${{ steps.baseline.outputs.bundle-dir || steps.pr-diff.outputs.bundle-dir }}
99
+ selected-route-ids:
100
+ description: Comma-separated route ids selected for the run.
101
+ value: ${{ steps.baseline.outputs.selected-route-ids || steps.pr-diff.outputs.selected-route-ids }}
102
+
103
+ # `baseline` mode only.
104
+ results-file:
105
+ description: Absolute path to the generated baseline results file.
106
+ value: ${{ steps.baseline.outputs.results-file }}
107
+ manifest-file:
108
+ description: Absolute path to the generated baseline manifest file.
109
+ value: ${{ steps.baseline.outputs.manifest-file }}
110
+ screenshots-root:
111
+ description: Absolute path to the generated screenshots root.
112
+ value: ${{ steps.baseline.outputs.screenshots-root }}
113
+
114
+ # `pr-diff` mode only.
115
+ should-run:
116
+ description: Whether the SnapDrift flow ran or intentionally skipped due to scope.
117
+ value: ${{ steps.pr-diff.outputs.should-run }}
118
+ scope-reason:
119
+ description: Reason for the final scope decision.
120
+ value: ${{ steps.pr-diff.outputs.scope-reason }}
121
+ baseline-found:
122
+ description: Whether a usable baseline was found.
123
+ value: ${{ steps.pr-diff.outputs.baseline-found }}
124
+ status:
125
+ description: Final SnapDrift status.
126
+ value: ${{ steps.pr-diff.outputs.status }}
127
+ summary-path:
128
+ description: Path to the final summary JSON file.
129
+ value: ${{ steps.pr-diff.outputs.summary-path }}
130
+ markdown-path:
131
+ description: Path to the final summary markdown file.
132
+ value: ${{ steps.pr-diff.outputs.markdown-path }}
133
+
134
+ runs:
135
+ using: composite
136
+ steps:
137
+ - name: Validate mode
138
+ shell: bash
139
+ env:
140
+ MODE: ${{ inputs.mode }}
141
+ run: |
142
+ set -euo pipefail
143
+ if [ "$MODE" != "pr-diff" ] && [ "$MODE" != "baseline" ]; then
144
+ echo "::error::Unsupported SnapDrift mode '$MODE'. Expected 'pr-diff' or 'baseline'."
145
+ exit 1
146
+ fi
147
+
148
+ - name: SnapDrift baseline
149
+ id: baseline
150
+ if: inputs.mode == 'baseline'
151
+ uses: ranacseruet/snapdrift/actions/baseline@539953ab07065be805be393d69bd3c9b6d53cf6e # v0.8.1
152
+ with:
153
+ repo-config-path: ${{ inputs.repo-config-path }}
154
+ route-ids: ${{ inputs.route-ids }}
155
+ artifact-retention-days: ${{ inputs.artifact-retention-days }}
156
+ upload-artifact: ${{ inputs.upload-artifact }}
157
+
158
+ - name: SnapDrift pull request diff
159
+ id: pr-diff
160
+ if: inputs.mode == 'pr-diff'
161
+ uses: ranacseruet/snapdrift/actions/pr-diff@539953ab07065be805be393d69bd3c9b6d53cf6e # v0.8.1
162
+ with:
163
+ github-token: ${{ inputs.github-token }}
164
+ repo-config-path: ${{ inputs.repo-config-path }}
165
+ pr-number: ${{ inputs.pr-number }}
166
+ route-ids: ${{ inputs.route-ids }}
167
+ force-run: ${{ inputs.force-run }}
168
+ force-run-reason: ${{ inputs.force-run-reason }}
169
+ baseline-repository: ${{ inputs.baseline-repository }}
170
+ baseline-workflow-id: ${{ inputs.baseline-workflow-id }}
171
+ baseline-branch: ${{ inputs.baseline-branch }}
172
+ artifact-retention-days: ${{ inputs.artifact-retention-days }}
173
+ comment-on-pr: ${{ inputs.comment-on-pr }}
174
+ max-changed-rows: ${{ inputs.max-changed-rows }}
175
+ max-error-rows: ${{ inputs.max-error-rows }}
@@ -1,6 +1,10 @@
1
1
  name: SnapDrift Baseline
2
2
  description: Capture configured routes, stage a SnapDrift baseline, and upload it with the configured artifact name.
3
3
 
4
+ branding:
5
+ icon: camera
6
+ color: purple
7
+
4
8
  inputs:
5
9
  repo-config-path:
6
10
  description: Path to the SnapDrift config file.
@@ -1,6 +1,10 @@
1
1
  name: SnapDrift Capture
2
2
  description: Capture configured SnapDrift routes and emit the standard results and manifest files.
3
3
 
4
+ branding:
5
+ icon: camera
6
+ color: purple
7
+
4
8
  inputs:
5
9
  repo-config-path:
6
10
  description: Path to the SnapDrift config file.
@@ -1,6 +1,10 @@
1
1
  name: SnapDrift Comment
2
2
  description: Upsert a PR comment with the SnapDrift report.
3
3
 
4
+ branding:
5
+ icon: message-square
6
+ color: purple
7
+
4
8
  inputs:
5
9
  github-token:
6
10
  description: Token used to update the pull request comment.
@@ -1,6 +1,10 @@
1
1
  name: SnapDrift Compare
2
2
  description: Compare the current capture against a baseline and emit the SnapDrift report files.
3
3
 
4
+ branding:
5
+ icon: layers
6
+ color: purple
7
+
4
8
  inputs:
5
9
  repo-config-path:
6
10
  description: Path to the SnapDrift config file.
@@ -1,6 +1,10 @@
1
1
  name: SnapDrift Enforce
2
2
  description: Evaluate the SnapDrift summary against diff.mode and fail when required.
3
3
 
4
+ branding:
5
+ icon: shield
6
+ color: purple
7
+
4
8
  inputs:
5
9
  summary-path:
6
10
  description: Path to the SnapDrift summary JSON.
@@ -1,6 +1,10 @@
1
1
  name: SnapDrift PR Diff
2
2
  description: Run the end-to-end SnapDrift pull request pipeline, including scope resolution, skipped-report handling, artifact upload, PR reporting, and enforcement.
3
3
 
4
+ branding:
5
+ icon: eye
6
+ color: purple
7
+
4
8
  inputs:
5
9
  github-token:
6
10
  description: Token used to inspect pull request files, fetch baselines, and publish SnapDrift PR reports.
@@ -1,6 +1,10 @@
1
1
  name: SnapDrift Resolve Baseline
2
2
  description: Resolve and download the latest successful SnapDrift baseline from main.
3
3
 
4
+ branding:
5
+ icon: download-cloud
6
+ color: purple
7
+
4
8
  inputs:
5
9
  github-token:
6
10
  description: Token used to query workflow runs and download artifacts.
@@ -1,6 +1,10 @@
1
1
  name: SnapDrift Scope
2
2
  description: Determine whether SnapDrift should run and which configured routes should be selected for the current change set.
3
3
 
4
+ branding:
5
+ icon: filter
6
+ color: purple
7
+
4
8
  inputs:
5
9
  github-token:
6
10
  description: Token used to query pull request changed files.
@@ -1,6 +1,10 @@
1
1
  name: SnapDrift Stage
2
2
  description: Stage baseline or drift artifacts into the SnapDrift bundle structure before upload.
3
3
 
4
+ branding:
5
+ icon: package
6
+ color: purple
7
+
4
8
  inputs:
5
9
  artifact-type:
6
10
  description: Either baseline or diff.
@@ -46,8 +46,9 @@ SnapDrift owns route selection, capture, comparison, skipped-report generation,
46
46
 
47
47
  ```yaml
48
48
  - name: SnapDrift Baseline
49
- uses: ranacseruet/snapdrift/actions/baseline@v0.4.0
49
+ uses: ranacseruet/snapdrift@v0.8.1
50
50
  with:
51
+ mode: baseline
51
52
  repo-config-path: .github/snapdrift.json
52
53
  artifact-retention-days: '30'
53
54
  ```
@@ -70,12 +71,30 @@ Then add SnapDrift after the app is running:
70
71
 
71
72
  ```yaml
72
73
  - name: SnapDrift Report
73
- uses: ranacseruet/snapdrift/actions/pr-diff@v0.4.0
74
+ uses: ranacseruet/snapdrift@v0.8.1
74
75
  with:
76
+ mode: pr-diff
75
77
  github-token: ${{ secrets.GITHUB_TOKEN }}
76
78
  repo-config-path: .github/snapdrift.json
77
79
  ```
78
80
 
81
+ ## Choosing an entry point
82
+
83
+ `ranacseruet/snapdrift` is the GitHub Marketplace entry point. It is a thin dispatcher: `mode: baseline` runs the baseline pipeline and `mode: pr-diff` runs the pull request pipeline, forwarding every input and output of the underlying wrapper action.
84
+
85
+ The wrappers remain published in their own right, and are equivalent to the dispatcher:
86
+
87
+ | Entry point | Equivalent to |
88
+ |-------------|---------------|
89
+ | `ranacseruet/snapdrift@v0.8.1` with `mode: baseline` | `ranacseruet/snapdrift/actions/baseline@v0.8.1` |
90
+ | `ranacseruet/snapdrift@v0.8.1` with `mode: pr-diff` | `ranacseruet/snapdrift/actions/pr-diff@v0.8.1` |
91
+
92
+ Use the wrappers directly when you want to skip the dispatcher's input indirection, and the lower-level `capture`, `compare`, `scope`, `resolve-baseline`, `stage`, `comment`, and `enforce` actions when you need to orchestrate the stages yourself.
93
+
94
+ ### Pinning
95
+
96
+ The dispatcher loads its wrapper from a pinned ref of this same repository. Released versions pin that ref to a commit SHA, so resolving `ranacseruet/snapdrift` to a SHA freezes the whole chain. If you are consuming an unreleased ref (`@main`, or a branch), the inner ref is a tag instead, and a SHA pin on the dispatcher does not freeze what it delegates to — pin the wrapper actions directly if that matters to you.
97
+
79
98
  ## Example workflow (Node app)
80
99
 
81
100
  ```yaml
@@ -111,8 +130,9 @@ jobs:
111
130
  done
112
131
 
113
132
  - name: SnapDrift Report
114
- uses: ranacseruet/snapdrift/actions/pr-diff@v0.4.0
133
+ uses: ranacseruet/snapdrift@v0.8.1
115
134
  with:
135
+ mode: pr-diff
116
136
  github-token: ${{ secrets.GITHUB_TOKEN }}
117
137
  repo-config-path: .github/snapdrift.json
118
138
  ```
@@ -150,8 +170,9 @@ jobs:
150
170
  done
151
171
 
152
172
  - name: SnapDrift Report
153
- uses: ranacseruet/snapdrift/actions/pr-diff@v0.4.0
173
+ uses: ranacseruet/snapdrift@v0.8.1
154
174
  with:
175
+ mode: pr-diff
155
176
  github-token: ${{ secrets.GITHUB_TOKEN }}
156
177
  repo-config-path: .github/snapdrift.json
157
178
  ```
@@ -70,8 +70,9 @@ jobs:
70
70
  # ────────────────────────────────────────────────────────────────────────
71
71
 
72
72
  - name: Publish SnapDrift baseline
73
- uses: ranacseruet/snapdrift/actions/baseline@v0.4.0
73
+ uses: ranacseruet/snapdrift@v0.8.1
74
74
  with:
75
+ mode: baseline
75
76
  repo-config-path: .github/snapdrift.json
76
77
  artifact-retention-days: '30'
77
78
 
@@ -5,6 +5,7 @@ import path from 'node:path';
5
5
  import * as yaml from 'js-yaml';
6
6
 
7
7
  /** @typedef {import('../types/visual-diff-types').InitWarning} InitWarning */
8
+ /** @typedef {import('../types/visual-diff-types').JsonObject} JsonObject */
8
9
 
9
10
  const DEFAULT_OUTPUT_DIR = '.github';
10
11
  const DEFAULT_CONFIG_FILENAME = 'snapdrift.json';
@@ -13,8 +14,8 @@ const MIGRATION_NOTES_FILENAME = 'MIGRATION_NOTES.md';
13
14
  /**
14
15
  * Find the step that uses the Snap visual testing action.
15
16
  *
16
- * @param {object} workflow - Parsed YAML workflow
17
- * @returns {{ step: object, jobName: string, stepIndex: number } | null}
17
+ * @param {JsonObject} workflow - Parsed YAML workflow
18
+ * @returns {{ step: JsonObject, jobName: string, stepIndex: number } | null}
18
19
  */
19
20
  function findSnapActionStep(workflow) {
20
21
  const jobs = workflow.jobs || {};
@@ -34,7 +35,7 @@ function findSnapActionStep(workflow) {
34
35
  /**
35
36
  * Map Snap action enforcement mode to snapdrift diff.mode.
36
37
  *
37
- * @param {object} inputs
38
+ * @param {JsonObject} inputs
38
39
  * @returns {string}
39
40
  */
40
41
  function mapDiffMode(inputs) {
@@ -52,7 +53,7 @@ function mapDiffMode(inputs) {
52
53
  /**
53
54
  * Extract Snap action inputs and translate to snapdrift config fields.
54
55
  *
55
- * @param {object} step - The workflow step using the Snap action
56
+ * @param {JsonObject} step - The workflow step using the Snap action
56
57
  * @returns {{ config: object, warnings: InitWarning[] }}
57
58
  */
58
59
  function translateStepToConfig(step) {
@@ -87,7 +88,7 @@ function translateStepToConfig(step) {
87
88
  }
88
89
 
89
90
  // Snap provider config (if present in inputs)
90
- /** @type {object | undefined} */
91
+ /** @type {JsonObject | undefined} */
91
92
  let snap;
92
93
  const snapApiUrl = inputs['snap-api-url'] || inputs.snap_api_url;
93
94
  const snapApiKeyEnv = inputs['snap-api-key-env'] || inputs.snap_api_key_env;
@@ -195,10 +196,11 @@ export async function runInitFromAction(actionYamlPath) {
195
196
  throw new Error(`Cannot read workflow file: ${actionYamlPath}`);
196
197
  }
197
198
 
198
- const workflow = yaml.load(workflowContent);
199
- if (!workflow || typeof workflow !== 'object') {
199
+ const parsedWorkflow = yaml.load(workflowContent);
200
+ if (!parsedWorkflow || typeof parsedWorkflow !== 'object') {
200
201
  throw new Error(`Invalid workflow YAML: ${actionYamlPath}`);
201
202
  }
203
+ const workflow = /** @type {JsonObject} */ (parsedWorkflow);
202
204
 
203
205
  // Find the Snap action step
204
206
  const snapStep = findSnapActionStep(workflow);
@@ -6,6 +6,8 @@ import path from 'node:path';
6
6
 
7
7
  import { createProvider } from './provider.mjs';
8
8
 
9
+ /** @typedef {import('../types/visual-diff-types').JsonObject} JsonObject */
10
+
9
11
  /**
10
12
  * Resolve the current commit HEAD SHA.
11
13
  * Tries GITHUB_SHA first, then git rev-parse HEAD.
@@ -28,7 +30,7 @@ function resolveHeadSha() {
28
30
  * Read local baseline files from a directory.
29
31
  *
30
32
  * @param {string} baselineDir
31
- * @returns {Promise<{ results: object, manifest: object, screenshots: Array<{ filename: string, data: string }>, headSha: string }>}
33
+ * @returns {Promise<{ results: JsonObject, manifest: JsonObject, screenshots: Array<{ filename: string, data: string }>, headSha: string }>}
32
34
  */
33
35
  export async function readLocalBaselines(baselineDir) {
34
36
  const resultsPath = path.join(baselineDir, 'results.json');
@@ -130,7 +132,7 @@ export async function runMigrateToLocal(config, opts) {
130
132
  const provider = createProvider('snap', config);
131
133
  const baselineDir = opts.baselineDir;
132
134
 
133
- /** @type {{ results: object, manifest: object, screenshots: Array<{ filename: string, data: Buffer }>, engine: { name: string, version: string } }} */
135
+ /** @type {{ results: JsonObject, manifest: JsonObject, screenshots: Array<{ filename: string, data: Buffer }>, engine: { name: string, version: string } }} */
134
136
  let exported;
135
137
  try {
136
138
  exported = await provider.exportBaselines();
@@ -23,6 +23,7 @@ import { selectConfiguredRoutes, splitCommaList, determineDriftStatus, VIEWPORT_
23
23
  /** @typedef {import('../types/visual-diff-types').ProviderBaselineData} BaselineData */
24
24
  /** @typedef {import('../types/visual-diff-types').SnapConfig} SnapConfig */
25
25
  /** @typedef {import('../types/visual-diff-types').VisualRegressionConfig} VisualRegressionConfig */
26
+ /** @typedef {import('../types/visual-diff-types').JsonObject} JsonObject */
26
27
 
27
28
  const DEFAULT_API_URL = 'https://snap.i2dev.com';
28
29
  const MAX_RETRIES = 3;
@@ -667,8 +668,8 @@ export class SnapProvider {
667
668
  *
668
669
  * @param {{ tag?: string }} [_options]
669
670
  * @returns {Promise<{
670
- * results: object,
671
- * manifest: object,
671
+ * results: JsonObject,
672
+ * manifest: JsonObject,
672
673
  * screenshots: Array<{ filename: string, data: Buffer }>,
673
674
  * engine: { name: string, version: string }
674
675
  * }>}
@@ -751,7 +752,7 @@ export class SnapProvider {
751
752
 
752
753
  /** @type {Array<{ filename: string, data: Buffer }>} */
753
754
  const screenshots = [];
754
- /** @type {Array<object>} */
755
+ /** @type {JsonObject[]} */
755
756
  const manifestEntries = [];
756
757
 
757
758
  for (const route of routes) {
@@ -1209,7 +1210,7 @@ export class SnapProvider {
1209
1210
  /**
1210
1211
  * Poll a run until it reaches a terminal state.
1211
1212
  * @param {string} runId
1212
- * @returns {Promise<object>}
1213
+ * @returns {Promise<JsonObject>}
1213
1214
  */
1214
1215
  async #pollRun(runId) {
1215
1216
  const startTime = Date.now();
@@ -1229,7 +1230,7 @@ export class SnapProvider {
1229
1230
 
1230
1231
  /**
1231
1232
  * Map a Snap run result to a VisualDiffSummary.
1232
- * @param {object} run
1233
+ * @param {JsonObject} run
1233
1234
  * @param {VisualRegressionConfig} config
1234
1235
  * @returns {import('../types/visual-diff-types').VisualDiffSummary}
1235
1236
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "snapdrift",
3
- "version": "0.7.0",
3
+ "version": "0.8.1",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "SnapDrift captures, compares, and reports UI drift for Playwright-based app checks.",
@@ -46,7 +46,7 @@
46
46
  "eslint": "^10.2.0",
47
47
  "globals": "^17.4.0",
48
48
  "jest": "^30.3.0",
49
- "typescript": "^6.0.2"
49
+ "typescript": "^7.0.2"
50
50
  },
51
51
  "jest": {
52
52
  "testEnvironment": "node",
@@ -82,6 +82,7 @@
82
82
  "./lib/init-from-action.mjs": "./lib/init-from-action.mjs"
83
83
  },
84
84
  "files": [
85
+ "action.yml",
85
86
  "actions/",
86
87
  "bin/",
87
88
  "lib/",
@@ -1,3 +1,6 @@
1
+ /** Object-shaped data decoded from an external JSON or YAML boundary. */
2
+ export type JsonObject = Record<string, any>;
3
+
1
4
  export type VisualViewportPreset = 'desktop' | 'mobile';
2
5
  export interface VisualCustomViewport { width: number; height: number; }
3
6
  export type VisualViewport = VisualViewportPreset | VisualCustomViewport;
@@ -166,8 +169,8 @@ export interface MigrateToSnapResult {
166
169
  }
167
170
 
168
171
  export interface ExportedBaseline {
169
- results: object;
170
- manifest: object;
172
+ results: JsonObject;
173
+ manifest: JsonObject;
171
174
  screenshots: Array<{ filename: string; data: Buffer }>;
172
175
  engine: { name: string; version: string };
173
176
  }