Code Health Assessment

vercel/commerce

Generated by Inkwell Forge — automated codebase documentation analysis. Based on analysis of 5 screens. Subject matter expert review is recommended before distribution.

May 5, 2026

Code Health Assessment

Application: commerce Document Title: Code Health Assessment Date: May 2026


Executive Summary

Generated by Inkwell Forge — automated codebase documentation analysis. Subject matter expert review is recommended before distribution.

This assessment covers 5 screens of the commerce application. Overall, the codebase is rated Moderate Concern: the architectural foundation is sound — React Server Components are used consistently and correctly, the Shopify integration is cleanly abstracted behind lib/shopify, and TypeScript strict mode is enabled throughout — but a cluster of recurring patterns across screens introduces reliability and maintainability risk that warrants structured remediation.

Key strengths include the disciplined use of the Next.js App Router RSC model (zero client-side JavaScript overhead on all five assessed screens), clean separation of data-fetching concerns behind the lib/shopify abstraction layer, and the consistent application of notFound() guards that prevent broken UI states for missing Shopify resources. The next.config.ts demonstrates deliberate use of experimental Next.js features (Partial Prerendering, inline CSS, the useCache API) that position the application well for performance at scale.

The three highest-risk findings are: [CH-002] Unguarded featuredImage Access in JSON-LD Construction (a null-dereference that will crash the Product Detail page render for any product without a featured image), [CH-001] Duplicate getPage/getProduct API Calls Across generateMetadata and Page Component (a cross-cutting pattern affecting three screens that doubles Shopify API calls per request when fetch deduplication is not active), and [CH-006] JSON.stringify Used for HTML-Embedded JSON-LD Without HTML-Character Escaping (a latent XSS vector on the Product Detail page if Shopify-sourced content contains </script> sequences). See the Finding Inventory for the complete catalog of findings by severity.

This assessment is based entirely on per-screen technical documentation. Static analysis tooling, runtime profiling, git history, and direct source code access were not available. Findings marked "(based on documentation — verify with static analysis)" or "(inferred from …)" require confirmation before scheduling remediation work.


Assessment Methodology

Quality Model

ISO/IEC 25010:2023 — eight quality characteristics assessed where applicable: Maintainability, Reliability, Security, Performance Efficiency, Functional Suitability, Usability, Compatibility, and Portability.

Debt Classification

SQALE method — findings are classified by remediation type (Design Debt, Code Debt, Test Debt, Doc Debt, Dependency Debt) and effort band (Low / Medium / High / Very High).

Severity Model

Aligned with SonarQube severity definitions:

Severity Definition Impact SLA
Blocker Defect that will cause production failure or data loss System-breaking Fix immediately
Critical Severe code quality issue with high probability of causing bugs Major risk Fix within current sprint
Major Significant quality issue that degrades maintainability or reliability Moderate risk Fix within 30 days
Minor Code quality issue with limited impact Low risk Fix within 90 days
Info Best practice recommendation or improvement suggestion No immediate risk Consider for future work

Scope

This assessment covers 5 screens of the commerce application:

Screen Route
Home /
Item Detail /[page]
Product Detail /product/[handle]
Search /search
Search Detail /search/[collection]

Screens, features, and components not included in the assessed documentation are outside the scope of this document. This includes lib/shopify, components/product/product-description, components/product/gallery, components/grid/tile, components/layout/product-grid-items, components/grid, and all middleware and layout files beyond app/layout.tsx.

Limitations


Quality Scorecard

Quality Dimension Score Rating Key Finding Findings Count
Maintainability 3/5 Fair Duplicate API call pattern recurs across three screens; hardcoded magic numbers and unsafe type casts add fragility 4
Reliability 3/5 Fair Unguarded null dereference on featuredImage will crash Product Detail for products without a hero image 3
Security 4/5 Good JSON.stringify without HTML-character escaping creates a latent XSS vector in JSON-LD injection 2
Performance Efficiency 3/5 Fair No pagination on Search and Search Detail; duplicate API calls per request; no loading skeleton on ProductDescription 3
Functional Suitability 3/5 Fair Search Detail renders no collection heading; hardcoded SEO description is a template placeholder 2
Usability 3/5 Fair Missing loading feedback for ProductDescription; no <time> element for dates; <span> used where <strong> is semantically correct 3
Compatibility 4/5 Good Locale-dependent date rendering tied to server locale may produce inconsistent output across environments 1
Portability 4/5 Good Canary Next.js version with three experimental flags introduces upgrade risk 1

Detailed Findings by Quality Dimension

Maintainability (Score: 3/5)

Overview

The five assessed screens follow a consistent architectural pattern — thin RSC compositor pages that delegate data fetching and rendering to child components. This is a positive structural choice. However, three recurring patterns degrade maintainability: (1) a duplicate API call pattern in generateMetadata / page component pairs that appears on three screens, (2) unsafe searchParams type assertions on two screens, and (3) a hardcoded magic number for the image gallery cap. The codebase has no co-located tests on any assessed screen, which is the most significant maintainability gap.


CH-001: Duplicate API Calls in generateMetadata and Page Component

Severity: Major Quality Sub-Characteristic: Reusability / Analysability Screen(s): Item Detail (/[page]), Product Detail (/product/[handle]), Search Detail (/search/[collection]) Component(s): app/[page]/page.tsx, app/product/[handle]/page.tsx, app/search/[collection]/page.tsx

Description: On three screens, the same Shopify service function is called independently in both generateMetadata and the default page export: getPage on Item Detail, getProduct on Product Detail, and getCollection on Search Detail. Each call is a separate invocation with no shared result. If the underlying lib/shopify functions use the native fetch API with identical cache keys, Next.js request memoization will deduplicate them within a single render pass. If lib/shopify uses a non-fetch HTTP client (e.g., a custom GraphQL client or node-fetch), two separate Shopify API network requests are made per page load on each of these three routes. This doubles API quota consumption, increases latency, and makes the data-fetching logic harder to reason about.

Evidence:

Risk: If lib/shopify does not use native fetch (a common pattern in Shopify integrations that use a custom GraphQL client), every page load on these three routes makes two Shopify API calls instead of one. Under load, this doubles API rate-limit consumption and increases p99 latency. It also creates a subtle consistency risk: if the Shopify data changes between the two calls (unlikely but possible during a deployment), generateMetadata and the page body could render different data.

Recommendation:

// lib/shopify/cached.ts
import { cache } from 'react';
import { getProduct as _getProduct } from './index';

export const getProduct = cache(_getProduct);

Effort: Medium (2–4 hours across all three screens)

SQALE Characteristic: Efficiency / Maintainability

Source Evidence: Item Detail (/[page]), Product Detail (/product/[handle]), Search Detail (/search/[collection])


CH-003: Unsafe searchParams Type Assertion

Severity: Major Quality Sub-Characteristic: Modifiability / Analysability Screen(s): Search (/search), Search Detail (/search/[collection]) Component(s): app/search/page.tsx, app/search/[collection]/page.tsx

Description: Both screens cast searchParams using as { [key: string]: string }. This assertion suppresses TypeScript's awareness that: (a) query parameter values can be string | string[] | undefined when the same key appears multiple times in a URL, and (b) searchParams itself may be undefined (the prop type on Search Detail is documented as Promise<...> | undefined). The cast bypasses the type system's protection, meaning a crafted URL like /search?q=shirt&q=pants would pass a string[] to getProducts as the query parameter, producing undefined behavior. On Search Detail, if searchParams is undefined, the destructuring will throw a runtime error.

Evidence:

Risk: A crafted URL with duplicate query keys could cause getProducts or getCollectionProducts to receive a string[] where a string is expected, potentially causing an unhandled exception or incorrect API query. On Search Detail, an undefined searchParams causes a runtime crash. Both scenarios would surface as 500 errors to the user.

Recommendation:

// Safe extraction pattern
const rawSort = Array.isArray(searchParams?.sort)
  ? searchParams.sort[0]
  : searchParams?.sort;
const sort = typeof rawSort === 'string' ? rawSort : undefined;

Effort: Low (< 1 hour per screen; 2 hours total)

SQALE Characteristic: Reliability / Maintainability

Source Evidence: Search (/search), Search Detail (/search/[collection])


Severity: Minor Quality Sub-Characteristic: Modifiability Screen(s): Product Detail (/product/[handle]) Component(s): app/product/[handle]/page.tsx

Description: The expression product.images.slice(0, 5) uses the literal 5 as the maximum number of images passed to the Gallery component. This magic number has no named constant, no comment explaining the rationale, and no configuration entry. If the desired gallery size changes, a developer must locate this literal by searching the codebase rather than updating a single named constant.

Evidence: Product Detail (/product/[handle]): §8 — "Only the first 5 product images (product.images.slice(0, 5)) are passed to Gallery, regardless of how many images Shopify returns. This is a hard-coded business rule limiting gallery size." §17 — "Hard-coded image limit: product.images.slice(0, 5) is a magic number with no named constant or configuration."

Risk: Low immediate risk. The risk is a maintenance error: a future developer changes the gallery layout to support 6 images, updates the Gallery component, but misses the slice(0, 5) call in the page file, resulting in the 6th image never being displayed. The bug would be non-obvious because the Gallery component would appear to work correctly.

Recommendation:

// lib/constants.ts
export const MAX_GALLERY_IMAGES = 5;
product.images.slice(0, MAX_GALLERY_IMAGES).map(...)

Effort: Low (< 30 minutes)

SQALE Characteristic: Maintainability

Source Evidence: Product Detail (/product/[handle])


CH-010: No Co-Located Tests on Any Assessed Screen

Severity: Major Quality Sub-Characteristic: Testability Screen(s): Home (/), Item Detail (/[page]), Product Detail (/product/[handle]), Search (/search), Search Detail (/search/[collection]) Component(s): All page components

Description: No co-located tests were found for any of the five assessed screens. The package.json test script runs only prettier:check — a formatting check, not a test suite. No testing framework (Jest, Vitest, Playwright, Cypress) appears in the project manifest. This means there is no automated regression safety net for any of the documented screens. The documentation for each screen provides detailed recommended testing strategies (unit, integration, E2E), indicating the team is aware of the gap.

Evidence:

Risk: Without automated tests, any refactoring of the Shopify service layer, sort logic, metadata generation, or routing behavior carries a high risk of silent regression. The duplicate API call pattern (CH-001), the null-dereference risk (CH-002), and the type assertion issues (CH-003) would all benefit from test coverage before remediation to prevent introducing new bugs.

Recommendation:

Effort: Very High (16+ hours to establish the framework and initial coverage across all five screens)

SQALE Characteristic: Testability

Source Evidence: All five assessed screens (§16); package.json


Reliability (Score: 3/5)

Overview

The screens handle the most common failure mode (missing Shopify resource) consistently and correctly via notFound() guards. However, one confirmed null-dereference bug will crash the Product Detail page for any product without a featured image, and the uniform treatment of "not found" and "API error" as the same condition makes operational diagnosis difficult. No error tracking SDK is identifiable from the project manifest.


CH-002: Unguarded featuredImage Access in JSON-LD Construction

Severity: Critical Quality Sub-Characteristic: Fault Tolerance Screen(s): Product Detail (/product/[handle]) Component(s): app/product/[handle]/page.tsxproductJsonLd construction block

Description: The productJsonLd object accesses product.featuredImage.url without a null guard. The generateMetadata function in the same file correctly guards this access with product.featuredImage || {}, but the page body's JSON-LD construction does not apply the same pattern. If a Shopify product has no featured image (a valid Shopify state), product.featuredImage will be null or undefined, and accessing .url on it will throw a TypeError at render time, crashing the entire page render and returning a 500 error to the user.

Evidence: Product Detail (/product/[handle]): §10 — "Missing featured image: The productJsonLd object accesses product.featuredImage.url without a null guard. If featuredImage is null or undefined, this will throw a runtime error." §17 — "Unguarded featuredImage access in JSON-LD: product.featuredImage.url in productJsonLd has no null guard, unlike the generateMetadata function which uses product.featuredImage || {}."

Risk: Any product in the Shopify catalog that lacks a featured image will produce a 500 error on its product detail page. This is a production-impacting bug, not a theoretical risk. The inconsistency with generateMetadata (which correctly guards the same field) suggests this was an oversight rather than an intentional design choice.

Recommendation:

const productJsonLd = {
  // ...
  image: product.featuredImage?.url,
  offers: {
    // ...
  },
};
...(product.featuredImage && { image: product.featuredImage.url }),

Effort: Low (< 30 minutes)

SQALE Characteristic: Reliability

Source Evidence: Product Detail (/product/[handle])


CH-008: No Differentiation Between "Not Found" and "API Error"

Severity: Minor Quality Sub-Characteristic: Fault Tolerance / Analysability Screen(s): Item Detail (/[page]), Product Detail (/product/[handle]), Search Detail (/search/[collection]) Component(s): app/[page]/page.tsx, app/product/[handle]/page.tsx, app/search/[collection]/page.tsx

Description: On all three screens that call notFound(), both a "resource not found in Shopify" condition and a "Shopify API network/infrastructure failure" condition result in the same notFound() call (or an unhandled exception that surfaces as a generic 500). Users and monitoring systems cannot distinguish between a legitimately missing page/product and a Shopify API outage from the rendered output. During an outage, users would see 404 pages for products that actually exist, which is misleading and could suppress incident detection.

Evidence:

Risk: During a Shopify API outage, product and content pages return 404 responses. This suppresses the true error signal, makes incident detection harder, and could cause SEO crawlers to de-index pages that are temporarily unavailable rather than permanently removed.

Recommendation:

// In page component
try {
  const page = await getPage(params.page);
  if (!page) notFound();
  // render...
} catch (err) {
  if (err instanceof ShopifyNotFoundError) notFound();
  throw err; // propagate to error.tsx
}

Effort: High (4–8 hours, primarily in lib/shopify to introduce typed error classes)

SQALE Characteristic: Reliability

Source Evidence: Item Detail (/[page]), Product Detail (/product/[handle]), Search Detail (/search/[collection])


CH-009: No Observability SDK Identifiable in Project Manifest

Severity: Minor Quality Sub-Characteristic: Analysability Screen(s): All five assessed screens Component(s): All page components; package.json

Description: No error tracking, application performance monitoring, or logging SDK is identifiable in the project manifest (package.json). No observability-related calls appear in any of the five documented screen files. This means that server-side rendering errors, Shopify API failures, and null-dereference crashes (such as CH-002) may go undetected until a user reports them.

Evidence:

Risk: Production errors on any of the five assessed routes will be invisible to the engineering team unless a user reports them or the team actively monitors server logs. The null-dereference bug in CH-002 is a concrete example of an error that would be silent without instrumentation.

Recommendation:

Effort: Medium (2–4 hours to integrate an SDK; Low if Vercel observability is already active)

SQALE Characteristic: Maintainability / Reliability

Source Evidence: All five assessed screens (§10); package.json


Security (Score: 4/5)

Overview

The security posture of the assessed screens is generally strong. All screens are React Server Components, which eliminates the risk of leaking server-side secrets to the client bundle. No user input is processed on any screen except as URL parameters passed to the Shopify API abstraction layer. The two findings below are a latent XSS vector in JSON-LD injection and a minor type-safety bypass in URL parameter handling (the latter is also captured under Maintainability as CH-003).

[Diagram: Security boundary diagram showing the RSC server boundary, lib/shopify abstraction layer, and Shopify Storefront API — illustrating where credentials are held and where user-supplied input enters the system]


CH-006: JSON.stringify Without HTML-Character Escaping in JSON-LD Injection

Severity: Critical Quality Sub-Characteristic: Security — Integrity Screen(s): Product Detail (/product/[handle]) Component(s): app/product/[handle]/page.tsx — JSON-LD <script> block

Description: The Product Detail page injects structured data into the page using dangerouslySetInnerHTML={{ __html: JSON.stringify(productJsonLd) }}. JSON.stringify does not escape HTML special characters such as <, >, &, or /. If a Shopify product's title, description, or any other field included in productJsonLd contains the sequence </script>, the browser's HTML parser will interpret it as closing the <script> tag, potentially allowing injected content to execute as JavaScript. While Shopify sanitizes merchant-authored content, this assumption is a single point of failure: a compromised Shopify account, a supply-chain issue in Shopify's content pipeline, or a future change to Shopify's sanitization policy would expose this vector.

Evidence: Product Detail (/product/[handle]): §14 — "JSON.stringify does not escape HTML special characters like <, >, or & by default, which could theoretically allow script injection if a product title or description contained </script> sequences. A safer approach would use a JSON serializer that escapes these characters."

Risk: If exploited, this could allow arbitrary JavaScript execution in the user's browser (XSS), enabling session hijacking, credential theft, or malicious redirects. The risk is currently mitigated by Shopify's content sanitization, but defense-in-depth requires that the application not rely solely on the upstream provider's sanitization.

Recommendation:

function safeJsonLd(data: unknown): string {
  return JSON.stringify(data)
    .replace(/</g, '\\u003c')
    .replace(/>/g, '\\u003e')
    .replace(/&/g, '\\u0026')
    .replace(/'/g, '\\u0027');
}
<script
  type="application/ld+json"
  dangerouslySetInnerHTML={{ __html: safeJsonLd(productJsonLd) }}
/>

Effort: Low (< 1 hour)

SQALE Characteristic: Security

Source Evidence: Product Detail (/product/[handle])


CH-007: page.body HTML Rendered Without Application-Level Sanitization

Severity: Minor Quality Sub-Characteristic: Security — Integrity Screen(s): Item Detail (/[page]) Component(s): app/[page]/page.tsx, components/prose

Description: The page.body field — a raw HTML string authored in Shopify's admin — is passed directly to the Prose component via an html prop. The documentation notes that the Prose component likely uses dangerouslySetInnerHTML to render this content. No application-level HTML sanitization is applied before rendering. The safety of this pattern depends entirely on Shopify's server-side sanitization of page body content. This is a defense-in-depth gap: if Shopify's sanitization is bypassed (e.g., via a compromised merchant account or a Shopify platform vulnerability), the application has no secondary protection.

Evidence: Item Detail (/[page]): §14 — "The safety of this rendering depends entirely on the Prose component's implementation — if it uses dangerouslySetInnerHTML without sanitization, XSS is theoretically possible if Shopify's content pipeline is compromised. In practice, Shopify sanitizes page body content server-side, but this screen performs no additional sanitization."

Risk: Severity is rated Minor (not Critical) because: (a) Shopify's content sanitization is a well-established, actively maintained control; (b) the content is merchant-authored, not user-submitted; and (c) this is a server-rendered page with no client-side state that could be exfiltrated. The risk is real but requires a multi-layer compromise to exploit.

Recommendation:

Effort: Medium (2–4 hours to add sanitization; Low to add documentation comment)

SQALE Characteristic: Security

Source Evidence: Item Detail (/[page])


Performance Efficiency (Score: 3/5)

Overview

The RSC-first architecture is a strong performance foundation: zero client-side JavaScript on all five assessed screens, full-route caching via Next.js, and AVIF/WebP image optimization configured in next.config.ts. The three performance findings below are architectural gaps rather than implementation errors: the absence of pagination on product listing screens, the duplicate API call pattern (also captured as CH-001), and the missing loading skeleton for ProductDescription.

[Diagram: Data flow diagram for Product Detail page showing parallel vs. sequential fetch paths — getProduct (called twice), getProductRecommendations, and the Suspense streaming boundaries]


CH-005: No Pagination on Product Listing Screens

Severity: Major Quality Sub-Characteristic: Time Behaviour / Resource Utilisation Screen(s): Search (/search), Search Detail (/search/[collection]) Component(s): app/search/page.tsx, app/search/[collection]/page.tsx

Description: Both product listing screens fetch and render all matching products in a single Shopify API request. Shopify's Storefront API caps results at 250 products per request. For stores with large catalogs or popular collections, this means: (a) the Shopify API response payload is large, (b) the server-rendered HTML is large, (c) the Time to First Byte increases proportionally with catalog size, and (d) the browser must parse and render a large DOM. No pagination, infinite scroll, or cursor-based loading is implemented at either screen level.

Evidence:

Risk: For stores with more than ~50 products per collection or search result, page load times will degrade noticeably. At 250 products (Shopify's API limit), the rendered HTML and image loading will be substantial. This is a scalability cliff: the application works well for small catalogs but degrades predictably as the catalog grows.

Recommendation:

Effort: Very High (16+ hours to implement cursor-based pagination across both screens and the lib/shopify service layer)

SQALE Characteristic: Efficiency

Source Evidence: Search (/search), Search Detail (/search/[collection])


CH-011: Missing Loading Skeleton for ProductDescription Suspense Boundary

Severity: Minor Quality Sub-Characteristic: Time Behaviour / Usability Screen(s): Product Detail (/product/[handle]) Component(s): app/product/[handle]/page.tsxProductDescription Suspense boundary

Description: The Suspense boundary wrapping ProductDescription uses fallback={null}, meaning no placeholder UI is shown while the component resolves. The Gallery component correctly uses a blank square placeholder to prevent layout shift, but the right-side panel (title, price, variant selectors, add-to-cart) renders nothing during loading. On slow connections, the product image appears but the purchase UI is absent, which is disorienting and may cause users to believe the page is broken.

Evidence: Product Detail (/product/[handle]): §17 — "No loading feedback for ProductDescription: The Suspense fallback for ProductDescription is null, meaning users see nothing while the component loads. This could cause layout shift and a poor experience on slow connections."

Risk: On slow connections or during high server load, users see a product image with no title, price, or add-to-cart button. This degrades the purchase experience and may increase bounce rate on the most commercially important page in the application.

Recommendation:

<Suspense fallback={<ProductDescriptionSkeleton />}>
  <ProductDescription product={product} />
</Suspense>

Effort: Medium (1–3 hours to design and implement the skeleton component)

SQALE Characteristic: Usability / Efficiency

Source Evidence: Product Detail (/product/[handle])


Functional Suitability (Score: 3/5)

Overview

The five screens implement their documented feature sets correctly. The two findings in this dimension are a missing page-level heading on Search Detail (a functional gap that affects both usability and SEO) and a hardcoded placeholder SEO description on the Home screen that should be replaced before production use.


CH-012: Search Detail Renders No Collection Heading

Severity: Major Quality Sub-Characteristic: Functional Completeness Screen(s): Search Detail (/search/[collection]) Component(s): app/search/[collection]/page.tsx

Description: The Search Detail page renders no <h1> or collection title in the page body. The collection name appears only in the browser tab title (via generateMetadata), but the page body contains only a product grid or an empty-state message. Users have no visual confirmation of which collection they are viewing. This is both a functional gap (users cannot confirm their navigation context) and an accessibility issue (no page heading for screen readers).

Evidence: Search Detail (/search/[collection]): §17 — "No collection heading rendered: The page body renders no <h1> or collection title, which is both an accessibility concern (no page heading) and a UX gap (users have no visual confirmation of which collection they are viewing beyond the browser tab title)."

Risk: Users who navigate to a collection page from a menu or search filter have no on-page confirmation of their location. If the collection name is not visible in the surrounding navigation (e.g., on mobile where the nav is collapsed), the page context is entirely ambiguous. This also means the page has no <h1>, which is a significant SEO signal gap for collection pages.

Recommendation:

<h1 className="text-2xl font-bold mb-6">{collection.title}</h1>

Effort: Low (< 1 hour, assuming CH-001 is addressed first to avoid a third getCollection call)

SQALE Characteristic: Functional Completeness

Source Evidence: Search Detail (/search/[collection])


CH-013: Hardcoded Template Placeholder in Home Screen SEO Metadata

Severity: Minor Quality Sub-Characteristic: Functional Correctness Screen(s): Home (/) Component(s): app/page.tsxmetadata export

Description: The metadata export on the Home screen contains a hardcoded description string: "High-performance ecommerce store built with Next.js, Vercel, and Shopify." This is a generic stack description from the Next.js Commerce starter template, not a meaningful, store-specific SEO description. It will appear in Google search results and social media link previews for the store's homepage, presenting a technical description rather than brand or product copy.

Evidence: Home (/): §17 — "The description in the metadata export is hardcoded as a generic stack description ('High-performance ecommerce store built with Next.js, Vercel, and Shopify.') rather than a meaningful, store-specific SEO description. This is likely a placeholder from a starter template and should be updated with actual brand/store copy before production use."

Risk: Low technical risk. The business risk is that the store's homepage appears in search results with a developer-facing description rather than a customer-facing one, reducing click-through rates from organic search.

Recommendation:

const { SITE_NAME, SITE_DESCRIPTION } = process.env;

export const metadata = {
  description: SITE_DESCRIPTION,
  // ...
};

Effort: Low (< 30 minutes)

SQALE Characteristic: Functional Correctness

Source Evidence: Home (/)


Usability (Score: 3/5)

Overview

The screens handle the most common empty and error states correctly (no-results messages, notFound() for missing resources). The usability findings below are a cluster of smaller gaps: a missing semantic <time> element, a <span> used where <strong> is semantically correct, and the missing ProductDescription loading skeleton (also captured under Performance as CH-011).


CH-014: Missing <time> Element for Machine-Readable Date

Severity: Minor Quality Sub-Characteristic: Accessibility / Usability Screen(s): Item Detail (/[page]) Component(s): app/[page]/page.tsx — last-updated paragraph

Description: The last-updated date on the Item Detail screen is rendered as a plain formatted string inside a <p> tag. The HTML <time> element with a datetime attribute is the semantically correct element for dates and times — it provides a machine-readable ISO 8601 timestamp alongside the human-readable display string, which assistive technologies, search engines, and browser extensions can consume.

Evidence: Item Detail (/[page]): §15 — "The Intl.DateTimeFormat date string is rendered as visible text only; there is no <time> element with a datetime attribute, which would be more semantically correct and machine-readable for assistive technologies." §17 — "No <time> element for the updated date."

Risk: Low. Screen readers can read the formatted date string. The gap is semantic correctness and machine-readability, not a functional failure.

Recommendation:

<p className="text-sm italic">
  {`${page.title} was last updated on `}
  <time dateTime={page.updatedAt}>
    {new Intl.DateTimeFormat('en-US', {
      year: 'numeric',
      month: 'long',
      day: 'numeric',
    }).format(new Date(page.updatedAt))}
  </time>
</p>

Effort: Low (< 30 minutes)

SQALE Characteristic: Usability

Source Evidence: Item Detail (/[page])


CH-016: <span> Used for Semantic Emphasis Where <strong> Is Correct

Severity: Info Quality Sub-Characteristic: Usability / Accessibility Screen(s): Search (/search) Component(s): app/search/page.tsx — results summary paragraph

Description: The search term in the results summary paragraph is wrapped in <span className="font-bold"> for visual bold styling. The semantically correct element for text that has strong importance or emphasis is <strong>, which conveys semantic meaning to screen readers and search engines in addition to the visual weight. Using <span> with a CSS class achieves the visual effect but loses the semantic signal.

Evidence: Search (/search): §15 — "The search term is wrapped in <span className='font-bold'> — a presentational element. Using <strong> instead would convey semantic emphasis to screen readers."

Risk: Minimal. Screen readers will still read the search term; they simply will not announce it as emphasized. This is a best-practice gap, not a functional failure.

Recommendation:

<strong>{searchValue}</strong>

Effort: Low (< 15 minutes)

SQALE Characteristic: Usability

Source Evidence: Search (/search)


Compatibility (Score: 4/5)

Overview

The application targets modern browsers via the es2015 TypeScript compilation target and uses CSS features (Tailwind v4, container queries) that have broad modern browser support. One compatibility concern is identified: locale-dependent date rendering tied to the server's runtime locale.


CH-015: Locale-Dependent Date Rendering Tied to Server Runtime Locale

Severity: Minor Quality Sub-Characteristic: Co-existence / Adaptability Screen(s): Item Detail (/[page]) Component(s): app/[page]/page.tsx — date formatting

Description: The updatedAt date is formatted using new Intl.DateTimeFormat(undefined, { ... }). Passing undefined as the locale instructs the runtime to use the system/environment default locale. On a Vercel deployment, the Node.js runtime locale is typically en-US, but this is not guaranteed across all deployment environments, regions, or future infrastructure changes. If the server locale changes, the date format changes silently (e.g., from "January 15, 2024" to "15. Januar 2024"), which may be unexpected for an English-language storefront.

Evidence: Item Detail (/[page]): §8 — "Using undefined as the locale argument to Intl.DateTimeFormat ... may differ from the user's locale and could produce inconsistent output across deployment environments." §17 — "Locale-dependent date rendering: Using undefined as the locale for Intl.DateTimeFormat ties the date format to the server's runtime locale."

Risk: Low in practice on Vercel, but the behavior is environment-dependent and non-deterministic across deployment targets. A future infrastructure change or multi-region deployment could produce inconsistent date formats.

Recommendation:

new Intl.DateTimeFormat('en-US', {
  year: 'numeric',
  month: 'long',
  day: 'numeric',
}).format(new Date(page.updatedAt))

Effort: Low (< 15 minutes)

SQALE Characteristic: Portability / Compatibility

Source Evidence: Item Detail (/[page])


Portability (Score: 4/5)

Overview

The application is well-positioned for portability: the Shopify integration is cleanly abstracted behind lib/shopify, environment variables are server-only, and the RSC architecture avoids client-side framework lock-in. One portability concern is the use of three experimental Next.js features in next.config.ts on a canary release.


CH-017: Canary Next.js Version with Three Experimental Flags

Severity: Minor Quality Sub-Characteristic: Installability / Adaptability Screen(s): All five assessed screens Component(s): next.config.ts, package.json

Description: The project uses next: "15.6.0-canary.60" — a canary (pre-release) build of Next.js — with three experimental flags enabled: ppr: true (Partial Prerendering), inlineCss: true, and useCache: true. Canary releases are not production-stable; APIs, behavior, and configuration options can change between canary versions without a semver major bump. The useCache API in particular is documented as experimental and subject to change. Running a canary build in production means the application may break on a routine pnpm update if the canary API surface changes.

Evidence: package.json: "next": "15.6.0-canary.60". next.config.ts: experimental: { ppr: true, inlineCss: true, useCache: true }.

Risk: Moderate upgrade risk. If the team updates Next.js (e.g., to a newer canary or to the stable 15.x release), any of the three experimental APIs could have breaking changes. The useCache API is the highest-risk flag as it is the newest and most likely to change. This is not an immediate production risk if the version is pinned, but it creates upgrade debt.

Recommendation:

Effort: Low (< 1 hour to pin and document; Medium for the eventual stable migration)

SQALE Characteristic: Portability

Source Evidence: package.json, next.config.ts


Technical Debt Inventory

TD ID Finding Type Severity Effort Screen(s) Source
TD-001 Duplicate getPage/getProduct/getCollection calls in generateMetadata and page component Design Debt Major Medium Item Detail, Product Detail, Search Detail Item Detail (/[page]), Product Detail (/product/[handle]), Search Detail (/search/[collection])
TD-002 Unguarded featuredImage.url access in productJsonLd construction Code Debt Critical Low Product Detail Product Detail (/product/[handle])
TD-003 Unsafe searchParams type assertion bypasses TypeScript safety Code Debt Major Low Search, Search Detail Search (/search), Search Detail (/search/[collection])
TD-004 Hardcoded magic number 5 for gallery image cap Code Debt Minor Low Product Detail Product Detail (/product/[handle])
TD-005 No pagination on product listing screens Design Debt Major Very High Search, Search Detail Search (/search), Search Detail (/search/[collection])
TD-006 JSON.stringify without HTML-character escaping in JSON-LD injection Code Debt Critical Low Product Detail Product Detail (/product/[handle])
TD-007 page.body HTML rendered without application-level sanitization Design Debt Minor Medium Item Detail Item Detail (/[page])
TD-008 No differentiation between "not found" and "API error" conditions Design Debt Minor High Item Detail, Product Detail, Search Detail Item Detail (/[page]), Product Detail (/product/[handle]), Search Detail (/search/[collection])
TD-009 No observability SDK identifiable in project manifest Design Debt Minor Medium All screens All screens; package.json
TD-010 No co-located tests; test script runs only Prettier Test Debt Major Very High All screens All screens; package.json
TD-011 Missing loading skeleton for ProductDescription Suspense boundary Code Debt Minor Medium Product Detail Product Detail (/product/[handle])
TD-012 Search Detail renders no collection heading (<h1>) Code Debt Major Low Search Detail Search Detail (/search/[collection])
TD-013 Hardcoded template placeholder SEO description on Home screen Doc Debt Minor Low Home Home (/)
TD-014 Missing <time> element for machine-readable date Code Debt Minor Low Item Detail Item Detail (/[page])
TD-015 <span> used for semantic emphasis instead of <strong> Code Debt Info Low Search Search (/search)
TD-016 Locale-dependent date rendering tied to server runtime locale Code Debt Minor Low Item Detail Item Detail (/[page])
TD-017 Canary Next.js version with three experimental flags Dependency Debt Minor Low (pin) / Medium (migrate) All screens package.json, next.config.ts

Finding Inventory

Finding ID Quality Dimension Severity Title Screen(s) Effort Status
CH-001 Maintainability Major Duplicate API Calls in generateMetadata and Page Component Item Detail, Product Detail, Search Detail Medium Open
CH-002 Reliability Critical Unguarded featuredImage Access in JSON-LD Construction Product Detail Low Open
CH-003 Maintainability Major Unsafe searchParams Type Assertion Search, Search Detail Low Open
CH-004 Maintainability Minor Hardcoded Magic Number for Gallery Image Cap Product Detail Low Open
CH-005 Performance Efficiency Major No Pagination on Product Listing Screens Search, Search Detail Very High Open
CH-006 Security Critical JSON.stringify Without HTML-Character Escaping in JSON-LD Injection Product Detail Low Open
CH-007 Security Minor page.body HTML Rendered Without Application-Level Sanitization Item Detail Medium Open
CH-008 Reliability Minor No Differentiation Between "Not Found" and "API Error" Item Detail, Product Detail, Search Detail High Open
CH-009 Reliability Minor No Observability SDK Identifiable in Project Manifest All screens Medium Open
CH-010 Maintainability Major No Co-Located Tests on Any Assessed Screen All screens Very High Open
CH-011 Performance Efficiency Minor Missing Loading Skeleton for ProductDescription Suspense Boundary Product Detail Medium Open
CH-012 Functional Suitability Major Search Detail Renders No Collection Heading Search Detail Low Open
CH-013 Functional Suitability Minor Hardcoded Template Placeholder in Home Screen SEO Metadata Home Low Open
CH-014 Usability Minor Missing <time> Element for Machine-Readable Date Item Detail Low Open
CH-015 Compatibility Minor Locale-Dependent Date Rendering Tied to Server Runtime Locale Item Detail Low Open
CH-016 Usability Info <span> Used for Semantic Emphasis Where <strong> Is Correct Search Low Open
CH-017 Portability Minor Canary Next.js Version with Three Experimental Flags All screens Low Open

Needs Verification

The following findings are based on inference from technology patterns or incomplete documentation evidence. They are not counted in severity totals and are not scheduled in the Remediation Roadmap. Each requires a specific verification action before it can be promoted to the main Finding Inventory.

Finding ID Quality Dimension Inferred Severity Title Inference Basis Verification Action
CHV-001 Security Minor Prose Component Uses dangerouslySetInnerHTML Without Sanitization (inferred from component name and html prop pattern — verify against components/prose source) Inspect components/prose to confirm whether dangerouslySetInnerHTML is used and whether any sanitization is applied. If confirmed unsanitized, promote CH-007 severity to Major.
CHV-002 Reliability Minor lib/shopify Uses Non-fetch HTTP Client, Disabling Next.js Request Memoization (inferred from Next.js App Router deduplication documentation — verify against lib/shopify source) Inspect lib/shopify to determine whether getPage, getProduct, getCollection, and getCollectionProducts use the native fetch API. If a non-fetch client is used, CH-001 severity should be upgraded to Critical.
CHV-003 Performance Efficiency Minor getCollectionProducts Has No Explicit Cache Configuration (inferred from Next.js 15 default dynamic rendering behavior — verify against lib/shopify source) Inspect lib/shopify getCollectionProducts and getProducts for next.revalidate or cache options. If absent, these calls are fully dynamic (no caching), which is a performance concern for high-traffic stores.
CHV-004 Reliability Info No error.tsx Co-Located with Search or Search Detail Routes (inferred from §17 of Search screen — "No explicit error boundary" — verify against app/search/ directory structure) Check whether app/search/error.tsx and app/search/[collection]/error.tsx exist. If absent, API errors on these routes fall through to the root error boundary, producing a full-page error for a partial failure.

Remediation Roadmap

Phase 1: Immediate (This Sprint)

Priority Finding ID(s) Action Effort Impact
1 CH-002 Add null guard to product.featuredImage.url in productJsonLd construction in app/product/[handle]/page.tsx Low Eliminates production crash for products without a featured image
2 CH-006 Replace JSON.stringify with an HTML-character-escaping serializer in the JSON-LD <script> block Low Closes latent XSS vector in JSON-LD injection

Phase 2: Short-Term (30 Days)

Priority Finding ID(s) Action Effort Impact
1 CH-001 Wrap getPage, getProduct, and getCollection in React cache() to deduplicate calls across generateMetadata and page components Medium Eliminates duplicate Shopify API calls on three routes; reduces API quota consumption and latency
2 CH-003 Replace as { [key: string]: string } type assertions with safe optional-chaining extraction on Search and Search Detail Low Prevents runtime crash on undefined searchParams and incorrect behavior on duplicate query keys
3 CH-012 Add <h1> collection title to Search Detail page body (depends on CH-001 to avoid a third getCollection call) Low Provides user navigation context; adds SEO <h1> signal to collection pages
4 CH-013 Replace hardcoded SEO description on Home screen with a SITE_DESCRIPTION environment variable Low Replaces developer-facing template copy with store-specific SEO description
5 CH-004 Extract 5 into a named MAX_GALLERY_IMAGES constant in lib/constants Low Eliminates magic number; makes gallery size configurable from a single location

Phase 3: Medium-Term (90 Days)

Priority Finding ID(s) Action Effort Impact
1 CH-010 Add Vitest + @testing-library/react and Playwright to the project; implement priority test cases for generateMetadata fallback logic, notFound() guards, sort resolution, and productJsonLd construction Very High Establishes regression safety net before further refactoring; enables confident remediation of CH-001, CH-003, CH-008
2 CH-009 Verify infrastructure-level observability (Vercel, instrumentation.ts); if absent, integrate an error tracking SDK Medium Makes production errors visible to the engineering team without user reports
3 CH-011 Implement a skeleton loading state for the ProductDescription Suspense boundary Medium Prevents blank right-panel on slow connections; reduces perceived load time on the most commercially important page
4 CH-014, CH-015 Replace plain date string with <time datetime="..."> element and hardcode locale to 'en-US' on Item Detail Low Improves semantic correctness, accessibility, and date format determinism
5 CH-017 Pin exact canary version; document experimental flags; plan migration to stable Next.js 15 Low Reduces upgrade risk; documents intent of experimental features
6 CH-007 Verify Prose component sanitization (see CHV-001); add server-side HTML sanitization if absent Medium Closes defense-in-depth gap for merchant-authored HTML content

Phase 4: Long-Term (Backlog)

Priority Finding ID(s) Action Effort Impact
1 CH-005 Implement cursor-based pagination on Search and Search Detail using Shopify's pageInfo.hasNextPage / endCursor API Very High Prevents performance degradation as catalog grows; required for stores with >50 products per collection
2 CH-008 Introduce typed error classes in lib/shopify to distinguish "not found" from "API error"; update page components to handle each case appropriately High Improves incident detection; prevents 404 pages during Shopify API outages
3 CH-016 Replace <span className="font-bold"> with <strong> in Search results summary Low Minor semantic correctness improvement

Positive Patterns

Pattern Screen(s) Quality Dimension Description Recommendation
Consistent RSC-first architecture All five screens Performance Efficiency Every assessed screen is a React Server Component with no "use client" directive, contributing zero client-side JavaScript bundle weight for page-level logic. This is a deliberate and correct application of the Next.js App Router model. Maintain this pattern for all new page-level components. Reserve "use client" for interactive leaf components only.
Clean Shopify abstraction via lib/shopify All five screens Maintainability All Shopify Storefront API calls are routed through named functions in lib/shopify (getPage, getProduct, getCollection, getProducts, getCollectionProducts, getProductRecommendations). No screen directly constructs GraphQL queries or handles HTTP responses. Continue this pattern for any new Shopify API operations. Document the lib/shopify API surface for new developers.
Consistent notFound() guard pattern Item Detail, Product Detail, Search Detail Reliability All three screens that fetch a single Shopify resource apply a notFound() guard immediately after the fetch, before any rendering or metadata generation proceeds. This prevents broken UI states for missing resources and is applied consistently in both generateMetadata and the page component. Apply this same pattern to any future screens that fetch a single resource by identifier.
Two-tier SEO metadata fallback chain Item Detail, Product Detail, Search Detail Functional Suitability All three screens implement a consistent SEO fallback pattern: seo.titleresource.title and seo.descriptionresource.summary/bodySummary. This correctly models Shopify's content hierarchy and ensures SEO metadata is always populated. Document this pattern as the standard for all future Shopify-backed pages.
TypeScript strict mode with noUncheckedIndexedAccess All five screens Maintainability tsconfig.json enables strict: true and noUncheckedIndexedAccess: true, providing the strongest available TypeScript type safety. This catches a class of null/undefined bugs at compile time. Ensure strict and noUncheckedIndexedAccess are never disabled. The unsafe type assertions in CH-003 are the exception that proves the rule — they should be fixed to restore full type safety.
AVIF/WebP image optimization All five screens Performance Efficiency next.config.ts configures formats: ["image/avif", "image/webp"] for Next.js Image optimization, ensuring product images are served in modern, compressed formats. The remotePatterns configuration correctly scopes allowed image domains to cdn.shopify.com. Apply the same remotePatterns scoping discipline to any future image CDN integrations.
Partial Prerendering (PPR) enabled All five screens Performance Efficiency next.config.ts enables ppr: true, allowing Next.js to serve a static shell instantly from the CDN while streaming dynamic content. This is a deliberate architectural choice that improves Time to First Byte for all assessed routes. Ensure Suspense boundaries are placed correctly to maximize the static shell size as new components are added.
Responsive grid with CSS-only layout Search, Search Detail Performance Efficiency The grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 responsive grid is implemented entirely in CSS (Tailwind), with no JavaScript-based layout calculation. This is the correct approach for a server-rendered product grid. Use this same CSS grid pattern for any future product listing screens.
HIDDEN_PRODUCT_TAG soft-visibility mechanism Product Detail Security / Functional Suitability Products tagged with HIDDEN_PRODUCT_TAG are excluded from search engine indexing via robots metadata while remaining accessible by direct URL. This is a deliberate design choice that correctly separates SEO visibility from access control, allowing draft products to be previewed without being indexed. Document this mechanism in the developer onboarding guide so new developers understand the distinction between "hidden from search" and "access-controlled."
Geist font via CSS variable Root Layout Performance Efficiency The Geist font is loaded via GeistSans.variable and applied as a CSS variable on the <html> element, following Next.js's recommended font optimization pattern. This ensures the font is loaded with font-display: swap and does not block rendering. Apply the same CSS variable pattern for any additional fonts added to the project.

Screen Health Matrix

Screen Route Maintainability Reliability Security Performance Overall Finding Count
Home / Good Good Good Good Good 2
Item Detail /[page] Fair Fair Fair Good Fair 5
Product Detail /product/[handle] Fair Poor Fair Fair Fair 7
Search /search Fair Good Good Fair Fair 4
Search Detail /search/[collection] Fair Fair Good Fair Fair 5

Note: Finding counts per screen reflect the number of findings where the screen is listed as an affected screen. Findings affecting all five screens (CH-009, CH-010, CH-017) are counted once per screen in this matrix.


Glossary

Quality Terms

Term Definition
ISO/IEC 25010:2023 International standard defining a software product quality model with eight characteristics: Functional Suitability, Performance Efficiency, Compatibility, Usability, Reliability, Security, Maintainability, and Portability.
SQALE Software Quality Assessment based on Lifecycle Expectations — a method for classifying technical debt by type (Design, Code, Test, Doc, Dependency) and estimating remediation cost.
Blocker SonarQube severity: a defect that will cause production failure or data loss. Requires immediate fix.
Critical SonarQube severity: a severe code quality issue with high probability of causing bugs. Fix within current sprint.
Major SonarQube severity: a significant quality issue that degrades maintainability or reliability. Fix within 30 days.
Minor SonarQube severity: a code quality issue with limited impact. Fix within 90 days.
Info SonarQube severity: a best practice recommendation with no immediate risk. Consider for future work.
Analysability ISO 25010 sub-characteristic of Maintainability: the degree to which a system can be diagnosed for deficiencies or causes of failure.
Modifiability ISO 25010 sub-characteristic of Maintainability: the degree to which a system can be modified without introducing defects.
Reusability ISO 25010 sub-characteristic of Maintainability: the degree to which an asset can be used in more than one system.
Testability ISO 25010 sub-characteristic of Maintainability: the degree to which test criteria can be established and tests executed.
Fault Tolerance ISO 25010 sub-characteristic of Reliability: the degree to which a system operates as intended despite hardware or software faults.
Time Behaviour ISO 25010 sub-characteristic of Performance Efficiency: the degree to which response and processing times meet requirements.
Functional Completeness ISO 25010 sub-characteristic of Functional Suitability: the degree to which the set of functions covers all specified tasks.
Functional Correctness ISO 25010 sub-characteristic of Functional Suitability: the degree to which a system provides correct results.
Co-existence ISO 25010 sub-characteristic of Compatibility: the ability to perform required functions while sharing an environment with other systems.
Installability ISO 25010 sub-characteristic of Portability: the degree to which a system can be installed or uninstalled in a specified environment.
Design Debt Technical debt arising from architectural or design pattern issues that require structural changes to resolve.
Code Debt Technical debt arising from implementation quality issues (naming, duplication, unsafe patterns) that can be resolved with targeted code changes.
Test Debt Technical debt arising from missing or inadequate automated test coverage.
Doc Debt Technical debt arising from missing, outdated, or incorrect documentation.
Dependency Debt Technical debt arising from outdated, unstable, or risky third-party dependencies.

Domain Terms

Term Definition
Shopify Storefront API Shopify's public-facing GraphQL API for building custom storefronts. Provides read access to products, collections, pages, and cart operations. Requires a Storefront Access Token.
Collection A Shopify grouping of products, identified by a URL-safe handle string (e.g., "t-shirts"). Equivalent to a product category.
Handle Shopify's URL-safe identifier for resources (products, collections, pages). Derived from the resource title (e.g., "New Arrivals" → "new-arrivals").
HIDDEN_PRODUCT_TAG A Shopify product tag (constant from lib/constants) that marks a product as non-indexable by search engines while keeping it accessible by direct URL.
page.body The full HTML content of a Shopify Page, authored in Shopify's rich text editor and returned as an HTML string by the Storefront API.
page.bodySummary A plain-text excerpt of a Shopify Page body, used as a fallback for SEO meta description.
priceRange A Shopify object containing minVariantPrice and maxVariantPrice, each with amount and currencyCode. Represents the price spread across all product variants.
availableForSale A Shopify boolean field indicating whether any variant of a product is currently purchasable.
featuredImage The primary/hero image of a Shopify product, used for Open Graph tags, JSON-LD, and as the default display image. May be null for products with no images.
sortKey A Shopify Storefront API enum value determining the field by which products are sorted (e.g., PRICE, TITLE, BEST_SELLING, CREATED_AT).
defaultSort The fallback sort configuration from lib/constants, used when the ?sort= URL parameter is absent or unrecognized.
sorting An array of sort option objects from lib/constants, each containing a URL slug, sortKey, and reverse flag. Maps user-facing sort options to Shopify API parameters.

Technical Terms

Term Definition
React Server Component (RSC) A React component that renders exclusively on the server and ships zero JavaScript to the client. All five assessed screens are RSCs.
App Router The Next.js 13+ routing system based on the app/ directory, where page.tsx files define route segments. Used throughout this application.
generateMetadata A Next.js App Router export from a page file that returns a Metadata object used to populate <head> tags (title, description, Open Graph, etc.).
notFound() A Next.js function from next/navigation that interrupts rendering and triggers the nearest not-found.tsx boundary, returning a 404 HTTP response.
Partial Prerendering (PPR) An experimental Next.js feature (ppr: true) that serves a static HTML shell from the CDN instantly while streaming dynamic content into Suspense boundaries.
useCache An experimental Next.js caching API (useCache: true in next.config.ts) for fine-grained cache control in Server Components. Subject to change in canary builds.
React cache() A React 19 API that memoizes the result of a function call for the duration of a single server render pass, enabling deduplication of identical data fetches across generateMetadata and page components.
dangerouslySetInnerHTML A React prop that renders a raw HTML string directly into the DOM, bypassing React's XSS protections. Requires careful sanitization of the input string.
JSON-LD JavaScript Object Notation for Linked Data — a structured data format embedded in a <script type="application/ld+json"> tag that search engines use to generate rich search results.
AggregateOffer A schema.org type used in JSON-LD to represent a product available at a range of prices across variants.
Suspense A React primitive that shows a fallback UI while a child component's async work completes. Used on Product Detail to enable streaming HTML delivery.
Full Route Cache A Next.js App Router caching layer that stores the rendered HTML and RSC payload of a route on the server/CDN, enabling fast repeated page loads.
Turbopack The Rust-based Next.js bundler used in development (next dev --turbopack), replacing Webpack for faster HMR.
noUncheckedIndexedAccess A TypeScript compiler option that adds undefined to the type of array index access and object property access, catching potential null-dereference bugs at compile time.
Prose An internal component (components/prose) that renders a raw HTML string with typographic styling, following the Tailwind CSS prose utility class pattern.
GridTileImage A reusable image tile component (components/grid/tile) used in grid and list layouts, supporting a label overlay with title and price.
ProductGridItems A component (components/layout/product-grid-items) that maps over a products array and renders individual product cards in a grid layout.
Canary release A pre-release, unstable build of a software package. next: "15.6.0-canary.60" is a canary build; APIs may change without semver guarantees.