# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Two Long-Lived Branches: `main` and `rn0772`

This repo maintains **two parallel release lines** because Adalo ships runner to apps built on two different React Native versions:

|                          | `main`                         | `rn0772`                                                                                              |
| ------------------------ | ------------------------------ | ----------------------------------------------------------------------------------------------------- |
| Published package        | `@protonapp/proton-runner`     | `@protonapp/proton-runner-rn0772`                                                                     |
| React Native             | `>= 0.63.2` (legacy)           | `>= 0.77.2` (New Architecture)                                                                        |
| React                    | `>= 16.13.1`                   | `>= 18.2.0`                                                                                           |
| ESLint config            | `.eslintrc` (JSON, legacy)     | `eslint.config.mjs` (flat config)                                                                     |
| Native media/picker deps | Peer/stubbed via Metro aliases | Installed directly (`react-native-video`, `react-native-webview`, `react-native-media-console`, etc.) |
| Recent commit prefix     | `Fix:` / `feat:` / `docs:`     | `[RN0772][Fix]` / `[Rn0772][Feat]`                                                                    |

**Why both exist:** RN 0.63 is still shipping to apps that haven't migrated. RN 0.77.2 is the forward path (New Architecture, modern React 18). Some APIs diverge significantly — e.g. `boxShadow` exists in RN 0.77 but not 0.63, so shadow fixes land twice with different implementations (see PR #1100 on main vs #1103 on rn0772).

**Before making changes, know which branch you're on:**

```bash
git branch --show-current                    # the quickest check
grep '"name"' package.json                   # -rn0772 suffix == rn0772
grep react-native package.json | head -1     # 0.63.x == main, 0.77.x == rn0772
```

**When porting a fix across branches:** land it on the branch where the bug was reported first, then open a follow-up PR cherry-picking (or re-implementing) on the other branch. Don't assume a fix on one works on the other — behavior often differs at the RN layer. The recent shadow-support work (`docs/superpowers/plans/2026-03-30-fix-shadow-support-old-rn.md`) is a good example: the RN 0.77 version uses `boxShadow`, the RN 0.63 version uses `elevation` + `backgroundColor`.

**This file lives on both branches** — keep it synced. If you change it on one, open a parallel PR to the other.

## Repository Purpose

Runner is the core rendering engine for the Adalo no-code platform. It consumes an app "body" (JSON definition of screens, components, data bindings, and actions) and renders it as a live React Native / React Native Web app. It is published as a library and consumed by `web-preview`, `web-runtime`, and the native mobile shells — **it has no standalone entry point or dev server**.

## Build & Development Workflow (same on both branches)

- `yarn start` / `yarn watch` — Babel in watch mode, compiling `src/` → `lib/` so consumers symlinked via `yarn link "<package-name>"` see updates live. This is the primary dev loop; there is no server to hit in-browser from inside this repo.
- `yarn compile` — one-shot Babel build to `lib/`. Source maps and non-JS files are copied; `*.test.*` files are excluded.
- `yarn postinstall` — deletes `node_modules/react` (to force use of the host app's React) and runs `patch-package` to apply patches from `patches/`. If React resolution errors appear after install, re-run this.

## Testing, Types, and Linting (same on both branches)

- `yarn test` — Jest (jsdom env). `yarn test:watch`, `yarn test:coverage`, `yarn test:clearCache` available.
- Run a single test file: `yarn test src/utils/__tests__/actions.test.js`
- Run a single test by name: `yarn test -t "substring of test name"`
- `yarn types:check` — `tsc --noEmit` against `tsconfig.json` (baseUrl is `./src`, so imports like `utils/foo` resolve).
- `yarn lint` (auto-fix) / `yarn lint:check` — ESLint over `src/**/*.{js,ts,tsx}`. Rule set differs between branches (see table above), but the commands are the same.
- `yarn format` / `yarn format:check` — Prettier + ESLint. CI runs `prettier:check`, `lint:check`, `types:check`, and `test` as separate jobs; all must pass.
- `yarn test:all` — local pre-push shortcut for `format:check && test`.

**Important:** `yarn prettier` runs against `./**`, so a bare `yarn prettier` will reformat the entire repo, not just the file you care about. To format a single file, use `yarn prettier --write path/to/file` (Prettier picks up the config; CLI flag overrides the script's glob).

## Architecture: How Runner Actually Works

### Entry point and host integration

`src/index.js` exports a `RunnerWrapper` class component that hosts the entire runtime. The host app (web-preview / web-runtime / mobile shell) passes in: the `app` body, URL getters (`baseURL`, `baseAPIURL`, `uploadsBaseURL`, `assetsBaseURL`, etc.), a `libraries` map of dynamically loaded marketplace components, feature flags, and transition/layout config. These are passed down as **legacy React context (`childContextTypes`)** — this pattern is widespread in this repo; don't migrate it casually, many components consume it via `contextTypes`.

`RunnerWrapper` → `<Provider>` (its own Redux store from `ducks/`) → `<PersistGate>` → `<PortalProvider>` → `Runner` (`src/components/index.js`) → `Navigator` → `Screen` → `ObjectRenderer` → individual components / `LibraryComponent` for marketplace components.

### Redux (`src/ducks/`)

A self-contained store is created inside `RunnerWrapper` via `getStore()`. Reducers: `data`, `auth`, `formInputs`, `notifications`, `toasts`, `random`, `location`, `pushTreeMap`, `images`. Persisted with `redux-persist` + AsyncStorage; `data`, `toasts`, `location`, `pushTreeMap`, `images` are **blacklisted from persistence**. Middleware: `redux-thunk` + `redux-promise-middleware`. Consumers can grab the store via the `getStore` context function (exposed on RunnerWrapper).

### Data binding & dependencies

The most intricate part of the codebase. `src/utils/dependencies.js` (~1200 lines) plus `src/utils/dependencies/` compute, for a given screen/component, which pieces of backend data must be fetched, how to filter/sort them, and how to feed values into component props. `ducks/data.js` handles the actual fetch/cache of collection data. Changes here are high-risk — always add/extend tests in `src/utils/__tests__/dependencies*` and `src/utils/__tests__/dataBindings/`.

### Actions

User-triggered actions (navigation, create/update/delete record, custom API calls, auth flows, etc.) are pipelined through `src/utils/actions.js` + `action-classes.js` + `action-filtering.js`. They consume the same dependency graph to resolve input values.

### Platform-specific files

Babel's `module-resolver` (see `babel.config.js`) + Jest's `moduleFileExtensions` resolve in this order: `.ios.ts(x)`, `.android.ts(x)`, `.web.ts(x)`, `.ts(x)`, `.jsx`, `.ios.js`, `.android.js`, `.web.js`, `.js`. When adding a web-specific implementation, create a `foo.web.ts` alongside `foo.ts` — the web host picks up the `.web` variant automatically. Examples: `alerts.web.js`, `linking.web.js`, `sharing.web.js`, `uploads.web.js`, `user-locale.web.ts`.

### Metro config and native-only deps (behavior differs by branch)

`metro.config.js` blocks and aliases to empty stubs a set of native-only packages (`@protonapp/runner-webview`, `runner-filepicker`, `runner-imagepicker`, `react-native-video`, `react-native-media-console`). This lets the same source build for web where those modules aren't installed.

- On **`main`**, those media/picker packages are **not** direct dependencies — the stubs are the only resolution for web, and native hosts provide their own implementations via the `@protonapp/*` aliases.
- On **`rn0772`**, several of those packages (`react-native-video`, `react-native-webview`, `react-native-media-console`, etc.) **are** direct dependencies. The Metro stubs are still present as a safety net, but the real implementations ship with the package.

If you add a native-only dependency, add a `.web.js` stub and (if needed) a Metro alias — on both branches.

## Coding Conventions to Respect

- **Date/time: no direct `luxon` or `moment` imports.** ESLint blocks them on both branches. Use utilities from `src/utils/luxon/index.ts`. The only files allowed to import `luxon`/`moment` directly are `src/utils/luxon/datetime.ts(.test.ts)` and `src/utils/moment-locale.ts`.
- Mix of `.js`, `.ts`, and `.tsx` — `allowJs: true`. New files should be TypeScript; don't mass-convert existing ones as part of unrelated work.
- `strict: true` in `tsconfig.json`. Path imports are baseUrl-relative (`utils/...`, `ducks/...`, `components/...`), enabled by both `tsconfig.baseUrl` and the Babel `module-resolver` root `src`.
- `console.*` is allowed in source (eslint `no-console: off`) but stripped in production builds via `babel-plugin-transform-remove-console`.
- Node 18 (`.nvmrc`) on both branches. **Don't upgrade React inside this repo** — it's deliberately deleted post-install so the consumer's copy is used.
- Match the commit-message style of the branch you're on: plain `Fix:` / `feat:` on `main`, `[RN0772][Fix]` / `[Rn0772][Feat]` on `rn0772`.

## Debugging

- **Data load diagnostics:** set LocalStorage key `debugStorage` on the previewer/share host — runner will log tables of collection loads to the console. Useful when investigating over-fetching of Adalo collections / Xano / external DBs.
- **why-did-you-render:** not installed here (breaks the build); lives in `web-preview` and is enabled with `yarn start:wdyr` there. For anonymous function components, attach `Component.whyDidYouRender = { logOnDifferentValues: true, customName: 'Name' }` so logs are readable.
