PWA Install Readiness and Manifest Asset Gate avatar

PWA Install Readiness and Manifest Asset Gate

Pricing

from $20.00 / 1,000 run starteds

Go to Apify Store
PWA Install Readiness and Manifest Asset Gate

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

kingii98

Maintained by Community

Actor 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:

  1. GET <origin> — read the HTML head, find <link rel="manifest"> and the inline navigator.serviceWorker.register(...) hints. The body read stops at maxDocumentBytes.
  2. GET <manifest URL> — read the manifest and parse it as JSON.
  3. HEAD <service worker script> — try each inline hint first, then each path of serviceWorkerPaths, and stop at the first script that answers.

For each declared icon, screenshot and shortcut icon:

  1. HEAD <asset URL> — read the status, the content type and the byte size.
  2. GET <asset URL> with Range: 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.

FormatBytes needed
PNG24
WebP30
BMP26
GIF10
ICO8
JPEGThe frame header can sit far into the file. Raise assetHeaderBytes for JPEG screenshots.
SVGThe 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.

FieldDefaultMeaning
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.
requireMaskableIcontrueThe 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.
requiredShortcutCount0How many shortcuts the manifest must declare. 0 turns the rule off.
allowedDisplayModesall four installable modesThe display modes that count as installable. display, or one entry of display_override, must hold one of them.
rulesall 19 rulesThe 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.
maxAssets200Hard bound on the assets that one run verifies. Maximum 500.
assetHeaderBytes64Bytes of each asset to read with the ranged GET. 32 to 8192.
maxRedirects5Maximum redirect hops for one request. 0 to 10.
timeoutSeconds20Timeout of one request. 1 to 60.
maxDocumentBytes2097152Maximum bytes to read from one HTML page or one manifest.
concurrency4Asset probes at the same time. 1 to 8.
userAgentPwaInstallGate/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 codeSeverityMeaning
INSECURE_ORIGINfailThe page finally served over HTTP. A browser offers the install dialog on a secure origin only.
MANIFEST_LINK_MISSINGfailThe HTML head declares no <link rel="manifest"> element.
MANIFEST_UNREACHABLEfailThe manifest gave an error status or no answer.
MANIFEST_NOT_JSONfailThe manifest bytes are not a JSON object.
MANIFEST_CONTENT_TYPE_MISSINGwarnThe manifest was served without a Content-Type header.
MANIFEST_CONTENT_TYPE_GENERICwarnThe manifest was served as application/json, not as application/manifest+json.
MANIFEST_CONTENT_TYPE_WRONGwarnThe manifest was served with an unrelated media type.
NAME_MISSINGfailThe manifest declares neither name nor short_name, so the install dialog has no title.
SHORT_NAME_MISSINGwarnThe manifest declares no short_name, so the home-screen label uses name.
START_URL_CROSS_ORIGINfailstart_url points at another origin.
SCOPE_EXCLUDES_START_URLwarnscope does not hold start_url.
DISPLAY_MISSINGfailThe manifest declares no display member.
DISPLAY_NOT_INSTALLABLEfaildisplay and display_override hold no allowed mode.
BACKGROUND_COLOR_MISSINGwarnNo background_color, so the splash screen uses a browser default.
THEME_COLOR_MISSINGwarnNo theme_color, so the title bar uses a browser default.
ICON_SIZE_MISSINGfailNo reachable general-purpose icon covers a required edge.
ICON_ASSET_UNREACHABLEfailOne declared icon did not load.
ICON_SIZE_MISMATCHfailOne icon file does not hold the size that the manifest declares.
MASKABLE_ICON_MISSINGfailNo reachable icon declares purpose maskable, so Android crops the icon into the adaptive shape.
SCREENSHOT_FORM_FACTOR_MISSINGwarnNo reachable screenshot serves a required form factor. The rich install dialog falls back to the plain one.
SHORTCUT_COUNT_LOWwarnThe manifest declares fewer shortcuts than the profile requires.
SHORTCUT_ICON_UNREACHABLEwarnOne declared shortcut icon did not load.
SHORTCUT_ICON_SIZE_MISMATCHwarnOne shortcut icon file does not hold the declared size.
SERVICE_WORKER_UNREACHABLEwarnNo 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

EventUnitPrice
run-startedone runUSD 0.02
manifest-auditedone origin manifest auditedUSD 0.03
asset-verifiedone declared icon, screenshot or shortcut icon verifiedUSD 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-started fires one time for each run, after the input was read and accepted. An input that cannot be parsed is not charged.
  • manifest-audited fires 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 the MANIFEST_LINK_MISSING verdict. 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 with originStatus of TARGET_REFUSED or UNREACHABLE, and they are not charged.
  • asset-verified fires 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 with billed: false, and it is not charged. An asset that the maxAssets bound cut from the run got no request either, and it is not charged. The note field 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 maxAssets bounds 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 sync
uv run pytest
uv run ruff check .