feat(core): add the inspector engine (phase 13) - #15
Merged
Conversation
Prettier has been failing on this file since the table was added, which means `npm run check` has not been green on `main` and the next phase to run it would have inherited the failure as its own. Whitespace only — the column widths change, the content does not. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Phase 13. `core` can now answer three questions about a document it was handed — what built this site, what does it set in type, what images does it reference — so that the extension (phase 14) and the app (phase 15+) have something real to display and neither has to implement any of it. Technology detection widens past the seven storefronts `platform/detect.ts` knows, to frameworks, CMSes, analytics, tag managers, CDNs, payment and chat widgets, font services and error tracking. The model is unchanged because it was already right: weighted markers, summed and clamped, with every marker that fired kept so §11's report can answer "why did it say that?". `detect.ts` now runs on the shared engine rather than its own copy of it, so the two cannot drift; `PlatformId` and Layer A's dispatch are untouched, because a ruleset that grows every month cannot also be a type something switches on. The rules are written here, from vendor documentation and vendors' own embed snippets, and the file says so at length. Wappalyzer's ruleset is the tempting shortcut and is no longer open source; several of the "open" forks carry its data unchanged and inherit the problem. §16 asks for the licence and provenance of every rule to be recorded in the file that holds them, and an unattributed rule is now a defect rather than a loose end. The Persian widgets — Yektanet, Goftino, Raychat, ZarinPal, ArvanCloud — are in for the same reason they always are: they are what this project's users actually meet, and no foreign ruleset covers them. Fonts and images are new. Fonts read `@font-face`, stylesheet links, the family and weight a font service was asked for in its query string, and inline styles; what needs `getComputedStyle` is an optional input the extension fills rather than something `core` tries to compute, since the same code runs in a service worker, in Node and in Tauri's WebView. That distinction is load-bearing in the output too: `usedBy` is only ever filled from measurement, so its absence means "not measured" and never "unused". Images cover `<img>`, `srcset`, `<picture><source>`, CSS backgrounds, preloads, icons and social previews, resolved against `<base href>` where the document sets one, and merged by absolute URL — a hero image is routinely preloaded, given as `src` and listed in `srcset` four times, and a list that repeated it five times would be complete and useless. Icons and Open Graph previews keep their own origin kind so a "download all" does not offer a 32px favicon alongside the product photographs. `parseSrcset` is deliberately not a comma split. A resizing CDN writes `/c_fill,w_300,h_300/pic.jpg`, and splitting on commas turns one product image into two broken URLs; HTML's own parse takes the URL as a run of non-whitespace and only then looks for a descriptor, so that is what this does. There is a test with a comma-bearing CDN URL to keep it that way. Nothing here fetches, which is what lets the whole inspector run in a popup without a request against the shop's server and without touching §10's politeness accounting. Where a value can only be known by downloading the asset — byte size, natural dimensions — the field is absent rather than guessed. Empty results are treated as answers throughout, and tested as such: a hand-written page genuinely uses no recognised technology, and a confident wrong answer is the failure mode that matters most in a tool people read as a fact about a shop. 762 tests still pass; 38 new ones cover the phase's checklist. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The repository is public and the README is what people read first, so three things in it were wrong or missing after phase 13. The test badge said 760, which was already stale before this phase and is now 800. The packages table did not mention the inspector at all, so the only place `core`'s new surface appeared was a row in the phase table. The important one is the wording: phase 13 built an engine and no UI, so the Status section now says plainly that the inspector is not yet something you can click and names the phase that changes that. A public README that implied a working feature here would be advertising something nobody can use. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What and why
Phase 13 of
docs/roadmap.md.corecan now answer three questions about a document it was handed — what built this site, what does it set in type, what images does it reference — so that the extension (phase 14) and the app (phase 15+) have something real to display and neither has to implement any of it.Technology detection widens past storefronts. The seven platforms
platform/detect.tsknows become ten categories: framework, CMS, analytics, tag manager, CDN, payment, chat, font service, error tracking, and storefronts alongside them. The scoring model is unchanged because it was already right — weighted markers, summed and clamped, with every marker that fired kept so §11's report can answer "why did it say that?".detect.tsnow runs on the shared engine ininspect/signals.tsrather than its own copy of it, so the two cannot drift.PlatformIdand Layer A's dispatch are untouched: a ruleset that grows every month cannot also be a type something switches on, so the inspector returns an open string id and Layer A keeps its closed union.The rules are written here, and the file says so at length. Wappalyzer's ruleset is the tempting shortcut and stopped being open source in 2023; several of the "open" forks carry its data unchanged and inherit the problem. §16 asks for the licence and provenance of every rule to be recorded in the file that holds them, so
technologies.tsopens with that and names a source per rule set. An unattributed rule is now a defect rather than a loose end. The Persian widgets — Yektanet, Goftino, Raychat, ZarinPal, ArvanCloud — are in for the usual reason: they are what this project's users actually meet, and no foreign ruleset covers them.Fonts and images are new. Fonts read
@font-face, stylesheet links, the family and weight a font service was asked for in its query string, and inline styles. What needsgetComputedStyleis an optional input the extension fills rather than somethingcoretries to compute, since the same code runs in a service worker, in Node and in Tauri's WebView. That distinction is load-bearing in the output too:usedByis only ever filled from measurement, so its absence means "not measured" and never "unused".Images cover
<img>,srcset,<picture><source>, CSS backgrounds, preloads, icons and social previews, resolved against<base href>where the document sets one, and merged by absolute URL. A hero image is routinely preloaded, given assrcand listed insrcsetfour times, and a list that repeated it five times would be complete and useless. Icons and Open Graph previews keep their own origin kind so a future "download all" does not offer a 32px favicon alongside the product photographs.Nothing here fetches. That is what lets the whole inspector run in a popup without a request against the shop's server and without touching §10's politeness accounting. Where a value can only be known by downloading the asset — byte size, natural dimensions — the field is absent rather than guessed.
Reviewer notes
parseSrcsetis deliberately not a comma split. A resizing CDN writes/c_fill,w_300,h_300/pic.jpg, and splitting on commas turns one product image into two broken URLs. HTML's own parse takes the URL as a run of non-whitespace and only then looks for a descriptor, so that is what this does. There is a test with a comma-bearing CDN URL to keep it that way.Empty results are answers, not failures, and are tested as such. A hand-written page genuinely uses no recognised technology. A confident wrong answer is the failure mode that matters most in a tool people read as a fact about a shop, so a rule that half-fires returns nothing rather than a low-confidence entry a UI would render as a fact.
Two commits are not phase 13.
style(docs)fixes a Prettier failure indocs/store-listing.mdthat predates this branch —npm run checkwas not green onmain, and the next phase would have inherited it.docsupdates the README, which is worth a look on its own: it now says plainly that the inspector is an engine with no UI yet, because a public README implying a working feature here would be advertising something nobody can use. The stale test badge (760) is corrected there too.How it was verified
npm run checkpasses in full — format, lint, typecheck, tests.800 tests pass: the 762 that passed before this branch, plus 38 new ones in
packages/core/test/inspect/inspect.test.tscovering every box indocs/prompts/phase-13.md.Fixture-driven and offline, per §12 — no shop was contacted, and by design none can be: every function takes a document it was given. The new fixture
packages/core/test/fixtures/inspector-shop.htmlis a WooCommerce shop behind Next.js with the analytics, chat, payment and CDN widgets a Persian storefront actually carries, plus the awkward image cases (one hero referenced five ways, a comma-bearing CDN URL, a lazy-loading placeholder).no-markup.htmlis reused as the honest-nothing case.Not verified against a live shop, and cannot be at this phase: there is no surface that exposes any of this yet. Phase 14 is where the engine first meets real pages, which is the stated reason the roadmap puts it on the extension before the app.
Checklist
npm run checkpasses (format, lint, typecheck, tests)🤖 Generated with Claude Code