Skip to content

React Router authenticate.admin helper + Resource Picker UI mock - #36

Open
fazlulkabir34 wants to merge 31 commits into
ctrlaltdylan:mainfrom
bevycommerce:main
Open

React Router authenticate.admin helper + Resource Picker UI mock#36
fazlulkabir34 wants to merge 31 commits into
ctrlaltdylan:mainfrom
bevycommerce:main

Conversation

@fazlulkabir34

Copy link
Copy Markdown

Summary

This branch brings two main upgrades to mock-bridge: first-class support for Shopify App React Router admin authentication in tests, and a fully mocked shopify.resourcePicker flow with an admin-frame modal, catalog API, and stronger bridge integration—so automated browser tests and MCP-style tooling can exercise picker UX without hitting Shopify.

What’s included

React Router admin auth — withMockBridgeAdminAuthForReactRouter (and related exports/build artifacts) so apps using authenticate.admin(request) can run against the mock bridge with clearer wiring and types.
Resource Picker — Replaces the previous stub with a mock modal UI in the admin frame, store/state for open/close/search/selection, variant-aware catalog behavior, and improvements around focus management and error handling.
Server/catalog integration — Mock catalog backed by /api/resource-picker-catalog (documented); customizable via resourcePickerCatalog on the server where applicable.
App Bridge layer — Updates to feature wiring (invokeFeature, resource-picker bridge path, loading tweaks) so the picker behaves consistently with the rest of the mock.
Docs — New guides: docs/SHOPIFY_APP_REACT_ROUTER.md, docs/RESOURCE_PICKER.md; README feature matrix updated (resource picker marked as mock UI); small related edits to backend integration and Remix docs.

fazlulkabir34 and others added 30 commits March 25, 2026 02:52
- Add withMockBridgeAdminAuthForReactRouter under @getverdict/mock-bridge/react-router
- Wire package.json exports for ., ./auth, ./react-router, ./client
- Optional peerDependencies for @shopify/shopify-app-react-router and @shopify/shopify-api
- Document SHOPIFY_APP_REACT_ROUTER, BACKEND_INTEGRATION caveat, README link
- tsconfig paths for Shopify package subpath typings under classic moduleResolution
…kage exports

- Introduced withMockBridgeAdminAuthForReactRouter for enhanced admin authentication in React Router apps.
- Updated package.json to remove deprecated react-router export and streamline exports.
- Enhanced documentation to reflect changes in integration instructions for Shopify App React Router.
- Adjusted type exports for better clarity and consistency.
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Removed redundant mention of `@getverdict/mock-bridge/auth` in the Shopify App React Router section.
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
chore: update package-lock.json and tsconfig.json
- Added ResourcePicker component for selecting products, variants, and collections.
- Updated App component to include ResourcePicker.
- Enhanced mock bridge to handle resource picker actions.
- Updated package.json and package-lock.json to include new dependencies for React and related types.
- Modified README.md to reflect the new ResourcePicker functionality.
- Updated tsconfig.json to include new type definitions for resource picker.
…d functions

- Enhanced ResourcePickerCatalogResponse to include variants.
- Updated getDefaultResourcePickerCatalog to return variants using flattenVariants.
- Modified mergeResourcePickerCatalog to handle variants in the merge process.
- Adjusted server logic to utilize the new variants structure.
- Removed unnecessary useCallback for toggleId in ResourcePickerModal.
- Simplified onChange handlers for buttons in ResourcePickerModal.
- Introduced fetchCatalog function to centralize catalog fetching logic and improve error handling.
- Updated closedPickerState to include loading state.
- Enhanced getFeatureStore to throw an error if a feature is not found.
- Refactored flattenVariants function for better readability and performance.
… improvements

- Added focus management for the search input in ResourcePickerModal to improve accessibility.
- Implemented keyboard navigation within the dialog to enhance user experience.
- Updated the ResourcePicker state to include selectionRowById for better tracking of selected items.
- Refactored fetchCatalog to handle aborting previous requests and improved error handling.
- Adjusted the mock bridge to support new features and ensure robust communication with the resource picker.
Syncs 5 months of upstream dependabot bumps into the fork, which had
diverged since 2eb5394 (upstream PR ctrlaltdylan#24) with the React Router admin
auth helper and the mock ResourcePicker.

Upstream brings (all dependency bumps, no source changes):
- follow-redirects 1.15.11 -> 1.16.0
- admin-frame: vite 5.4 -> 7.3.1, esbuild 0.27.3
- uuid 9.0.1 -> 14.0.0

Conflict resolution (package.json dependencies):

Upstream's uuid 14 is ESM-only: "type": "module" with no `require`
condition in its exports map. This package compiles to CommonJS
("module": "commonjs"), so the published dist does `require('uuid')`,
which only works on Node >= 22.12 via require(esm). Since package.json
declares "engines": { "node": ">=18.0.0" }, taking uuid 14 verbatim
would break consumers on Node 18/20 with ERR_REQUIRE_ESM.

uuid 9 is also deprecated upstream ("uuid@10 and below is no longer
supported"), so it is not a durable resting place either.

Resolved to uuid ^11.1.1, which ships a real CommonJS build
(exports["."].node.require -> ./dist/cjs/index.js), is not deprecated,
and declares no engines floor. The only API used is `v4`
(src/auth/token-generator.ts, app-bridge/src/invokeFeature.ts), which
is unchanged across 9 -> 11.

Also drops @types/uuid ^9.0.7: uuid 11 ships its own .d.ts and tsc
passes without it.

Verified: tsc --noEmit clean, full build passes on vite 7,
`require('uuid').v4()` works at runtime, app-bridge bundle rebuilt.
Vite 7.3.1 (pulled in by the upstream sync) declares
engines.node ^20.19.0 || >=22.12.0, but nothing in the repo stated a Node
requirement anywhere.

Deliberately does NOT raise the root package's engines.node from >=18.0.0.
That field is the runtime contract for consumers of the published package,
and Node 18 genuinely satisfies it: no runtime dependency has a floor above
18 (the highest is http-proxy-middleware at >=18.0.0), and the published
tarball ships only prebuilt dist/, assets/, admin-frame/dist/ and
app-bridge/dist/, so consumers never install or execute Vite. Raising it to
22 would emit EBADENGINE -- or hard-fail under engine-strict -- for every
Node 18/20 consumer, over a build tool they never run.

Instead the constraint is recorded where it is actually true:

- admin-frame/package.json gains engines.node matching Vite's own range.
  admin-frame is "private": true, so this constrains contributors only and
  has no effect on published consumers.
- README documents both numbers separately: Node 18+ to use the package,
  ^20.19.0 || >=22.12.0 to build from source (including git-ref installs,
  where `prepare` runs the full build).
- CLAUDE.md notes the build requirement next to the build commands.

Verified: full `npm run build` passes, root engines.node unchanged.
app-bridge/src/invokeFeature.ts imports `uuid`, but app-bridge/package.json
declared no uuid at all -- only @shopify/app-bridge-types, esbuild and
typescript. The import resolved purely by accident, via hoisting from the
root node_modules, and app-bridge/package-lock.json had zero uuid entries.

This is a real failure, not a theoretical one. Building app-bridge in
isolation from the root (its src + package.json + lockfile, npm install,
npm run build) fails:

  X [ERROR] Could not resolve "uuid"
      src/invokeFeature.ts:1:29:
        1 | import { v4 as uuidv4 } from 'uuid';

The same isolated build succeeds after this change.

Declared in devDependencies rather than dependencies: app-bridge is never
installed as a package by anyone -- esbuild inlines uuid into a self-contained
IIFE, and only the built dist/index.js ships (via the root package's `files`).
uuid is therefore a build-time input, exactly like the esbuild and typescript
entries beside it. An --omit=dev install could not build this package anyway,
since esbuild itself is a devDependency, so nothing is lost by the placement.

Pinned to ^11.1.1 to match the root, which moved to uuid 11 in the upstream
sync for CommonJS compatibility.

Verified: isolated standalone build fails before / passes after, full
`npm run build` passes, and the emitted bundle is byte-identical.
chore: sync upstream ctrlaltdylan/mock-bridge (resolve uuid ESM conflict)
fix: declare uuid as an explicit dependency of app-bridge
The patched `window.fetch` attaches `Authorization: Bearer <currentSessionToken>`
to every request, but `currentSessionToken` was only ever assigned inside the
SESSION_TOKEN_RESPONSE handler — so it stayed null until the app called
`shopify.idToken()`. App Bridge v4 apps typically never call it, because the real
App Bridge hands them an already-authenticated fetch.

Every data request therefore went out unauthenticated. In an embedded Remix or
React Router app that means the host bounces client-side loader fetches to the
session-token page: navigation lands on the app's own login route, and on-mount
API calls try to parse that HTML as JSON.

Seed `currentSessionToken` from the `id_token` the host already puts in the
iframe URL, and start the refresh loop for the `shopify` global rather than only
inside `createApp()` — the legacy v2 entry point a v4 app never calls.

Also adds `sessionTokenTtlSeconds` so tokens can outlive a long-running test.
The default stays 60s to match real Shopify, and the refresh interval derives
from it (half the TTL, clamped to 5–30s).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Yarn 1 does not run a git dependency's `prepare` script. It clones the repo
as-is, so `build:admin-frame` never runs, `admin-frame/dist` is absent, and the
`express.static()` mount serves nothing — the embedded iframe renders blank.

npm consumers are unaffected: npm does run `prepare` and packs per `files`.
Committing the output makes both package managers behave identically and removes
a build-on-install from consumer CI.

Un-ignores `dist/` at the root too, matching the tracked state of `dist/` and
`app-bridge/dist/`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…rmup

fix: hold a session token before the app asks for one, + make yarn git installs work
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant