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 CHANGED
@@ -1,56 +1,145 @@
1
1
  # Shopify Upgrade Guard
2
2
 
3
- Shopify Upgrade Guard detects documented Shopify platform/API upgrade risks in your repository before they become production migrations.
3
+ **Catch documented Shopify API and platform upgrade risks before they become production migrations.**
4
+
5
+ [![npm](https://img.shields.io/npm/v/shopify-upgrade-guard?logo=npm)](https://www.npmjs.com/package/shopify-upgrade-guard)
6
+ [![npm downloads](https://img.shields.io/npm/dm/shopify-upgrade-guard?logo=npm)](https://www.npmjs.com/package/shopify-upgrade-guard)
7
+ [![CI](https://github.com/efegokdemir/shopify-upgrade-guard/actions/workflows/ci.yml/badge.svg)](https://github.com/efegokdemir/shopify-upgrade-guard/actions/workflows/ci.yml)
8
+ [![CodeQL](https://github.com/efegokdemir/shopify-upgrade-guard/actions/workflows/codeql.yml/badge.svg)](https://github.com/efegokdemir/shopify-upgrade-guard/actions/workflows/codeql.yml)
9
+ [![license](https://img.shields.io/github/license/efegokdemir/shopify-upgrade-guard)](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
- This repository contains the `0.2.1` hotfix release. It is an offline, evidence-backed scanner for Shopify platform and API upgrade risks.
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
- ## Try it locally
25
+ Or install it in a project:
10
26
 
11
27
  ```bash
12
- git clone https://github.com/efegokdemir/shopify-upgrade-guard.git
13
- cd shopify-upgrade-guard
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
- The scan is offline, read-only, deterministic, and uses bundled Shopify release evidence. `--target` changes rule evaluation: target-only risks are reported as upgrade blockers, while existing debt is reported separately.
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
- Pin the Action to a reviewed commit SHA in production:
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
- steps:
30
- - uses: actions/checkout@<immutable-sha>
31
- - uses: efegokdemir/shopify-upgrade-guard@<immutable-sha>
32
- with:
33
- target: 2026-10
34
- fail-on: warning
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
- Inputs: `target`, `fail-on` (`never`, `error`, `warning`, or `info`), `fail-on-new`, and `path`.
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
- Outputs: `outcome`, `finding-count`, `new-finding-count`, `error-count`, `current-versions` (the structured inventory), and `result-json`.
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
- The Action uses the current GitHub `node24` JavaScript Action runtime. Consumers do not install dependencies.
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
- node src/cli.js scan [--target YYYY-MM] [--format human|json|sarif] [--fail-on ...] [--fail-on-new]
47
- node src/cli.js baseline create|check [--path DIR]
48
- node src/cli.js rules
49
- node src/cli.js versions
50
- node src/cli.js explain UG-CHECKOUT-001
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
- Exit codes: `0` means the scan completed and the policy passed; `1` means active findings exceeded the policy; `2` means the scanner could not complete.
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. `baseline` writes `.upgradeguard-baseline.json`; baselined findings remain visible in JSON but do not fail policy, and new fingerprints remain actionable.
156
+ `exclude` accepts repository-relative glob patterns. Absolute paths and traversal are rejected.
68
157
 
69
- ## Rules and evidence
158
+ See [examples](examples/) for copy-paste CI and configuration examples.
70
159
 
71
- The initial pack is deliberately small and high-precision. Every finding includes an official source, affected version metadata where applicable, location, confidence, and migration guidance.
160
+ ## Security and privacy
72
161
 
73
- | ID | Detection | Official evidence |
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
- ## Security and privacy
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
- No telemetry, Shopify credentials, store access, or network access is required for ordinary scans. The tool reads bounded repository files, skips generated/vendor directories, does not execute repository code, and does not upload source. See [the threat model](docs/threat-model.md).
202
+ See [ROADMAP.md](ROADMAP.md).
90
203
 
91
- ## Roadmap and limitations
204
+ ## License
92
205
 
93
- GraphQL schema-wide deprecation validation and exact base-tree resolved-finding reconstruction require additional maintained evidence and are not claimed as implemented. See [ROADMAP.md](ROADMAP.md).
206
+ MIT — see [LICENSE](LICENSE).
@@ -1,5 +1,5 @@
1
1
  {
2
- "verifiedAt": "2026-09-30",
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": "latest-stable" },
12
- { "version": "2026-10", "releaseDate": "2026-10-01", "accessibleUntil": "2027-10-16T15:00:00Z", "status": "release-candidate" }
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shopify-upgrade-guard",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "Offline static analysis for Shopify platform and API upgrade risks.",
5
5
  "type": "module",
6
6
  "bin": {
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);