PWA Install Readiness and Manifest Asset Gate
Pricing
from $20.00 / 1,000 run starteds
PWA Install Readiness and Manifest Asset Gate
Reads the manifest link from the HTML head of each origin, reads the web app manifest, then verifies every declared icon, screenshot and shortcut icon with one HEAD and one ranged GET. It reports the true pixel size read from the file header against the d
Pricing
from $20.00 / 1,000 run starteds
Rating
0.0
(0)
Developer
kingii98
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
2 days ago
Last modified
Categories
Share
Test the install contract of your web app after each deploy. The Actor reads the manifest link from the HTML head, reads the web app manifest, and then verifies every icon, every screenshot and every shortcut icon that the manifest declares. The report gives one verdict of PASS, WARN or FAIL for each origin, and a reason code for each rule that did not pass.
A build change or an asset rename removes a screenshot or an icon. The install dialog, the splash screen and the long-press shortcuts then break, and nobody sees the loss until a user reports it. This Actor finds that loss in one run.
The store holds many Actors that extract icons. This one validates an install contract. The output is a verdict, not an asset list.
HTTP only. No browser, no proxy, no external database. The Actor never downloads a whole image: it reads the true pixel size from the first bytes of the file.
What the Actor sends
For each origin:
GET <origin>— read the HTML head, find<link rel="manifest">and the inlinenavigator.serviceWorker.register(...)hints. The body read stops atmaxDocumentBytes.GET <manifest URL>— read the manifest and parse it as JSON.HEAD <service worker script>— try each inline hint first, then each path ofserviceWorkerPaths, and stop at the first script that answers.
For each declared icon, screenshot and shortcut icon:
HEAD <asset URL>— read the status, the content type and the byte size.GET <asset URL>withRange: bytes=0-<assetHeaderBytes - 1>— read the file header, and get the true pixel size from it.
The Actor follows each redirect by hand, one hop at a time. Every hop takes the public-address test again. The Actor never sends a state-changing request.
Byte count that each image format needs
The true size can be read only when the header is complete inside the bytes that were read. The
default of 64 bytes is sufficient for every format in this table. An incomplete header gives a
size of unknown, which is a skipped comparison, never a failure.
| Format | Bytes needed |
|---|---|
| PNG | 24 |
| WebP | 30 |
| BMP | 26 |
| GIF | 10 |
| ICO | 8 |
| JPEG | The frame header can sit far into the file. Raise assetHeaderBytes for JPEG screenshots. |
| SVG | The width, the height or the viewBox attribute must be inside the read bytes. |
Input
Run the Actor with empty input to see the default fixture against a stable public PWA. Every field has a default, so an empty run always starts.
| Field | Default | Meaning |
|---|---|---|
origins | ["https://squoosh.app/"] | 1 to 100 public site origins or page URLs. Private, loopback and reserved addresses are refused, and so are URLs that hold credentials. |
requiredIconSizes | ["192", "512"] | Square icon edges that a reachable general-purpose icon must cover. A larger declared square covers a smaller requirement. |
requireMaskableIcon | true | The manifest must declare at least one reachable icon with purpose maskable. |
requiredScreenshotFormFactors | ["narrow", "wide"] | Form factors that the screenshots must cover for the rich install dialog. An empty list turns the rule off. |
requiredShortcutCount | 0 | How many shortcuts the manifest must declare. 0 turns the rule off. |
allowedDisplayModes | all four installable modes | The display modes that count as installable. display, or one entry of display_override, must hold one of them. |
rules | all 19 rules | The rules that decide the verdict. An empty list runs every rule. |
serviceWorkerPaths | ["/sw.js", "/service-worker.js", "/serviceworker.js"] | Paths to try for the service worker script. 10 entries or fewer. |
maxAssets | 200 | Hard bound on the assets that one run verifies. Maximum 500. |
assetHeaderBytes | 64 | Bytes of each asset to read with the ranged GET. 32 to 8192. |
maxRedirects | 5 | Maximum redirect hops for one request. 0 to 10. |
timeoutSeconds | 20 | Timeout of one request. 1 to 60. |
maxDocumentBytes | 2097152 | Maximum bytes to read from one HTML page or one manifest. |
concurrency | 4 | Asset probes at the same time. 1 to 8. |
userAgent | PwaInstallGate/0.1 (+https://apify.com) | The User-Agent header. ASCII only. |
The profile is the group of the six fields that carry the mark (profile) in the schema:
requiredIconSizes, requireMaskableIcon, requiredScreenshotFormFactors,
requiredShortcutCount, allowedDisplayModes and rules.
Output
The dataset holds three record types.
origin — one record for each origin. It gives originStatus (OK, TARGET_REFUSED or
UNREACHABLE), pageUrl, pageStatus, manifestUrl, manifestStatus, manifestContentType,
manifestParsed, the manifest members name, shortName, startUrl, scope, display,
backgroundColor and themeColor, the counts iconsDeclared, screenshotsDeclared,
shortcutsDeclared, shortcutIconsDeclared, assetsVerified, assetsBroken and
assetsSizeMismatch, the coverage maps iconSizeCoverage and screenshotFormFactors,
maskableIcons, serviceWorkerUrl, serviceWorkerStatus, and the verdict fields verdict,
failedRules, warnRules, reasonCodes, ruleStatuses, ruleDetails, unusableManifestEntries
and note.
asset — one record for each declared icon, screenshot and shortcut icon. It gives
assetKind, assetUrl, declaredSrc, declaredSizes, declaredType, purposes, formFactor,
shortcutName, httpStatus, headStatus, rangeStatus, finalUrl, redirectDepth,
contentType, byteSize, headerBytesRead, the true size read from the file header
(trueWidth, trueHeight, trueSize, trueFormat), sizeMatch, reachable, error and
billed.
summary — one record for the run: originsRequested, originsAudited, originsBlocked,
originsPass, originsWarn, originsFail, assetsDeclared, assetsVerified, assetsBroken,
assetsSizeMismatch, reasonCodeCounts, rulesEnabled, profile, installGatePass and
status.
A business verdict is never a failed run. A failed gate, a refused target, an unreachable origin and a missing manifest all end with a SUCCEEDED run, a dataset record and a status message. Only a malfunction, for example an input that cannot be parsed, gives a failed run.
Verdict
Each rule gives pass, warn, fail or skip. A rule that cannot be decided is skip, never a
failure. The verdict of one origin is FAIL when one rule failed, WARN when one rule warned,
and PASS when no rule failed and no rule warned. The gate of the run passes when no origin
failed and no origin was blocked.
A rule that breaks the install dialog itself has the severity fail. A rule that only degrades the
installed experience has the severity warn.
Reason codes
| Reason code | Severity | Meaning |
|---|---|---|
INSECURE_ORIGIN | fail | The page finally served over HTTP. A browser offers the install dialog on a secure origin only. |
MANIFEST_LINK_MISSING | fail | The HTML head declares no <link rel="manifest"> element. |
MANIFEST_UNREACHABLE | fail | The manifest gave an error status or no answer. |
MANIFEST_NOT_JSON | fail | The manifest bytes are not a JSON object. |
MANIFEST_CONTENT_TYPE_MISSING | warn | The manifest was served without a Content-Type header. |
MANIFEST_CONTENT_TYPE_GENERIC | warn | The manifest was served as application/json, not as application/manifest+json. |
MANIFEST_CONTENT_TYPE_WRONG | warn | The manifest was served with an unrelated media type. |
NAME_MISSING | fail | The manifest declares neither name nor short_name, so the install dialog has no title. |
SHORT_NAME_MISSING | warn | The manifest declares no short_name, so the home-screen label uses name. |
START_URL_CROSS_ORIGIN | fail | start_url points at another origin. |
SCOPE_EXCLUDES_START_URL | warn | scope does not hold start_url. |
DISPLAY_MISSING | fail | The manifest declares no display member. |
DISPLAY_NOT_INSTALLABLE | fail | display and display_override hold no allowed mode. |
BACKGROUND_COLOR_MISSING | warn | No background_color, so the splash screen uses a browser default. |
THEME_COLOR_MISSING | warn | No theme_color, so the title bar uses a browser default. |
ICON_SIZE_MISSING | fail | No reachable general-purpose icon covers a required edge. |
ICON_ASSET_UNREACHABLE | fail | One declared icon did not load. |
ICON_SIZE_MISMATCH | fail | One icon file does not hold the size that the manifest declares. |
MASKABLE_ICON_MISSING | fail | No reachable icon declares purpose maskable, so Android crops the icon into the adaptive shape. |
SCREENSHOT_FORM_FACTOR_MISSING | warn | No reachable screenshot serves a required form factor. The rich install dialog falls back to the plain one. |
SHORTCUT_COUNT_LOW | warn | The manifest declares fewer shortcuts than the profile requires. |
SHORTCUT_ICON_UNREACHABLE | warn | One declared shortcut icon did not load. |
SHORTCUT_ICON_SIZE_MISMATCH | warn | One shortcut icon file does not hold the declared size. |
SERVICE_WORKER_UNREACHABLE | warn | No service worker script answered, and the page holds no inline registration. A worker that is registered inside a bundled script file is not seen by an HTTP-only check. |
Pay per event
| Event | Unit | Price |
|---|---|---|
run-started | one run | USD 0.02 |
manifest-audited | one origin manifest audited | USD 0.03 |
asset-verified | one declared icon, screenshot or shortcut icon verified | USD 0.001 |
The default maximum charge for one run is USD 5.00. That covers about 90 origins with 20 assets each.
When each event fires:
run-startedfires one time for each run, after the input was read and accepted. An input that cannot be parsed is not charged.manifest-auditedfires one time for each origin whose page was reached and whose rules were evaluated. This includes an origin whose HTML head declares no manifest link: the work — the page request, the service worker probe and the rule evaluation — was done, and the buyer gets theMANIFEST_LINK_MISSINGverdict. You pay for a FAIL verdict of this kind. An origin that the target guard refused, and an origin whose page gave an error status or no answer, produced no audit. Those are reported withoriginStatusofTARGET_REFUSEDorUNREACHABLE, and they are not charged.asset-verifiedfires one time for each declared asset that got a HEAD and a ranged GET. An asset URL that the target guard refused got no request. It is reported withbilled: false, and it is not charged. An asset that themaxAssetsbound cut from the run got no request either, and it is not charged. Thenotefield of the origin record counts the assets that were cut.
The billed field of each asset record says exactly what was charged, so you can compare the
dataset with your invoice.
Repeat use
Run the Actor after each deploy of the web app or of the CDN, from your build pipeline, and once a
week as a regression run. The install contract is a group of static files that a build step
renames or removes without a warning. The reasonCodes field names the loss.
Limits
- 100 origins for one run.
- 500 assets for one run, and
maxAssetsbounds it further. - Loopback, private, link-local and reserved targets are refused, at the origin, at each redirect hop, and at each asset URL that the manifest supplies.
- A URL that holds credentials is refused.
- A service worker that is registered inside a bundled script file is not seen. This is an HTTP-only check, and it runs no JavaScript.
Development
uv syncuv run pytestuv run ruff check .