shopify-upgrade-guard 0.2.1 → 0.2.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/README.md +158 -45
- package/data/versions.json +3 -3
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/src/discovery.js +1 -1
package/README.md
CHANGED
|
@@ -1,56 +1,145 @@
|
|
|
1
1
|
# Shopify Upgrade Guard
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Catch documented Shopify API and platform upgrade risks before they become production migrations.**
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/shopify-upgrade-guard)
|
|
6
|
+
[](https://www.npmjs.com/package/shopify-upgrade-guard)
|
|
7
|
+
[](https://github.com/efegokdemir/shopify-upgrade-guard/actions/workflows/ci.yml)
|
|
8
|
+
[](https://github.com/efegokdemir/shopify-upgrade-guard/actions/workflows/codeql.yml)
|
|
9
|
+
[](LICENSE)
|
|
10
|
+
|
|
11
|
+
Shopify Upgrade Guard is an offline, evidence-backed CLI and GitHub Action for Shopify developers. It scans a repository for documented upgrade and deprecation risks, evaluates them against a target Shopify API version, and gives you migration guidance backed by official Shopify sources.
|
|
12
|
+
|
|
13
|
+
**No Shopify credentials. No telemetry. No source upload. No repository code execution.**
|
|
4
14
|
|
|
5
15
|
> Unofficial open-source developer tooling. Not affiliated with, endorsed by, or certified by Shopify.
|
|
6
16
|
|
|
7
|
-
|
|
17
|
+
## Quick start
|
|
18
|
+
|
|
19
|
+
Run it without installing anything globally:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npx shopify-upgrade-guard scan --target 2026-10
|
|
23
|
+
```
|
|
8
24
|
|
|
9
|
-
|
|
25
|
+
Or install it in a project:
|
|
10
26
|
|
|
11
27
|
```bash
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
npm ci
|
|
15
|
-
npm run build:action
|
|
16
|
-
node src/cli.js scan --path /path/to/shopify-app --target 2026-10
|
|
28
|
+
npm install --save-dev shopify-upgrade-guard
|
|
29
|
+
npx shopify-upgrade-guard scan --target 2026-10
|
|
17
30
|
```
|
|
18
31
|
|
|
19
|
-
|
|
32
|
+
Upgrade Guard separates **current debt** from **target-version blockers**, so teams can see what already exists and what becomes relevant for the migration they are planning.
|
|
33
|
+
|
|
34
|
+
## Why Upgrade Guard?
|
|
35
|
+
|
|
36
|
+
- **Target-aware** — evaluate findings against the Shopify version you actually plan to adopt.
|
|
37
|
+
- **Evidence-backed** — Shopify-specific rules link to official Shopify documentation or changelog evidence.
|
|
38
|
+
- **PR-aware** — distinguish newly introduced findings from pre-existing debt.
|
|
39
|
+
- **Baseline-friendly** — adopt the tool without forcing a big-bang cleanup of historical findings.
|
|
40
|
+
- **CI-ready** — human, JSON, and SARIF output plus a GitHub Action.
|
|
41
|
+
- **Offline by default** — ordinary scans require no network access, Shopify token, or store access.
|
|
42
|
+
- **Deterministic** — designed for repeatable CI results and reviewable rule behaviour.
|
|
20
43
|
|
|
21
44
|
## GitHub Action
|
|
22
45
|
|
|
23
|
-
|
|
46
|
+
A minimal pull-request gate:
|
|
24
47
|
|
|
25
48
|
```yaml
|
|
49
|
+
name: Shopify Upgrade Guard
|
|
50
|
+
|
|
51
|
+
on:
|
|
52
|
+
pull_request:
|
|
53
|
+
|
|
26
54
|
permissions:
|
|
27
55
|
contents: read
|
|
28
56
|
|
|
29
|
-
|
|
30
|
-
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
57
|
+
jobs:
|
|
58
|
+
upgrade-guard:
|
|
59
|
+
runs-on: ubuntu-latest
|
|
60
|
+
steps:
|
|
61
|
+
- uses: actions/checkout@v4
|
|
62
|
+
|
|
63
|
+
- uses: efegokdemir/shopify-upgrade-guard@05221e7582b701b5399b4f27d35ed92257f5ef0c # v0.2.1
|
|
64
|
+
with:
|
|
65
|
+
target: 2026-10
|
|
66
|
+
fail-on: warning
|
|
67
|
+
fail-on-new: 'true'
|
|
35
68
|
```
|
|
36
69
|
|
|
37
|
-
|
|
70
|
+
For convenience, workflows may use the movable minor release alias `efegokdemir/shopify-upgrade-guard@v0.2`. For high-assurance production workflows, pin the full reviewed commit SHA shown above; minor aliases are not immutable.
|
|
71
|
+
|
|
72
|
+
### Action inputs
|
|
38
73
|
|
|
39
|
-
|
|
74
|
+
| Input | Purpose |
|
|
75
|
+
| --- | --- |
|
|
76
|
+
| `target` | Shopify target version, for example `2026-10` |
|
|
77
|
+
| `fail-on` | Policy threshold: `never`, `error`, `warning`, or `info` |
|
|
78
|
+
| `fail-on-new` | Apply the failure threshold only to findings introduced by the PR |
|
|
79
|
+
| `path` | Repository path to scan |
|
|
40
80
|
|
|
41
|
-
|
|
81
|
+
### Action outputs
|
|
82
|
+
|
|
83
|
+
`outcome`, `finding-count`, `new-finding-count`, `error-count`, `current-versions`, and `result-json`.
|
|
84
|
+
|
|
85
|
+
The Action is bundled and runs on the current GitHub `node24` JavaScript Action runtime.
|
|
86
|
+
|
|
87
|
+
## What it catches today
|
|
88
|
+
|
|
89
|
+
The rule pack is intentionally small and high-precision.
|
|
90
|
+
|
|
91
|
+
| Rule | Detects | Evidence |
|
|
92
|
+
| --- | --- | --- |
|
|
93
|
+
| `UG-CHECKOUT-001` | `useBuyerJourneyIntercept` / `buyerJourney.intercept` | [Shopify changelog](https://shopify.dev/changelog/deprecating-the-usebuyerjourneyintercept-api-on-checkout-ui-extensions) |
|
|
94
|
+
| `UG-CHECKOUT-002` | Checkout `block_progress` capability | [Shopify changelog](https://shopify.dev/changelog/deprecating-the-usebuyerjourneyintercept-api-on-checkout-ui-extensions) |
|
|
95
|
+
| `UG-REST-001` | Legacy Admin REST usage | [REST Admin API](https://shopify.dev/docs/api/admin-rest) |
|
|
96
|
+
| `UG-VERSION-001` | Unsupported or target-retiring API versions | [API versioning](https://shopify.dev/docs/api/usage/versioning) |
|
|
97
|
+
| `UG-VERSION-002` | Supported versions older than the latest stable version | [API versioning](https://shopify.dev/docs/api/usage/versioning) |
|
|
98
|
+
| `UG-CUSTOMER-001` | Customer Account checkout removals relevant to 2026-10 | [Shopify changelog](https://shopify.dev/changelog/customer-account-api-last-incomplete-checkout-and-checkout-types-removed) |
|
|
99
|
+
| `UG-POS-001` | Removed POS `session.currentSession.staffMemberId` usage | [Shopify changelog](https://shopify.dev/changelog/removed-session-currentsession-staffmemberid-from-pos-ui-extensions-2026-10) |
|
|
100
|
+
| `UG-ADMIN-001` | Legacy Admin GraphQL `priceRule` usage | [2026-10 release notes](https://shopify.dev/release-notes/2026-10) |
|
|
101
|
+
| `UG-SCRIPT-001` | Script Tag create/update usage | [Script Tag deprecation](https://shopify.dev/changelog/online-store-script-tags-deprecation) |
|
|
102
|
+
|
|
103
|
+
Supported inventory surfaces include Admin REST, Admin GraphQL, Checkout UI extensions, Customer Account UI extensions, POS UI extensions, Functions, Shopify app TOML, and recognized Shopify client configuration.
|
|
42
104
|
|
|
43
105
|
## CLI
|
|
44
106
|
|
|
45
107
|
```text
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
108
|
+
shopify-upgrade-guard scan [--target YYYY-MM] [--format human|json|sarif] [--fail-on ...] [--fail-on-new]
|
|
109
|
+
shopify-upgrade-guard baseline create|check [--path DIR]
|
|
110
|
+
shopify-upgrade-guard rules
|
|
111
|
+
shopify-upgrade-guard versions
|
|
112
|
+
shopify-upgrade-guard explain UG-CHECKOUT-001
|
|
51
113
|
```
|
|
52
114
|
|
|
53
|
-
|
|
115
|
+
Examples:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
# Human-readable scan
|
|
119
|
+
npx shopify-upgrade-guard scan --target 2026-10
|
|
120
|
+
|
|
121
|
+
# CI-friendly JSON
|
|
122
|
+
npx shopify-upgrade-guard scan --target 2026-10 --format json
|
|
123
|
+
|
|
124
|
+
# SARIF for code-scanning workflows
|
|
125
|
+
npx shopify-upgrade-guard scan --target 2026-10 --format sarif
|
|
126
|
+
|
|
127
|
+
# Fail only when the PR introduces new warning-or-higher findings
|
|
128
|
+
npx shopify-upgrade-guard scan --target 2026-10 --fail-on warning --fail-on-new
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Exit codes: `0` means the scan completed and policy passed; `1` means active findings exceeded the configured policy; `2` means the scanner could not complete.
|
|
132
|
+
|
|
133
|
+
## Adopt it gradually with a baseline
|
|
134
|
+
|
|
135
|
+
Existing migration debt should not prevent a team from adopting Upgrade Guard.
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
npx shopify-upgrade-guard baseline create
|
|
139
|
+
npx shopify-upgrade-guard baseline check
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Baselined findings remain reviewable while new fingerprints stay actionable.
|
|
54
143
|
|
|
55
144
|
## Configuration
|
|
56
145
|
|
|
@@ -64,30 +153,54 @@ Commit `.upgradeguard.json` to set repository defaults:
|
|
|
64
153
|
}
|
|
65
154
|
```
|
|
66
155
|
|
|
67
|
-
`exclude` accepts repository-relative glob patterns. Absolute paths and traversal are rejected.
|
|
156
|
+
`exclude` accepts repository-relative glob patterns. Absolute paths and traversal are rejected.
|
|
68
157
|
|
|
69
|
-
|
|
158
|
+
See [examples](examples/) for copy-paste CI and configuration examples.
|
|
70
159
|
|
|
71
|
-
|
|
160
|
+
## Security and privacy
|
|
72
161
|
|
|
73
|
-
|
|
74
|
-
| --- | --- | --- |
|
|
75
|
-
| UG-CHECKOUT-001 | `useBuyerJourneyIntercept` / `buyerJourney.intercept` in JS/TS | [Shopify changelog](https://shopify.dev/changelog/deprecating-the-usebuyerjourneyintercept-api-on-checkout-ui-extensions) |
|
|
76
|
-
| UG-CHECKOUT-002 | `block_progress` in an extension TOML | [Shopify changelog](https://shopify.dev/changelog/deprecating-the-usebuyerjourneyintercept-api-on-checkout-ui-extensions) |
|
|
77
|
-
| UG-REST-001 | Versioned REST Admin URL or explicit REST client marker | [REST Admin API](https://shopify.dev/docs/api/admin-rest) |
|
|
78
|
-
| UG-VERSION-001 | Unsupported or target-retiring version in the inventory | [API versioning](https://shopify.dev/docs/api/usage/versioning) |
|
|
79
|
-
| UG-VERSION-002 | Supported version older than latest stable | [API versioning](https://shopify.dev/docs/api/usage/versioning) |
|
|
80
|
-
| UG-CUSTOMER-001 | Customer Account checkout fields/types removed in 2026-10 | [Shopify changelog](https://shopify.dev/changelog/customer-account-api-last-incomplete-checkout-and-checkout-types-removed) |
|
|
81
|
-
| UG-POS-001 | Removed POS `session.currentSession.staffMemberId` | [Shopify changelog](https://shopify.dev/changelog/removed-session-currentsession-staffmemberid-from-pos-ui-extensions-2026-10) |
|
|
82
|
-
| UG-ADMIN-001 | Legacy Admin GraphQL `priceRule` usage | [2026-10 release notes](https://shopify.dev/release-notes/2026-10) |
|
|
83
|
-
| UG-SCRIPT-001 | Script Tag create/update usage | [Script Tag deprecation](https://shopify.dev/changelog/online-store-script-tags-deprecation) |
|
|
84
|
-
|
|
85
|
-
Supported inventory surfaces currently include Admin REST, Admin GraphQL, Checkout UI extensions, Customer Account UI extensions, POS UI extensions, Functions, Shopify app TOML, and recognized Shopify client configuration.
|
|
162
|
+
Upgrade Guard treats scanned repositories as untrusted input.
|
|
86
163
|
|
|
87
|
-
|
|
164
|
+
Ordinary scans:
|
|
165
|
+
|
|
166
|
+
- do not execute repository code
|
|
167
|
+
- do not contact Shopify
|
|
168
|
+
- do not require Shopify credentials
|
|
169
|
+
- do not upload source
|
|
170
|
+
- skip generated/vendor directories by default
|
|
171
|
+
- use bounded file discovery
|
|
172
|
+
|
|
173
|
+
See [SECURITY.md](SECURITY.md) and the [threat model](docs/threat-model.md).
|
|
174
|
+
|
|
175
|
+
## Related Shopify developer tools
|
|
176
|
+
|
|
177
|
+
Building or maintaining Shopify apps?
|
|
178
|
+
|
|
179
|
+
- **[ChangeGuard](https://github.com/efegokdemir/shopify-app-changeguard)** — Review meaningful `shopify.app*.toml` configuration changes before they reach production.
|
|
180
|
+
- **[Shopify Scope Guard](https://github.com/efegokdemir/shopify-scope-guard)** — Audit whether declared Shopify permissions are supported by offline code evidence.
|
|
181
|
+
|
|
182
|
+
Both tools are offline, open-source, and require no Shopify credentials.
|
|
183
|
+
|
|
184
|
+
## Contributing
|
|
185
|
+
|
|
186
|
+
Contributions are welcome, especially new evidence-backed Shopify migration rules and scanner hardening.
|
|
187
|
+
|
|
188
|
+
A Shopify-specific rule should include:
|
|
189
|
+
|
|
190
|
+
1. an official `shopify.dev` evidence URL
|
|
191
|
+
2. the affected version or status
|
|
192
|
+
3. bounded deterministic detection
|
|
193
|
+
4. migration guidance
|
|
194
|
+
5. positive and false-positive tests
|
|
195
|
+
|
|
196
|
+
Start with [the contribution guide](CONTRIBUTING.md) or browse the [open issues](https://github.com/efegokdemir/shopify-upgrade-guard/issues).
|
|
197
|
+
|
|
198
|
+
## Roadmap
|
|
199
|
+
|
|
200
|
+
Current priorities include additional verified Shopify breaking-change coverage, better PR base-tree classification, high-confidence Functions/Customer Account/POS/GraphQL rules, and maintainable GraphQL deprecation evidence.
|
|
88
201
|
|
|
89
|
-
|
|
202
|
+
See [ROADMAP.md](ROADMAP.md).
|
|
90
203
|
|
|
91
|
-
##
|
|
204
|
+
## License
|
|
92
205
|
|
|
93
|
-
|
|
206
|
+
MIT — see [LICENSE](LICENSE).
|
package/data/versions.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"verifiedAt": "2026-
|
|
2
|
+
"verifiedAt": "2026-10-01",
|
|
3
3
|
"source": "https://shopify.dev/docs/api/usage/versioning",
|
|
4
4
|
"versions": [
|
|
5
5
|
{ "version": "2025-01", "releaseDate": "2025-01-01", "accessibleUntil": "2026-01-16T15:00:00Z", "status": "unsupported" },
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
{ "version": "2025-10", "releaseDate": "2025-10-01", "accessibleUntil": "2026-10-16T15:00:00Z", "status": "stable" },
|
|
9
9
|
{ "version": "2026-01", "releaseDate": "2026-01-01", "accessibleUntil": "2027-01-16T15:00:00Z", "status": "stable" },
|
|
10
10
|
{ "version": "2026-04", "releaseDate": "2026-04-01", "accessibleUntil": "2027-04-16T15:00:00Z", "status": "stable" },
|
|
11
|
-
{ "version": "2026-07", "releaseDate": "2026-07-01", "accessibleUntil": "2027-07-16T15:00:00Z", "status": "
|
|
12
|
-
{ "version": "2026-10", "releaseDate": "2026-10-01", "accessibleUntil": "2027-10-16T15:00:00Z", "status": "
|
|
11
|
+
{ "version": "2026-07", "releaseDate": "2026-07-01", "accessibleUntil": "2027-07-16T15:00:00Z", "status": "stable" },
|
|
12
|
+
{ "version": "2026-10", "releaseDate": "2026-10-01", "accessibleUntil": "2027-10-16T15:00:00Z", "status": "latest-stable" }
|
|
13
13
|
]
|
|
14
14
|
}
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createRequire as e}from"module";var t={24:t=>{t.exports=e(import.meta.url)("node:fs")},708:t=>{t.exports=e(import.meta.url)("node:process")},242:(e,t,n)=>{n.a(e,(async(e,t)=>{try{var r=n(24);var i=n(708);var s=n(787);var o=n(529);const input=e=>i.env[`INPUT_${e.toUpperCase().replaceAll("-","_")}`]||"";const setOutput=(e,t)=>{const n=`ghadelimiter_${Date.now()}`;r.appendFileSync(i.env.GITHUB_OUTPUT||"/dev/null",`${e}<<${n}\n${t}\n${n}\n`)};const e=input("target")||undefined;const a=input("fail-on")||"never";const c=input("fail-on-new")==="true";const l=input("path")||i.env.GITHUB_WORKSPACE||i.cwd();try{const t=await(0,s.S)(l,{target:e,failOn:a,baseRef:i.env.GITHUB_BASE_REF||undefined});const n=JSON.stringify(t);const u=t.findings.filter((e=>!e.baselined));setOutput("outcome",(0,o.W9)(t,a,c)?"failure":"success");setOutput("finding-count",String(u.length));setOutput("new-finding-count",String(u.filter((e=>e.lifecycle==="NEW")).length));setOutput("error-count",String(u.filter((e=>e.severity==="error")).length));setOutput("current-versions",JSON.stringify(t.inventory));setOutput("result-json",n);if(i.env.GITHUB_STEP_SUMMARY)r.appendFileSync(i.env.GITHUB_STEP_SUMMARY,`## Shopify Upgrade Guard\n\n${(0,o.L2)(t)}\n`);for(const e of u)if(e.file&&e.line)console.error(`::warning file=${e.file},line=${e.line},col=${e.column}::${e.title}`);if((0,o.W9)(t,a,c)){console.error(`::error::Upgrade findings exceeded --fail-on ${a}${c?" for NEW findings":""}`);i.exitCode=1}}catch(e){console.error(`::error::${e.message}`);i.exitCode=2}t()}catch(e){t(e)}}),1)},529:(e,t,n)=>{n.d(t,{L2:()=>formatHuman,W9:()=>shouldFail});const r={error:0,warning:1,info:2};function activeFindings(e){return e.findings.filter((e=>!e.baselined))}function formatHuman(e){const t=activeFindings(e);const n=["Shopify Upgrade Guard","",`Target version: ${e.targetVersion}`,`Files scanned: ${e.filesScanned}`,""];n.push("Current repository");n.push(...sectionLines(e.currentProblems.filter((e=>!e.baselined)),"Current debt"));n.push("","Upgrade blockers");n.push(...sectionLines(e.upgradeBlockers.filter((e=>!e.baselined)),"Target migration"));const r=t.filter((e=>e.changeStatus==="introduced"));const i=t.filter((e=>e.changeStatus==="pre-existing"));if(r.length||i.length)n.push("",`Introduced by this PR: ${r.length}`,`Pre-existing: ${i.length}`);if(!t.length)n.push("No upgrade risks found.","","Result: scan passed");else n.push("",`${t.length} active risk${t.length===1?"":"s"} found`,`Baselined: ${e.baselinedCount}`,"","Result: migration recommended");return n.join("\n")}function sectionLines(e,t){if(!e.length)return[`No ${t.toLowerCase()}.`];const n=[];for(const t of[...e].sort(((e,t)=>r[e.severity]-r[t.severity])))n.push(`${t.severity.toUpperCase()} ${t.ruleId}`,t.title,` ${t.file}:${t.line}`,` ${t.reason??t.currentState}`,` Migration: ${t.migration}`,` Source: ${t.source}`,"");return n}function shouldFail(e,t="never",n=false){if(t==="never")return false;return activeFindings(e).filter((e=>!n||e.lifecycle==="NEW")).some((e=>t==="info"||t==="warning"&&e.severity!=="info"||t==="error"&&e.severity==="error"))}function toSarif(e){const t=new Map;for(const n of activeFindings(e))if(!t.has(n.ruleId))t.set(n.ruleId,{id:n.ruleId,shortDescription:{text:n.title},helpUri:n.source,properties:{severity:n.severity,confidence:n.confidence}});const n=[...t.values()];return{version:"2.1.0",$schema:"https://json.schemastore.org/sarif-2.1.0.json",runs:[{tool:{driver:{name:"Shopify Upgrade Guard",informationUri:"https://github.com/efegokdemir/shopify-upgrade-guard",rules:n}},results:activeFindings(e).map((e=>({ruleId:e.ruleId,ruleIndex:n.findIndex((t=>t.id===e.ruleId)),level:e.severity==="error"?"error":e.severity==="warning"?"warning":"note",message:{text:`${e.title}. ${e.reason??e.migration}`},partialFingerprints:{primaryLocationLineHash:e.fingerprint},locations:[{physicalLocation:{artifactLocation:{uri:e.file.replaceAll("\\","/")},region:{startLine:e.line,startColumn:e.column}}}]})))}]}}},787:(t,n,r)=>{r.d(n,{S:()=>scan});const i=e(import.meta.url)("node:fs/promises");const s=e(import.meta.url)("node:path");const o=JSON.parse('{"sP":"https://shopify.dev/docs/api/usage/versioning","e4":[{"version":"2025-01","releaseDate":"2025-01-01","accessibleUntil":"2026-01-16T15:00:00Z","status":"unsupported"},{"version":"2025-04","releaseDate":"2025-04-01","accessibleUntil":"2026-04-16T15:00:00Z","status":"unsupported"},{"version":"2025-07","releaseDate":"2025-07-01","accessibleUntil":"2026-07-16T15:00:00Z","status":"unsupported"},{"version":"2025-10","releaseDate":"2025-10-01","accessibleUntil":"2026-10-16T15:00:00Z","status":"stable"},{"version":"2026-01","releaseDate":"2026-01-01","accessibleUntil":"2027-01-16T15:00:00Z","status":"stable"},{"version":"2026-04","releaseDate":"2026-04-01","accessibleUntil":"2027-04-16T15:00:00Z","status":"stable"},{"version":"2026-07","releaseDate":"2026-07-01","accessibleUntil":"2027-07-16T15:00:00Z","status":"latest-stable"},{"version":"2026-10","releaseDate":"2026-10-01","accessibleUntil":"2027-10-16T15:00:00Z","status":"release-candidate"}]}');const a=o.sP;const c=Object.freeze(o.e4);const l=c.find((e=>e.status==="latest-stable"));const u=/^(\d{4})-(01|04|07|10)$/;function parseVersion(e){if(typeof e!=="string")return null;const t=e.match(u);return t?`${t[1]}-${t[2]}`:null}function compareVersions(e,t){const n=parseVersion(e);const r=parseVersion(t);if(!n||!r)throw new Error(`Cannot compare invalid Shopify versions: ${e}, ${t}`);return n===r?0:n<r?-1:1}function isBefore(e,t){return compareVersions(e,t)<0}function isAtOrAfter(e,t){return compareVersions(e,t)>=0}function versionRecord(e){const t=parseVersion(e);return t?c.find((e=>e.version===t))??null:null}function statusFor(e){const t=parseVersion(e);if(!t)return"unknown";return versionRecord(t)?.status??"unknown/future"}function isKnownVersion(e){return Boolean(versionRecord(e))}function targetRecord(e){const t=parseVersion(e);if(!t)throw new Error(`Invalid target version: ${e}. Expected a quarterly YYYY-MM version (01, 04, 07, or 10).`);return versionRecord(t)??{version:t,status:"unknown/future",accessibleUntil:null}}function becomesUnsupportedBefore(e,t){const n=versionRecord(e);const r=targetRecord(t);return Boolean(n?.accessibleUntil&&n.accessibleUntil.slice(0,7)<=r.version)}function versionSummary(){return c}const d=new Set([".git","node_modules","dist","build","coverage","vendor",".next",".cache"]);const p=new Set([".js",".jsx",".ts",".tsx",".graphql",".gql",".toml",".json"]);async function discover(e,t={}){const n=[];const r=[];const o=t.exclude??[];async function walk(t){let a;try{a=await(0,i.readdir)(t,{withFileTypes:true})}catch(e){if(e.code==="ENOENT")return;throw e}for(const c of a){if(c.name.startsWith(".")&&c.name!==".upgradeguard.json")continue;const a=(0,s.join)(t,c.name);const l=(0,s.relative)(e,a);if(c.isDirectory()){if(!d.has(c.name)&&!isExcluded(l,o))await walk(a);continue}if(c.isSymbolicLink())continue;const u=c.name.endsWith(".toml")?".toml":(0,s.extname)(c.name);if(!p.has(u)||c.name.endsWith(".min.js")||isExcluded(l,o))continue;const f=await(0,i.lstat)(a);if(f.size>1024*1024)continue;const h=await(0,i.readFile)(a,"utf8");const g={path:a,relativePath:l,text:h};n.push(g);r.push(...inventoryFor(g))}}await walk(e);n.sort(((e,t)=>e.relativePath.localeCompare(t.relativePath)));r.sort(compareInventory);return{files:n,inventory:r}}function inventoryFor(e){const t=[];const n=e.relativePath.endsWith(".toml");const r=/(?:^|\/)shopify\.(?:app|extension)\.toml$/.test(e.relativePath);const i=r?matchVersion(e.text,/(?:^|\n)\s*api_version\s*=\s*["'](\d{4}-(?:0[147]|10))["']/g):[];for(const n of i)t.push({surface:surfaceForToml(e.relativePath,e.text),version:n.version,file:e.relativePath,line:n.line,source:"api_version"});if(!n){for(const n of matchVersion(e.text,/\/admin\/api\/(\d{4}-(?:0[147]|10))\/(?!graphql(?:\.json)?\b)/g))t.push({surface:"admin_rest_api",version:n.version,file:e.relativePath,line:n.line,source:"admin_api_url"});for(const n of matchVersion(e.text,/\/admin\/api\/(\d{4}-(?:0[147]|10))\/graphql(?:\.json)?\b/g))t.push({surface:"admin_graphql_api",version:n.version,file:e.relativePath,line:n.line,source:"admin_graphql_url"});if(/shopify/i.test(e.text))for(const n of matchVersion(e.text,/\b(?:apiVersion|api_version)\s*[:=]\s*["'](\d{4}-(?:0[147]|10))["']/g))t.push({surface:"shopify_client",version:n.version,file:e.relativePath,line:n.line,source:"client_configuration"})}return[...new Map(t.map((e=>[`${e.surface}|${e.version}|${e.file}`,e]))).values()]}function matchVersion(e,t){return[...e.matchAll(t)].map((t=>{const n=e.slice(0,t.index+t[0].indexOf(t[1]));return{version:parseVersion(t[1]),line:n.split("\n").length}}))}function surfaceForToml(e,t){const n=e.toLowerCase();if(n.includes("checkout")||/\bblock_progress\b/.test(t))return"checkout_ui_extension";if(n.includes("customer"))return"customer_account_ui_extension";if(n.includes("pos"))return"pos_ui_extension";if(n.includes("function"))return"shopify_function";return n.endsWith("shopify.app.toml")?"shopify_app":"extension"}function compareInventory(e,t){return`${e.file}:${e.line}:${e.surface}`.localeCompare(`${t.file}:${t.line}:${t.surface}`)}function isExcluded(e,t){return t.some((t=>globToRegExp(t).test(e)))}function validateExcludePattern(e){globToRegExp(e);return e}function globToRegExp(e){if(typeof e!=="string"||e.length>200||e.startsWith("/")||e.includes(".."))throw new Error(`Invalid exclude pattern: ${e}`);const t=e.replace(/[.+^${}()|[\]\\]/g,"\\$&").replace(/\*\*/g,"§").replace(/\*/g,"[^/]*").replace(/§/g,".*").replace(/\?/g,"[^/]");return new RegExp(`^${t}$`)}const f="2026-07";const h="2026-10";const g=/\.(?:[jt]sx?|graphql|gql)$/;const m=[{id:"UG-CHECKOUT-001",surface:"checkout_ui_extension",severity:"warning",title:"Buyer journey intercept is deprecated",deprecatedIn:f,confidence:"high",documentationUrl:"https://shopify.dev/changelog/deprecating-the-usebuyerjourneyintercept-api-on-checkout-ui-extensions",migrationUrl:"https://shopify.dev/changelog/deprecating-the-usebuyerjourneyintercept-api-on-checkout-ui-extensions",description:"useBuyerJourneyIntercept and buyerJourney.intercept are deprecated from Checkout UI extension version 2026-07. Migrate validation to a cart and checkout validation Function.",detect(e){if(!g.test(e.relativePath))return[];return matches(maskCommentsAndStrings(e.text),/\b(?:useBuyerJourneyIntercept|buyerJourney\.intercept)\b/g,"Replace client-side blocking with a cart and checkout validation Function.")},evaluate(e,t){return versionAware(e,this,t)}},{id:"UG-CHECKOUT-002",surface:"checkout_ui_extension",severity:"warning",title:"Checkout block_progress capability is deprecated",deprecatedIn:f,confidence:"high",documentationUrl:"https://shopify.dev/changelog/deprecating-the-usebuyerjourneyintercept-api-on-checkout-ui-extensions",migrationUrl:"https://shopify.dev/changelog/deprecating-the-usebuyerjourneyintercept-api-on-checkout-ui-extensions",description:"The block_progress capability is deprecated from Checkout UI extension version 2026-07 and supports the deprecated buyer journey intercept API.",detect(e){if(!e.relativePath.endsWith("shopify.extension.toml"))return[];return matches(maskTomlComments(e.text),/^\s*block_progress\s*=\s*(?:true|false)\s*$/gm,"Move validation logic to a cart and checkout validation Function.")},evaluate(e,t){return versionAware(e,this,t)}},{id:"UG-CUSTOMER-001",surface:"customer_account_api",severity:"error",title:"Customer Account checkout types are removed",removedIn:h,confidence:"high",documentationUrl:"https://shopify.dev/changelog/customer-account-api-last-incomplete-checkout-and-checkout-types-removed",migrationUrl:"https://shopify.dev/changelog/customer-account-api-last-incomplete-checkout-and-checkout-types-removed",description:"Customer.lastIncompleteCheckout and the Checkout type subtree are removed in API version 2026-10 with no replacement. Use Storefront cart flows or Customer.orders as appropriate.",detect(e){if(!g.test(e.relativePath))return[];return matches(maskComments(e.text),/\blastIncompleteCheckout\b|\bCheckout\s*\{/g,"Remove the Customer Account checkout field/type usage and migrate to Storefront cart flows or Customer.orders.")},evaluate(e,t){return removalAware(e,this,t)}},{id:"UG-POS-001",surface:"pos_ui_extension",severity:"error",title:"POS currentSession.staffMemberId is removed",removedIn:h,confidence:"high",documentationUrl:"https://shopify.dev/changelog/removed-session-currentsession-staffmemberid-from-pos-ui-extensions-2026-10",migrationUrl:"https://shopify.dev/changelog/removed-session-currentsession-staffmemberid-from-pos-ui-extensions-2026-10",description:"session.currentSession.staffMemberId is removed from POS UI Extensions in API version 2026-10. Use session.staffMember.value?.id or subscribe to staffMember.",detect(e){if(!/\.(?:[jt]sx?)$/.test(e.relativePath))return[];return matches(maskComments(e.text),/\bsession\.currentSession\.staffMemberId\b/g,"Use session.staffMember.value?.id or subscribe to the staffMember signal.")},evaluate(e,t){return removalAware(e,this,t)}},{id:"UG-ADMIN-001",surface:"admin_graphql_api",severity:"warning",title:"Legacy GraphQL priceRule field is removed",removedIn:h,confidence:"medium",documentationUrl:"https://shopify.dev/release-notes/2026-10",migrationUrl:"https://shopify.dev/release-notes/2026-10",description:"Legacy priceRule fields and types are removed from the Admin GraphQL API in 2026-10. Use discountTitle or discountCode where applicable.",detect(e){if(!g.test(e.relativePath))return[];return matches(maskComments(e.text),/\bpriceRule\b/g,"Replace priceRule usage with the supported discountTitle or discountCode fields.")},evaluate(e,t){return removalAware(e,this,t)}},{id:"UG-SCRIPT-001",surface:"online_store_script_tags",severity:"error",title:"Script Tag creation and updates are deprecated",deprecatedIn:h,confidence:"high",documentationUrl:"https://shopify.dev/changelog/online-store-script-tags-deprecation",migrationUrl:"https://shopify.dev/docs/apps/build/online-store/script-tag-deprecation",description:"ScriptTag create/update operations error from 2026-10 and storefront injection stops in 2027-03. Migrate to a theme app extension/app embed block or web pixel.",detect(e){if(!g.test(e.relativePath))return[];return matches(maskComments(e.text),/\b(?:scriptTagCreate|scriptTagUpdate|script_tags)\b/g,"Migrate Script Tag injection to a theme app extension/app embed block or web pixel.")},evaluate(e,t){return removalAware(e,this,t)}},{id:"UG-REST-001",surface:"admin_rest_api",severity:"warning",title:"REST Admin API usage is legacy",confidence:"high",documentationUrl:"https://shopify.dev/docs/api/admin-rest",migrationUrl:"https://shopify.dev/docs/api/admin-graphql",description:"The REST Admin API is legacy. New public apps must use the GraphQL Admin API; existing integrations should plan migration where applicable.",detect(e){if(!/\.(?:[jt]sx?|graphql|gql)$/.test(e.relativePath))return[];const t=[/\/admin\/api\/(?:\d{4}-(?:0[147]|10)|latest|unstable)\/(?!graphql(?:\.json)?\b)/g];if(/shopify/i.test(e.text))t.push(/\b(?:restResources|Rest\s*Admin|adminRest)\b/g);const n=t.flatMap((t=>matches(e.text,t,"Prefer the GraphQL Admin API for new work and plan migration for this REST integration.")));return n.length?[n[0]]:[]},evaluate(e){return{...e,classification:"current",reason:this.description}}},{id:"UG-VERSION-001",surface:"versioned_api",severity:"error",title:"Shopify API version is unsupported",confidence:"high",documentationUrl:"https://shopify.dev/docs/api/usage/versioning",description:"Shopify may fall forward when a request targets an inaccessible version. Pin to a supported stable version and test the migration.",detect(){return[]},evaluate(e,t){const n=statusFor(e.version);const r=becomesUnsupportedBefore(e.version,t.targetVersion);if(n!=="unsupported"&&!r)return null;return{...e,classification:n==="unsupported"?"current":"target",reason:n==="unsupported"?this.description:`This version becomes inaccessible by target ${t.targetVersion}. ${this.description}`}}},{id:"UG-VERSION-002",surface:"versioned_api",severity:"info",title:"Shopify API version is not the latest stable",confidence:"high",documentationUrl:"https://shopify.dev/docs/api/usage/versioning",description:`The latest bundled stable version is ${l.version}. Older supported versions remain usable, but quarterly upgrades reduce migration risk.`,detect(){return[]},evaluate(e){if(statusFor(e.version)==="stable"&&e.version!==l.version)return{...e,classification:"current",reason:this.description};return null}}];function ruleById(e){return m.find((t=>t.id===e))}function versionAware(e,t,n){const r=n.inventory.find((e=>e.surface==="checkout_ui_extension"))?.version;const i=r&&isAtOrAfter(r,t.deprecatedIn);const s=isAtOrAfter(n.targetVersion,t.deprecatedIn);if(!i&&!s)return null;return{...e,classification:i?"current":"target",reason:i?t.description:`This API becomes deprecated before target ${n.targetVersion}. ${t.description}`}}function removalAware(e,t,n){if(!isAtOrAfter(n.targetVersion,t.removedIn??t.deprecatedIn))return null;return{...e,classification:"target",reason:`This usage is affected by target ${n.targetVersion}. ${t.description}`}}function matches(e,t,n){return[...e.matchAll(t)].map((t=>{const r=e.slice(0,t.index);return{file:null,line:r.split("\n").length,column:t.index-r.lastIndexOf("\n"),snippet:t[0],guidance:n}}))}function attachFile(e,t){return e.map((e=>({...e,file:t.relativePath})))}function maskCommentsAndStrings(e){return e.replace(/\/\/[^\n]*|\/\*[\s\S]*?\*\/|(['"`])(?:\\.|(?!\1)[^\\])*\1/g,(e=>e.replace(/[^\n]/g," ")))}function maskComments(e){return e.replace(/\/\/[^\n]*|\/\*[\s\S]*?\*\//g,(e=>e.replace(/[^\n]/g," ")))}function maskTomlComments(e){return e.replace(/#[^\n]*/g,(e=>e.replace(/[^\n]/g," ")))}const v=e(import.meta.url)("node:crypto");const y=".upgradeguard-baseline.json";function fingerprint(e){return(0,v.createHash)("sha256").update(`${e.ruleId}|${e.surface}|${e.file}|${e.snippet.replace(/\s+/g," ").trim()}`).digest("hex").slice(0,16)}async function loadBaseline(e,t=y){try{const n=JSON.parse(await(0,i.readFile)((0,s.join)(e,t),"utf8"));if(!Array.isArray(n.findings)||n.findings.some((e=>typeof e.fingerprint!=="string")))throw new Error("findings must be an array of fingerprinted findings");return{fingerprints:new Set(n.findings.map((e=>e.fingerprint))),entries:n.findings,file:t}}catch(e){if(e.code==="ENOENT")return{fingerprints:new Set,entries:[],file:t};throw new Error(`Invalid ${t}: ${e.message}`)}}async function writeBaseline(e,t,n=y){const r={schemaVersion:1,generatedAt:(new Date).toISOString(),findings:t.map((e=>({fingerprint:fingerprint(e),ruleId:e.ruleId,surface:e.surface,file:e.file}))).sort(((e,t)=>e.fingerprint.localeCompare(t.fingerprint)))};await writeFile(join(e,n),`${JSON.stringify(r,null,2)}\n`,"utf8");return r}function applyBaseline(e,t){return e.map((e=>({...e,fingerprint:fingerprint(e),baselined:t.fingerprints.has(fingerprint(e))})))}const b=e(import.meta.url)("node:child_process");async function changedLines(e,t){if(!t||t.includes("..")||!/^[A-Za-z0-9_./-]+$/.test(t))return null;const n=await runGit(e,["diff","--no-ext-diff","--unified=0",`${t}...HEAD`,"--"]);const r=new Map;let i=null;for(const e of n.split("\n")){if(e.startsWith("+++ b/")){i=e.slice(6);continue}const t=e.match(/^@@ -\d+(?:,\d+)? \+(\d+)(?:,(\d+))? @@/);if(t&&i){const e=Number(t[1]);const n=Number(t[2]??1);r.set(i,[...r.get(i)??[],{start:e,end:e+Math.max(n,1)-1}])}}return r}async function runGit(e,t){return new Promise(((n,r)=>{const i=(0,b.spawn)("git",t,{cwd:e,stdio:["ignore","pipe","pipe"]});let s="";let o="";i.stdout.on("data",(e=>{s+=e}));i.stderr.on("data",(e=>{o+=e}));i.on("error",r);i.on("close",(e=>e===0?n(s):r(new Error(`git ${t[0]} failed: ${o.trim()}`))))}))}async function loadConfig(e){try{const t=JSON.parse(await(0,i.readFile)((0,s.join)(e,".upgradeguard.json"),"utf8"));validateConfig(t);return t}catch(e){if(e.code==="ENOENT")return{};throw new Error(`Invalid .upgradeguard.json: ${e.message}`)}}function validateConfig(e){if(e.targetVersion!==undefined&&!parseVersion(e.targetVersion))throw new Error("targetVersion must be a quarterly YYYY-MM version.");if(e.failOn!==undefined&&!["never","error","warning","info"].includes(e.failOn))throw new Error("failOn must be never, error, warning, or info.");if(e.exclude!==undefined&&(!Array.isArray(e.exclude)||e.exclude.some((e=>validateExcludePattern(e)===undefined))))throw new Error("exclude must be an array of repository-relative glob patterns.");if(e.baseline!==undefined&&(typeof e.baseline!=="string"||e.baseline.startsWith("/")||e.baseline.includes("..")))throw new Error("baseline must be a repository-relative file path.")}async function scan(e,t={}){const n=await loadConfig(e);const r=t.target??n.targetVersion??l.version;const i=t.failOn??n.failOn??"never";targetRecord(r);const s=t.exclude??n.exclude??[];s.forEach(validateExcludePattern);const{files:o,inventory:a}=await discover(e,{exclude:s});const c={root:e,targetVersion:r,inventory:a};const u=[];for(const e of o)for(const t of m){const n=t.detect(e);const i=n.length?attachFile(n,e):inventoryMatches(t,a,e);for(const e of i){const n=t.evaluate(e,c);if(n)u.push(toFinding(t,n,r))}}const d=t.noBaseline?{fingerprints:new Set,entries:[],file:null}:await loadBaseline(e,t.baseline??n.baseline);let p=applyBaseline(dedupe(u),d);const f=t.baseRef??process.env.GITHUB_BASE_REF;if(f){try{const t=await changedLines(e,f);p=p.map((e=>({...e,changeStatus:isChanged(e,t)?"introduced":"pre-existing",lifecycle:isChanged(e,t)?"NEW":"EXISTING"})))}catch{p=p.map((e=>({...e,lifecycle:e.baselined?"EXISTING":"UNCHANGED"})))}}else p=p.map((e=>({...e,lifecycle:e.baselined?"EXISTING":"UNCHANGED"})));const h=p.filter((e=>!e.baselined));const g=h.filter((e=>e.classification==="current"));const v=h.filter((e=>e.classification==="target"));return{schemaVersion:1,tool:"shopify-upgrade-guard",targetVersion:r,failOn:i,latestStable:l.version,filesScanned:o.length,inventory:a,findings:p,currentProblems:g,upgradeBlockers:v,baselinedCount:p.filter((e=>e.baselined)).length,resolvedBaselineCount:[...d.fingerprints].filter((e=>!p.some((t=>t.fingerprint===e)))).length,baselineFile:d.file}}function inventoryMatches(e,t,n){if(!e.id.startsWith("UG-VERSION"))return[];return t.filter((e=>e.file===n.relativePath)).map((e=>({file:e.file,line:e.line,column:1,snippet:`${e.source}: ${e.version}`,version:e.version,guidance:"Update this Shopify version to a supported stable release and test the migration."})))}function toFinding(e,t,n){return{ruleId:e.id,severity:e.severity,title:e.title,surface:e.surface,file:t.file,line:t.line,column:t.column,snippet:t.snippet,targetVersion:n,currentState:e.description,migration:t.guidance,source:e.documentationUrl,migrationUrl:e.migrationUrl??null,confidence:e.confidence,classification:t.classification,reason:t.reason,deprecatedIn:e.deprecatedIn??null,removedIn:e.removedIn??null}}function dedupe(e){return[...new Map(e.map((e=>[`${e.ruleId}|${e.file}|${e.line}|${e.snippet}`,e]))).values()]}function isChanged(e,t){return Boolean(t?.get(e.file)?.some((t=>e.line>=t.start&&e.line<=t.end)))}}};var n={};function __nccwpck_require__(e){var r=n[e];if(r!==undefined){return r.exports}var i=n[e]={exports:{}};var s=true;try{t[e](i,i.exports,__nccwpck_require__);s=false}finally{if(s)delete n[e]}return i.exports}(()=>{var e=typeof Symbol==="function"?Symbol("webpack queues"):"__webpack_queues__";var t=typeof Symbol==="function"?Symbol("webpack exports"):"__webpack_exports__";var n=typeof Symbol==="function"?Symbol("webpack error"):"__webpack_error__";var resolveQueue=e=>{if(e&&e.d<1){e.d=1;e.forEach((e=>e.r--));e.forEach((e=>e.r--?e.r++:e()))}};var wrapDeps=r=>r.map((r=>{if(r!==null&&typeof r==="object"){if(r[e])return r;if(r.then){var i=[];i.d=0;r.then((e=>{s[t]=e;resolveQueue(i)}),(e=>{s[n]=e;resolveQueue(i)}));var s={};s[e]=e=>e(i);return s}}var o={};o[e]=e=>{};o[t]=r;return o}));__nccwpck_require__.a=(r,i,s)=>{var o;s&&((o=[]).d=-1);var a=new Set;var c=r.exports;var l;var u;var d;var p=new Promise(((e,t)=>{d=t;u=e}));p[t]=c;p[e]=e=>(o&&e(o),a.forEach(e),p["catch"]((e=>{})));r.exports=p;i((r=>{l=wrapDeps(r);var i;var getResult=()=>l.map((e=>{if(e[n])throw e[n];return e[t]}));var s=new Promise((t=>{i=()=>t(getResult);i.r=0;var fnQueue=e=>e!==o&&!a.has(e)&&(a.add(e),e&&!e.d&&(i.r++,e.push(i)));l.map((t=>t[e](fnQueue)))}));return i.r?s:getResult()}),(e=>(e?d(p[n]=e):u(c),resolveQueue(o))));o&&o.d<0&&(o.d=0)}})();(()=>{__nccwpck_require__.d=(e,t)=>{for(var n in t){if(__nccwpck_require__.o(t,n)&&!__nccwpck_require__.o(e,n)){Object.defineProperty(e,n,{enumerable:true,get:t[n]})}}}})();(()=>{__nccwpck_require__.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t)})();if(typeof __nccwpck_require__!=="undefined")__nccwpck_require__.ab=new URL(".",import.meta.url).pathname.slice(import.meta.url.match(/^file:\/\/\/\w:/)?1:0,-1)+"/";var r=__nccwpck_require__(242);r=await r;
|
|
1
|
+
import{createRequire as e}from"module";var t={24:t=>{t.exports=e(import.meta.url)("node:fs")},708:t=>{t.exports=e(import.meta.url)("node:process")},242:(e,t,n)=>{n.a(e,(async(e,t)=>{try{var r=n(24);var i=n(708);var s=n(787);var o=n(529);const input=e=>i.env[`INPUT_${e.toUpperCase().replaceAll("-","_")}`]||"";const setOutput=(e,t)=>{const n=`ghadelimiter_${Date.now()}`;r.appendFileSync(i.env.GITHUB_OUTPUT||"/dev/null",`${e}<<${n}\n${t}\n${n}\n`)};const e=input("target")||undefined;const a=input("fail-on")||"never";const c=input("fail-on-new")==="true";const l=input("path")||i.env.GITHUB_WORKSPACE||i.cwd();try{const t=await(0,s.S)(l,{target:e,failOn:a,baseRef:i.env.GITHUB_BASE_REF||undefined});const n=JSON.stringify(t);const u=t.findings.filter((e=>!e.baselined));setOutput("outcome",(0,o.W9)(t,a,c)?"failure":"success");setOutput("finding-count",String(u.length));setOutput("new-finding-count",String(u.filter((e=>e.lifecycle==="NEW")).length));setOutput("error-count",String(u.filter((e=>e.severity==="error")).length));setOutput("current-versions",JSON.stringify(t.inventory));setOutput("result-json",n);if(i.env.GITHUB_STEP_SUMMARY)r.appendFileSync(i.env.GITHUB_STEP_SUMMARY,`## Shopify Upgrade Guard\n\n${(0,o.L2)(t)}\n`);for(const e of u)if(e.file&&e.line)console.error(`::warning file=${e.file},line=${e.line},col=${e.column}::${e.title}`);if((0,o.W9)(t,a,c)){console.error(`::error::Upgrade findings exceeded --fail-on ${a}${c?" for NEW findings":""}`);i.exitCode=1}}catch(e){console.error(`::error::${e.message}`);i.exitCode=2}t()}catch(e){t(e)}}),1)},529:(e,t,n)=>{n.d(t,{L2:()=>formatHuman,W9:()=>shouldFail});const r={error:0,warning:1,info:2};function activeFindings(e){return e.findings.filter((e=>!e.baselined))}function formatHuman(e){const t=activeFindings(e);const n=["Shopify Upgrade Guard","",`Target version: ${e.targetVersion}`,`Files scanned: ${e.filesScanned}`,""];n.push("Current repository");n.push(...sectionLines(e.currentProblems.filter((e=>!e.baselined)),"Current debt"));n.push("","Upgrade blockers");n.push(...sectionLines(e.upgradeBlockers.filter((e=>!e.baselined)),"Target migration"));const r=t.filter((e=>e.changeStatus==="introduced"));const i=t.filter((e=>e.changeStatus==="pre-existing"));if(r.length||i.length)n.push("",`Introduced by this PR: ${r.length}`,`Pre-existing: ${i.length}`);if(!t.length)n.push("No upgrade risks found.","","Result: scan passed");else n.push("",`${t.length} active risk${t.length===1?"":"s"} found`,`Baselined: ${e.baselinedCount}`,"","Result: migration recommended");return n.join("\n")}function sectionLines(e,t){if(!e.length)return[`No ${t.toLowerCase()}.`];const n=[];for(const t of[...e].sort(((e,t)=>r[e.severity]-r[t.severity])))n.push(`${t.severity.toUpperCase()} ${t.ruleId}`,t.title,` ${t.file}:${t.line}`,` ${t.reason??t.currentState}`,` Migration: ${t.migration}`,` Source: ${t.source}`,"");return n}function shouldFail(e,t="never",n=false){if(t==="never")return false;return activeFindings(e).filter((e=>!n||e.lifecycle==="NEW")).some((e=>t==="info"||t==="warning"&&e.severity!=="info"||t==="error"&&e.severity==="error"))}function toSarif(e){const t=new Map;for(const n of activeFindings(e))if(!t.has(n.ruleId))t.set(n.ruleId,{id:n.ruleId,shortDescription:{text:n.title},helpUri:n.source,properties:{severity:n.severity,confidence:n.confidence}});const n=[...t.values()];return{version:"2.1.0",$schema:"https://json.schemastore.org/sarif-2.1.0.json",runs:[{tool:{driver:{name:"Shopify Upgrade Guard",informationUri:"https://github.com/efegokdemir/shopify-upgrade-guard",rules:n}},results:activeFindings(e).map((e=>({ruleId:e.ruleId,ruleIndex:n.findIndex((t=>t.id===e.ruleId)),level:e.severity==="error"?"error":e.severity==="warning"?"warning":"note",message:{text:`${e.title}. ${e.reason??e.migration}`},partialFingerprints:{primaryLocationLineHash:e.fingerprint},locations:[{physicalLocation:{artifactLocation:{uri:e.file.replaceAll("\\","/")},region:{startLine:e.line,startColumn:e.column}}}]})))}]}}},787:(t,n,r)=>{r.d(n,{S:()=>scan});const i=e(import.meta.url)("node:fs/promises");const s=e(import.meta.url)("node:path");const o=JSON.parse('{"sP":"https://shopify.dev/docs/api/usage/versioning","e4":[{"version":"2025-01","releaseDate":"2025-01-01","accessibleUntil":"2026-01-16T15:00:00Z","status":"unsupported"},{"version":"2025-04","releaseDate":"2025-04-01","accessibleUntil":"2026-04-16T15:00:00Z","status":"unsupported"},{"version":"2025-07","releaseDate":"2025-07-01","accessibleUntil":"2026-07-16T15:00:00Z","status":"unsupported"},{"version":"2025-10","releaseDate":"2025-10-01","accessibleUntil":"2026-10-16T15:00:00Z","status":"stable"},{"version":"2026-01","releaseDate":"2026-01-01","accessibleUntil":"2027-01-16T15:00:00Z","status":"stable"},{"version":"2026-04","releaseDate":"2026-04-01","accessibleUntil":"2027-04-16T15:00:00Z","status":"stable"},{"version":"2026-07","releaseDate":"2026-07-01","accessibleUntil":"2027-07-16T15:00:00Z","status":"stable"},{"version":"2026-10","releaseDate":"2026-10-01","accessibleUntil":"2027-10-16T15:00:00Z","status":"latest-stable"}]}');const a=o.sP;const c=Object.freeze(o.e4);const l=c.find((e=>e.status==="latest-stable"));const u=/^(\d{4})-(01|04|07|10)$/;function parseVersion(e){if(typeof e!=="string")return null;const t=e.match(u);return t?`${t[1]}-${t[2]}`:null}function compareVersions(e,t){const n=parseVersion(e);const r=parseVersion(t);if(!n||!r)throw new Error(`Cannot compare invalid Shopify versions: ${e}, ${t}`);return n===r?0:n<r?-1:1}function isBefore(e,t){return compareVersions(e,t)<0}function isAtOrAfter(e,t){return compareVersions(e,t)>=0}function versionRecord(e){const t=parseVersion(e);return t?c.find((e=>e.version===t))??null:null}function statusFor(e){const t=parseVersion(e);if(!t)return"unknown";return versionRecord(t)?.status??"unknown/future"}function isKnownVersion(e){return Boolean(versionRecord(e))}function targetRecord(e){const t=parseVersion(e);if(!t)throw new Error(`Invalid target version: ${e}. Expected a quarterly YYYY-MM version (01, 04, 07, or 10).`);return versionRecord(t)??{version:t,status:"unknown/future",accessibleUntil:null}}function becomesUnsupportedBefore(e,t){const n=versionRecord(e);const r=targetRecord(t);return Boolean(n?.accessibleUntil&&n.accessibleUntil.slice(0,7)<=r.version)}function versionSummary(){return c}const d=new Set([".git","node_modules","dist","build","coverage","vendor",".next",".cache"]);const p=new Set([".js",".jsx",".ts",".tsx",".graphql",".gql",".toml",".json"]);async function discover(e,t={}){const n=[];const r=[];const o=t.exclude??[];async function walk(t){let a;try{a=await(0,i.readdir)(t,{withFileTypes:true})}catch(e){if(e.code==="ENOENT")return;throw e}for(const c of a){if(c.name.startsWith(".")&&c.name!==".upgradeguard.json")continue;const a=(0,s.join)(t,c.name);const l=(0,s.relative)(e,a).replaceAll("\\","/");if(c.isDirectory()){if(!d.has(c.name)&&!isExcluded(l,o))await walk(a);continue}if(c.isSymbolicLink())continue;const u=c.name.endsWith(".toml")?".toml":(0,s.extname)(c.name);if(!p.has(u)||c.name.endsWith(".min.js")||isExcluded(l,o))continue;const f=await(0,i.lstat)(a);if(f.size>1024*1024)continue;const h=await(0,i.readFile)(a,"utf8");const g={path:a,relativePath:l,text:h};n.push(g);r.push(...inventoryFor(g))}}await walk(e);n.sort(((e,t)=>e.relativePath.localeCompare(t.relativePath)));r.sort(compareInventory);return{files:n,inventory:r}}function inventoryFor(e){const t=[];const n=e.relativePath.endsWith(".toml");const r=/(?:^|\/)shopify\.(?:app|extension)\.toml$/.test(e.relativePath);const i=r?matchVersion(e.text,/(?:^|\n)\s*api_version\s*=\s*["'](\d{4}-(?:0[147]|10))["']/g):[];for(const n of i)t.push({surface:surfaceForToml(e.relativePath,e.text),version:n.version,file:e.relativePath,line:n.line,source:"api_version"});if(!n){for(const n of matchVersion(e.text,/\/admin\/api\/(\d{4}-(?:0[147]|10))\/(?!graphql(?:\.json)?\b)/g))t.push({surface:"admin_rest_api",version:n.version,file:e.relativePath,line:n.line,source:"admin_api_url"});for(const n of matchVersion(e.text,/\/admin\/api\/(\d{4}-(?:0[147]|10))\/graphql(?:\.json)?\b/g))t.push({surface:"admin_graphql_api",version:n.version,file:e.relativePath,line:n.line,source:"admin_graphql_url"});if(/shopify/i.test(e.text))for(const n of matchVersion(e.text,/\b(?:apiVersion|api_version)\s*[:=]\s*["'](\d{4}-(?:0[147]|10))["']/g))t.push({surface:"shopify_client",version:n.version,file:e.relativePath,line:n.line,source:"client_configuration"})}return[...new Map(t.map((e=>[`${e.surface}|${e.version}|${e.file}`,e]))).values()]}function matchVersion(e,t){return[...e.matchAll(t)].map((t=>{const n=e.slice(0,t.index+t[0].indexOf(t[1]));return{version:parseVersion(t[1]),line:n.split("\n").length}}))}function surfaceForToml(e,t){const n=e.toLowerCase();if(n.includes("checkout")||/\bblock_progress\b/.test(t))return"checkout_ui_extension";if(n.includes("customer"))return"customer_account_ui_extension";if(n.includes("pos"))return"pos_ui_extension";if(n.includes("function"))return"shopify_function";return n.endsWith("shopify.app.toml")?"shopify_app":"extension"}function compareInventory(e,t){return`${e.file}:${e.line}:${e.surface}`.localeCompare(`${t.file}:${t.line}:${t.surface}`)}function isExcluded(e,t){return t.some((t=>globToRegExp(t).test(e)))}function validateExcludePattern(e){globToRegExp(e);return e}function globToRegExp(e){if(typeof e!=="string"||e.length>200||e.startsWith("/")||e.includes(".."))throw new Error(`Invalid exclude pattern: ${e}`);const t=e.replace(/[.+^${}()|[\]\\]/g,"\\$&").replace(/\*\*/g,"§").replace(/\*/g,"[^/]*").replace(/§/g,".*").replace(/\?/g,"[^/]");return new RegExp(`^${t}$`)}const f="2026-07";const h="2026-10";const g=/\.(?:[jt]sx?|graphql|gql)$/;const m=[{id:"UG-CHECKOUT-001",surface:"checkout_ui_extension",severity:"warning",title:"Buyer journey intercept is deprecated",deprecatedIn:f,confidence:"high",documentationUrl:"https://shopify.dev/changelog/deprecating-the-usebuyerjourneyintercept-api-on-checkout-ui-extensions",migrationUrl:"https://shopify.dev/changelog/deprecating-the-usebuyerjourneyintercept-api-on-checkout-ui-extensions",description:"useBuyerJourneyIntercept and buyerJourney.intercept are deprecated from Checkout UI extension version 2026-07. Migrate validation to a cart and checkout validation Function.",detect(e){if(!g.test(e.relativePath))return[];return matches(maskCommentsAndStrings(e.text),/\b(?:useBuyerJourneyIntercept|buyerJourney\.intercept)\b/g,"Replace client-side blocking with a cart and checkout validation Function.")},evaluate(e,t){return versionAware(e,this,t)}},{id:"UG-CHECKOUT-002",surface:"checkout_ui_extension",severity:"warning",title:"Checkout block_progress capability is deprecated",deprecatedIn:f,confidence:"high",documentationUrl:"https://shopify.dev/changelog/deprecating-the-usebuyerjourneyintercept-api-on-checkout-ui-extensions",migrationUrl:"https://shopify.dev/changelog/deprecating-the-usebuyerjourneyintercept-api-on-checkout-ui-extensions",description:"The block_progress capability is deprecated from Checkout UI extension version 2026-07 and supports the deprecated buyer journey intercept API.",detect(e){if(!e.relativePath.endsWith("shopify.extension.toml"))return[];return matches(maskTomlComments(e.text),/^\s*block_progress\s*=\s*(?:true|false)\s*$/gm,"Move validation logic to a cart and checkout validation Function.")},evaluate(e,t){return versionAware(e,this,t)}},{id:"UG-CUSTOMER-001",surface:"customer_account_api",severity:"error",title:"Customer Account checkout types are removed",removedIn:h,confidence:"high",documentationUrl:"https://shopify.dev/changelog/customer-account-api-last-incomplete-checkout-and-checkout-types-removed",migrationUrl:"https://shopify.dev/changelog/customer-account-api-last-incomplete-checkout-and-checkout-types-removed",description:"Customer.lastIncompleteCheckout and the Checkout type subtree are removed in API version 2026-10 with no replacement. Use Storefront cart flows or Customer.orders as appropriate.",detect(e){if(!g.test(e.relativePath))return[];return matches(maskComments(e.text),/\blastIncompleteCheckout\b|\bCheckout\s*\{/g,"Remove the Customer Account checkout field/type usage and migrate to Storefront cart flows or Customer.orders.")},evaluate(e,t){return removalAware(e,this,t)}},{id:"UG-POS-001",surface:"pos_ui_extension",severity:"error",title:"POS currentSession.staffMemberId is removed",removedIn:h,confidence:"high",documentationUrl:"https://shopify.dev/changelog/removed-session-currentsession-staffmemberid-from-pos-ui-extensions-2026-10",migrationUrl:"https://shopify.dev/changelog/removed-session-currentsession-staffmemberid-from-pos-ui-extensions-2026-10",description:"session.currentSession.staffMemberId is removed from POS UI Extensions in API version 2026-10. Use session.staffMember.value?.id or subscribe to staffMember.",detect(e){if(!/\.(?:[jt]sx?)$/.test(e.relativePath))return[];return matches(maskComments(e.text),/\bsession\.currentSession\.staffMemberId\b/g,"Use session.staffMember.value?.id or subscribe to the staffMember signal.")},evaluate(e,t){return removalAware(e,this,t)}},{id:"UG-ADMIN-001",surface:"admin_graphql_api",severity:"warning",title:"Legacy GraphQL priceRule field is removed",removedIn:h,confidence:"medium",documentationUrl:"https://shopify.dev/release-notes/2026-10",migrationUrl:"https://shopify.dev/release-notes/2026-10",description:"Legacy priceRule fields and types are removed from the Admin GraphQL API in 2026-10. Use discountTitle or discountCode where applicable.",detect(e){if(!g.test(e.relativePath))return[];return matches(maskComments(e.text),/\bpriceRule\b/g,"Replace priceRule usage with the supported discountTitle or discountCode fields.")},evaluate(e,t){return removalAware(e,this,t)}},{id:"UG-SCRIPT-001",surface:"online_store_script_tags",severity:"error",title:"Script Tag creation and updates are deprecated",deprecatedIn:h,confidence:"high",documentationUrl:"https://shopify.dev/changelog/online-store-script-tags-deprecation",migrationUrl:"https://shopify.dev/docs/apps/build/online-store/script-tag-deprecation",description:"ScriptTag create/update operations error from 2026-10 and storefront injection stops in 2027-03. Migrate to a theme app extension/app embed block or web pixel.",detect(e){if(!g.test(e.relativePath))return[];return matches(maskComments(e.text),/\b(?:scriptTagCreate|scriptTagUpdate|script_tags)\b/g,"Migrate Script Tag injection to a theme app extension/app embed block or web pixel.")},evaluate(e,t){return removalAware(e,this,t)}},{id:"UG-REST-001",surface:"admin_rest_api",severity:"warning",title:"REST Admin API usage is legacy",confidence:"high",documentationUrl:"https://shopify.dev/docs/api/admin-rest",migrationUrl:"https://shopify.dev/docs/api/admin-graphql",description:"The REST Admin API is legacy. New public apps must use the GraphQL Admin API; existing integrations should plan migration where applicable.",detect(e){if(!/\.(?:[jt]sx?|graphql|gql)$/.test(e.relativePath))return[];const t=[/\/admin\/api\/(?:\d{4}-(?:0[147]|10)|latest|unstable)\/(?!graphql(?:\.json)?\b)/g];if(/shopify/i.test(e.text))t.push(/\b(?:restResources|Rest\s*Admin|adminRest)\b/g);const n=t.flatMap((t=>matches(e.text,t,"Prefer the GraphQL Admin API for new work and plan migration for this REST integration.")));return n.length?[n[0]]:[]},evaluate(e){return{...e,classification:"current",reason:this.description}}},{id:"UG-VERSION-001",surface:"versioned_api",severity:"error",title:"Shopify API version is unsupported",confidence:"high",documentationUrl:"https://shopify.dev/docs/api/usage/versioning",description:"Shopify may fall forward when a request targets an inaccessible version. Pin to a supported stable version and test the migration.",detect(){return[]},evaluate(e,t){const n=statusFor(e.version);const r=becomesUnsupportedBefore(e.version,t.targetVersion);if(n!=="unsupported"&&!r)return null;return{...e,classification:n==="unsupported"?"current":"target",reason:n==="unsupported"?this.description:`This version becomes inaccessible by target ${t.targetVersion}. ${this.description}`}}},{id:"UG-VERSION-002",surface:"versioned_api",severity:"info",title:"Shopify API version is not the latest stable",confidence:"high",documentationUrl:"https://shopify.dev/docs/api/usage/versioning",description:`The latest bundled stable version is ${l.version}. Older supported versions remain usable, but quarterly upgrades reduce migration risk.`,detect(){return[]},evaluate(e){if(statusFor(e.version)==="stable"&&e.version!==l.version)return{...e,classification:"current",reason:this.description};return null}}];function ruleById(e){return m.find((t=>t.id===e))}function versionAware(e,t,n){const r=n.inventory.find((e=>e.surface==="checkout_ui_extension"))?.version;const i=r&&isAtOrAfter(r,t.deprecatedIn);const s=isAtOrAfter(n.targetVersion,t.deprecatedIn);if(!i&&!s)return null;return{...e,classification:i?"current":"target",reason:i?t.description:`This API becomes deprecated before target ${n.targetVersion}. ${t.description}`}}function removalAware(e,t,n){if(!isAtOrAfter(n.targetVersion,t.removedIn??t.deprecatedIn))return null;return{...e,classification:"target",reason:`This usage is affected by target ${n.targetVersion}. ${t.description}`}}function matches(e,t,n){return[...e.matchAll(t)].map((t=>{const r=e.slice(0,t.index);return{file:null,line:r.split("\n").length,column:t.index-r.lastIndexOf("\n"),snippet:t[0],guidance:n}}))}function attachFile(e,t){return e.map((e=>({...e,file:t.relativePath})))}function maskCommentsAndStrings(e){return e.replace(/\/\/[^\n]*|\/\*[\s\S]*?\*\/|(['"`])(?:\\.|(?!\1)[^\\])*\1/g,(e=>e.replace(/[^\n]/g," ")))}function maskComments(e){return e.replace(/\/\/[^\n]*|\/\*[\s\S]*?\*\//g,(e=>e.replace(/[^\n]/g," ")))}function maskTomlComments(e){return e.replace(/#[^\n]*/g,(e=>e.replace(/[^\n]/g," ")))}const v=e(import.meta.url)("node:crypto");const y=".upgradeguard-baseline.json";function fingerprint(e){return(0,v.createHash)("sha256").update(`${e.ruleId}|${e.surface}|${e.file}|${e.snippet.replace(/\s+/g," ").trim()}`).digest("hex").slice(0,16)}async function loadBaseline(e,t=y){try{const n=JSON.parse(await(0,i.readFile)((0,s.join)(e,t),"utf8"));if(!Array.isArray(n.findings)||n.findings.some((e=>typeof e.fingerprint!=="string")))throw new Error("findings must be an array of fingerprinted findings");return{fingerprints:new Set(n.findings.map((e=>e.fingerprint))),entries:n.findings,file:t}}catch(e){if(e.code==="ENOENT")return{fingerprints:new Set,entries:[],file:t};throw new Error(`Invalid ${t}: ${e.message}`)}}async function writeBaseline(e,t,n=y){const r={schemaVersion:1,generatedAt:(new Date).toISOString(),findings:t.map((e=>({fingerprint:fingerprint(e),ruleId:e.ruleId,surface:e.surface,file:e.file}))).sort(((e,t)=>e.fingerprint.localeCompare(t.fingerprint)))};await writeFile(join(e,n),`${JSON.stringify(r,null,2)}\n`,"utf8");return r}function applyBaseline(e,t){return e.map((e=>({...e,fingerprint:fingerprint(e),baselined:t.fingerprints.has(fingerprint(e))})))}const b=e(import.meta.url)("node:child_process");async function changedLines(e,t){if(!t||t.includes("..")||!/^[A-Za-z0-9_./-]+$/.test(t))return null;const n=await runGit(e,["diff","--no-ext-diff","--unified=0",`${t}...HEAD`,"--"]);const r=new Map;let i=null;for(const e of n.split("\n")){if(e.startsWith("+++ b/")){i=e.slice(6);continue}const t=e.match(/^@@ -\d+(?:,\d+)? \+(\d+)(?:,(\d+))? @@/);if(t&&i){const e=Number(t[1]);const n=Number(t[2]??1);r.set(i,[...r.get(i)??[],{start:e,end:e+Math.max(n,1)-1}])}}return r}async function runGit(e,t){return new Promise(((n,r)=>{const i=(0,b.spawn)("git",t,{cwd:e,stdio:["ignore","pipe","pipe"]});let s="";let o="";i.stdout.on("data",(e=>{s+=e}));i.stderr.on("data",(e=>{o+=e}));i.on("error",r);i.on("close",(e=>e===0?n(s):r(new Error(`git ${t[0]} failed: ${o.trim()}`))))}))}async function loadConfig(e){try{const t=JSON.parse(await(0,i.readFile)((0,s.join)(e,".upgradeguard.json"),"utf8"));validateConfig(t);return t}catch(e){if(e.code==="ENOENT")return{};throw new Error(`Invalid .upgradeguard.json: ${e.message}`)}}function validateConfig(e){if(e.targetVersion!==undefined&&!parseVersion(e.targetVersion))throw new Error("targetVersion must be a quarterly YYYY-MM version.");if(e.failOn!==undefined&&!["never","error","warning","info"].includes(e.failOn))throw new Error("failOn must be never, error, warning, or info.");if(e.exclude!==undefined&&(!Array.isArray(e.exclude)||e.exclude.some((e=>validateExcludePattern(e)===undefined))))throw new Error("exclude must be an array of repository-relative glob patterns.");if(e.baseline!==undefined&&(typeof e.baseline!=="string"||e.baseline.startsWith("/")||e.baseline.includes("..")))throw new Error("baseline must be a repository-relative file path.")}async function scan(e,t={}){const n=await loadConfig(e);const r=t.target??n.targetVersion??l.version;const i=t.failOn??n.failOn??"never";targetRecord(r);const s=t.exclude??n.exclude??[];s.forEach(validateExcludePattern);const{files:o,inventory:a}=await discover(e,{exclude:s});const c={root:e,targetVersion:r,inventory:a};const u=[];for(const e of o)for(const t of m){const n=t.detect(e);const i=n.length?attachFile(n,e):inventoryMatches(t,a,e);for(const e of i){const n=t.evaluate(e,c);if(n)u.push(toFinding(t,n,r))}}const d=t.noBaseline?{fingerprints:new Set,entries:[],file:null}:await loadBaseline(e,t.baseline??n.baseline);let p=applyBaseline(dedupe(u),d);const f=t.baseRef??process.env.GITHUB_BASE_REF;if(f){try{const t=await changedLines(e,f);p=p.map((e=>({...e,changeStatus:isChanged(e,t)?"introduced":"pre-existing",lifecycle:isChanged(e,t)?"NEW":"EXISTING"})))}catch{p=p.map((e=>({...e,lifecycle:e.baselined?"EXISTING":"UNCHANGED"})))}}else p=p.map((e=>({...e,lifecycle:e.baselined?"EXISTING":"UNCHANGED"})));const h=p.filter((e=>!e.baselined));const g=h.filter((e=>e.classification==="current"));const v=h.filter((e=>e.classification==="target"));return{schemaVersion:1,tool:"shopify-upgrade-guard",targetVersion:r,failOn:i,latestStable:l.version,filesScanned:o.length,inventory:a,findings:p,currentProblems:g,upgradeBlockers:v,baselinedCount:p.filter((e=>e.baselined)).length,resolvedBaselineCount:[...d.fingerprints].filter((e=>!p.some((t=>t.fingerprint===e)))).length,baselineFile:d.file}}function inventoryMatches(e,t,n){if(!e.id.startsWith("UG-VERSION"))return[];return t.filter((e=>e.file===n.relativePath)).map((e=>({file:e.file,line:e.line,column:1,snippet:`${e.source}: ${e.version}`,version:e.version,guidance:"Update this Shopify version to a supported stable release and test the migration."})))}function toFinding(e,t,n){return{ruleId:e.id,severity:e.severity,title:e.title,surface:e.surface,file:t.file,line:t.line,column:t.column,snippet:t.snippet,targetVersion:n,currentState:e.description,migration:t.guidance,source:e.documentationUrl,migrationUrl:e.migrationUrl??null,confidence:e.confidence,classification:t.classification,reason:t.reason,deprecatedIn:e.deprecatedIn??null,removedIn:e.removedIn??null}}function dedupe(e){return[...new Map(e.map((e=>[`${e.ruleId}|${e.file}|${e.line}|${e.snippet}`,e]))).values()]}function isChanged(e,t){return Boolean(t?.get(e.file)?.some((t=>e.line>=t.start&&e.line<=t.end)))}}};var n={};function __nccwpck_require__(e){var r=n[e];if(r!==undefined){return r.exports}var i=n[e]={exports:{}};var s=true;try{t[e](i,i.exports,__nccwpck_require__);s=false}finally{if(s)delete n[e]}return i.exports}(()=>{var e=typeof Symbol==="function"?Symbol("webpack queues"):"__webpack_queues__";var t=typeof Symbol==="function"?Symbol("webpack exports"):"__webpack_exports__";var n=typeof Symbol==="function"?Symbol("webpack error"):"__webpack_error__";var resolveQueue=e=>{if(e&&e.d<1){e.d=1;e.forEach((e=>e.r--));e.forEach((e=>e.r--?e.r++:e()))}};var wrapDeps=r=>r.map((r=>{if(r!==null&&typeof r==="object"){if(r[e])return r;if(r.then){var i=[];i.d=0;r.then((e=>{s[t]=e;resolveQueue(i)}),(e=>{s[n]=e;resolveQueue(i)}));var s={};s[e]=e=>e(i);return s}}var o={};o[e]=e=>{};o[t]=r;return o}));__nccwpck_require__.a=(r,i,s)=>{var o;s&&((o=[]).d=-1);var a=new Set;var c=r.exports;var l;var u;var d;var p=new Promise(((e,t)=>{d=t;u=e}));p[t]=c;p[e]=e=>(o&&e(o),a.forEach(e),p["catch"]((e=>{})));r.exports=p;i((r=>{l=wrapDeps(r);var i;var getResult=()=>l.map((e=>{if(e[n])throw e[n];return e[t]}));var s=new Promise((t=>{i=()=>t(getResult);i.r=0;var fnQueue=e=>e!==o&&!a.has(e)&&(a.add(e),e&&!e.d&&(i.r++,e.push(i)));l.map((t=>t[e](fnQueue)))}));return i.r?s:getResult()}),(e=>(e?d(p[n]=e):u(c),resolveQueue(o))));o&&o.d<0&&(o.d=0)}})();(()=>{__nccwpck_require__.d=(e,t)=>{for(var n in t){if(__nccwpck_require__.o(t,n)&&!__nccwpck_require__.o(e,n)){Object.defineProperty(e,n,{enumerable:true,get:t[n]})}}}})();(()=>{__nccwpck_require__.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t)})();if(typeof __nccwpck_require__!=="undefined")__nccwpck_require__.ab=new URL(".",import.meta.url).pathname.slice(import.meta.url.match(/^file:\/\/\/\w:/)?1:0,-1)+"/";var r=__nccwpck_require__(242);r=await r;
|
package/package.json
CHANGED
package/src/discovery.js
CHANGED
|
@@ -11,7 +11,7 @@ export async function discover(root, options = {}) {
|
|
|
11
11
|
try { entries = await readdir(directory, { withFileTypes: true }); } catch (error) { if (error.code === 'ENOENT') return; throw error; }
|
|
12
12
|
for (const entry of entries) {
|
|
13
13
|
if (entry.name.startsWith('.') && entry.name !== '.upgradeguard.json') continue;
|
|
14
|
-
const path = join(directory, entry.name); const relativePath = relative(root, path);
|
|
14
|
+
const path = join(directory, entry.name); const relativePath = relative(root, path).replaceAll('\\', '/');
|
|
15
15
|
if (entry.isDirectory()) { if (!IGNORED_DIRECTORIES.has(entry.name) && !isExcluded(relativePath, excludes)) await walk(path); continue; }
|
|
16
16
|
if (entry.isSymbolicLink()) continue;
|
|
17
17
|
const extension = entry.name.endsWith('.toml') ? '.toml' : extname(entry.name);
|