> For the complete documentation index, see [llms.txt](https://basedapp.gitbook.io/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://basedapp.gitbook.io/docs/integrations/mini-apps-platform/10-developer-experience.md).

# Developer experience

The platform succeeds or fails on whether partners enjoy building mini-apps. This document covers the partner-facing surface: CLI, project template, local dev loop, simulator, type packages, docs site.

## Languages and toolchain

Mini-apps are **required** to be written in TypeScript. The CLI rejects pure-JS bundles at `based build`. The published output is ES2020 JS (TS is compiled away by esbuild), but the source-of-truth is TS — `tsconfig.json` is part of the project template and `@based/sdk-types` is a required dev dependency.

We officially support:

* TypeScript 5.x (required; minimum 5.4)
* Optional JSX preset for `ui.*` builders, so partners can write `<Button label=... />` if preferred. JSX compiles to `ui.Button({ label: ... })`. **Off by default**; opt in via `based init --jsx`.

## `based-cli`

The partner-facing CLI is a single Node binary distributed via npm:

```
npm install -g @based/cli
based login                # browser-based OAuth to platform dashboard
based init my-mini-app     # scaffold a project
cd my-mini-app
based dev                  # start dev server + simulator host
based build                # produce unsigned .basedapp
based publish              # upload to platform CI for sign + review
based versions             # list versions of your mini-app
based promote 1.4.2 stable # promote a beta to stable (queues review)
based logs --tail           # tail telemetry/error stream for your mini-app
```

Auth uses an OAuth flow against the partner dashboard. Tokens are scoped per mini-app id.

## Project template (`based init`)

Generated structure:

```
my-mini-app/
├── package.json
├── tsconfig.json
├── based.json              # manifest, with sane dev defaults
├── src/
│   ├── App.ts              # entry — exports default { App, onMount, … }
│   ├── screens/
│   │   └── Home.ts
│   └── api.ts              # partner backend client
├── locales/
│   ├── en.json
│   └── ja.json
├── assets/
│   ├── icon.png            # placeholder
│   └── splash.png
├── .basedignore
└── README.md
```

`package.json` has:

```json
{
  "scripts": {
    "dev": "based dev",
    "build": "based build",
    "test": "based test"
  },
  "dependencies": {
    "@based/sdk": "^1.0.0"          // the runtime helpers (ui builders, signal, t)
  },
  "devDependencies": {
    "@based/sdk-types": "^1.0.0",   // generated TS types for based.*
    "@based/cli": "^1.0.0",
    "typescript": "^5.4.0"
  }
}
```

## Local dev loop

```
$ based dev
[based] Bundling src/App.ts → .based/dev/main.js          (412 ms)
[based] Watching src/**, locales/**, assets/**, based.json
[based] Dev server  on  http://localhost:7373
[based] Simulator   on  ws://localhost:7373/ws

  Open the Based app on a connected device, then:
    iOS:     Settings → Developer → Mini-App Dev → paste URL
    Android: Settings → Developer → Mini-App Dev → scan QR
    Or:       open  basedapp://dev?url=http%3A%2F%2Flocalhost%3A7373
```

The dev server:

1. Watches source files; rebundles on change.
2. Pushes a notification over a websocket to any connected host.
3. Serves the bundle and source maps from `.based/dev/`.
4. Bypasses signature verification (host enforces dev-mode signed-out).
5. Logs every `based.*` call from the connected host with arguments and results.

The host running in **dev mode** subscribes to the websocket and reloads on push. State preservation across reloads is opt-in.

## Debug error overlay

Mini-app development often produces Flutter framework errors that bury themselves in console logs (the most common is the `semantics.parentDataDirty` assertion fired by certain `Row` + `Expanded` patterns inside `Card`/`Padding`). The host surfaces these on screen via a debug-only overlay so the developer doesn't have to grep stack traces to find what broke.

### Architecture

Three pieces, all under `flutter/based_app/lib/presentation/screens/mini_apps/`:

| Component                                             | Responsibility                                                                                                                                                                                                                                                                                                             |
| ----------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `mini_app_error_bus.dart` (`MiniAppErrorBus`)         | Singleton `ChangeNotifier` that captures every `FlutterErrorDetails` raised while the mini-app is mounted. Coalesces consecutive identical errors (Flutter assertions can fire every frame until the offending widget unmounts). Caps history at 50 entries.                                                               |
| `mini_app_error_overlay.dart` (`MiniAppErrorOverlay`) | Stateful widget that listens to the bus. Empty bus → renders nothing. Non-empty → red banner pinned to the bottom; tap to expand into a scrollable list of every captured error with summary + filtered stack frames. **Short-circuits to `SizedBox` in non-debug builds** (`!kDebugMode`), so release has zero footprint. |
| `_MiniAppErrorBoundary` (in `mini_app_screen.dart`)   | While mounted, scopes `ErrorWidget.builder` to a contained crash tile and chains `FlutterError.onError` to push into the bus. Restores the previous handlers on unmount.                                                                                                                                                   |

### Lifecycle

* **Bus is cleared** on `_boot()` (start of every mini-app session) and on `dispose()` (pop back). Errors are session-scoped — a fresh launch starts clean.
* **Errors are captured globally** via the chained `FlutterError.onError`, but only while a `MiniAppScreen` is mounted (because `_MiniAppErrorBoundary` installs and uninstalls the chain on its lifecycle).
* **Forwarding is preserved**: the previous handler (Sentry, console logging, etc.) is still called after the bus push, so captured errors don't disappear from your normal pipeline.

### Stack-frame filtering

The expanded view filters Flutter internals out of each stack and shows only frames that match:

* `package:based_ui_nodes/`
* `package:based_js_runtime/`
* `package:based_mini_app_sdk/`
* `package:based_app/services/mini_apps`
* `package:based_app/presentation/screens/mini_apps`

If no frame matches (e.g. a low-level layout assertion deep in `package:flutter/`), it falls back to showing the top three raw frames.

### Visibility envelope

| Build mode        | Capture            | Overlay rendered?        |
| ----------------- | ------------------ | ------------------------ |
| `kDebugMode`      | yes                | yes                      |
| Profile / release | yes (history kept) | no — `SizedBox.shrink()` |

The overlay never replaces the renderer; it stacks on top. If a layout error nukes the mini-app subtree, you still see the framework's own red error widget *plus* the overlay's banner with the assertion message and stack.

### What the overlay does NOT do

* It does not catch errors **thrown in JavaScript** (App() throwing, event handler throwing). Those are caught upstream by the JS shim's try/catch and re-emitted via `MiniAppHostException` — they show up in the host's normal error UI, not the overlay.
* It does not catch **layout-phase errors per widget**. Flutter's `parentDataDirty`-class assertions bubble through `FlutterError.onError` globally — there's no clean per-widget try/catch for them. The overlay shows them, but they still trip the framework's frame-abort behaviour.
* It does not surface errors raised **outside the mini-app screen** (host home, settings, etc.). The error chain is installed only while `_MiniAppErrorBoundary` is mounted.

### Extending it

If you want to forward overlay errors to telemetry or a dev-time websocket so partners running their own host see them in the CLI's `based dev` log:

```dart
MiniAppErrorBus.instance.addListener(() {
  for (final e in MiniAppErrorBus.instance.history) {
    devChannel.send({'type': 'flutter_error', 'message': e.details.exceptionAsString()});
  }
});
```

This is the natural integration point for `based logs --tail`.

## Simulator

There are two simulator paths. We support both because they cover different needs.

### Path A — Connected device (recommended)

The partner runs the actual Based host on iOS Simulator / Android Emulator / a physical device, and points it at `localhost:7373`. This is the most realistic environment.

### Path B — Standalone simulator

`based-cli` ships with a **headless mini-host**: a Flutter desktop app that embeds the runtime and renderer with a stub `BasedMiniAppHost`. It runs on macOS/Linux/Windows.

```
$ based sim
[based] Launching simulator at  http://localhost:7374
```

What the standalone simulator gives you:

* All `based.ui.*` widgets render natively on desktop.
* `based.signing.*` calls are stubbed with a clearly-labeled fake signing sheet that returns canned signed payloads.
* `based.http.*` works against real domains in your manifest.
* Theme switcher: flip tenant, dark/light, locale.
* Permission inspector: see every call, denial reason, decoded args.
* Rate-limit and CPU-budget visualizations.

The standalone simulator never connects to a real wallet. It is for UI/UX development and integration testing of the partner's logic, not for verifying signing flows. For those, use Path A on a real device with a test wallet.

## Type packages

Two npm packages are published by the platform:

| Package            | Contents                                                                                                | Source                                            |
| ------------------ | ------------------------------------------------------------------------------------------------------- | ------------------------------------------------- |
| `@based/sdk-types` | TS declarations for `based.*`, all data types, error codes.                                             | Generated from `BasedMiniAppHost` Dart interface. |
| `@based/sdk`       | Runtime helpers: `ui.*` builders, `signal`/`computed`, `t()`, `formatNumber()`. Pure JS, no host calls. | Hand-written.                                     |

Versioning:

* `@based/sdk-types` major matches the SDK's major: `@based/sdk-types@1.x` is for SDK 1.x.
* `@based/sdk` matches similarly.
* Partners pin major; the CLI warns when a new minor is available.

## Editor experience

We do not ship our own editor. We ship enough type info that VS Code (or any TS-aware editor) just works:

* Full autocomplete on `based.*` driven by `@based/sdk-types`.
* Hover docs (TSDoc) include the same description as the Dart interface, plus the required permission for each method (auto-injected by codegen).
* A bundled VS Code snippet pack (`based.snippets.json`) ships with the CLI; running `based init` proposes installing it.
* For IntelliJ users, the type info works out of the box; no extra plugin.

A future `@based/eslint-config` is planned but not v1.

## Testing

Partners test mini-apps at three levels:

| Level       | Tool                                                      | Coverage                                                      |
| ----------- | --------------------------------------------------------- | ------------------------------------------------------------- |
| Unit        | Jest / Vitest, via `based test`                           | Pure JS logic, builders, helpers. Stub `based.*` is provided. |
| Integration | Playwright-style harness against the standalone simulator | UI flows, deterministic signing stubs.                        |
| Manual      | Path A connected device                                   | Real signing, real network, edge cases.                       |

The `based test` runner is just Jest pre-wired with the `based.*` stub. We don't reinvent test infrastructure.

## Partner docs site

The docs site is a first-class platform deliverable. It lives at `https://miniapps.based.app/docs` and is built from the same source-of-truth artifacts as the SDK:

| Page set           | Source                                                                  | Notes                                                                          |
| ------------------ | ----------------------------------------------------------------------- | ------------------------------------------------------------------------------ |
| Getting started    | hand-written in `docs/mini-apps/site/getting-started/`                  | Five-page walkthrough: install CLI → init → run → sign one tx → publish.       |
| API reference      | generated from `BasedMiniAppHost`                                       | Mirrors the doc-comments on the Dart interface. Permission badges, type links. |
| Widget catalog     | generated from the `UiNode` catalog with live previews via `widgetbook` | Each widget has live knobs, dark/light/tenant variants.                        |
| Manifest reference | hand-written + JSON Schema                                              | Linked validation against `based.json`.                                        |
| Concepts           | hand-written                                                            | Threat model summary, performance budgets, lifecycle, theming.                 |
| Recipes            | hand-written                                                            | "Stake into a vault", "Display order book", "Cross-tenant theming"…            |
| Changelog          | generated from SDK release notes                                        | Per-major migration guides.                                                    |

Tech stack: Docusaurus or Mintlify (TBD; tracked in `12-open-questions.md`). Live widget previews use `widgetbook` exported as a web app and embedded in iframes.

## Submission and review

Partner submission flow:

```mermaid
flowchart LR
    PUB["based publish 1.4.2"]
    UP["Upload to CI"]
    AC["automated checks"]
    Q["enter review queue"]
    DASH["checks panel in dashboard"]
    PUB --> UP --> AC
    AC -- "pass" --> Q
    AC -- "fail" --> DASH
```

Review SLAs (proposed):

* First-time submission: up to 5 business days, manual review.
* Subsequent submissions with no permission diff: 1 business day, mostly automated.
* Permission additions or new domains: up to 3 business days, manual review.
* Hotfix channel for urgent security fixes: 24 hours, expedited review.

A submission status page in the partner dashboard shows the queue position, reviewer notes, and any required changes. Rejections are itemized.

## Partner dashboard

Out of scope for this doc to fully spec, but the dashboard provides:

* Overview of installed mini-apps, versions, rollout state.
* Telemetry: boot success rate, error rate, p95 boot time, MAU.
* Logs: tail of error/permission/console events.
* Channel management: stable/beta/internal promotion controls.
* Domain management: registered network domains, deep link prefixes.
* Team and key management: signing key rotation, team members.

## Migration from older SDK versions

When a partner targets an older SDK and the host has moved on:

* The CLI warns at `based build` if `targetSdkVersion` is more than one minor behind.
* The CLI provides a `based migrate <fromVersion>` command that applies known mechanical migrations.
* The docs site has a migration page per minor version with manual steps.

Partners are not forced to upgrade — compat shims keep them running. But the warning ensures they know.

Read [`11-roadmap-and-milestones.md`](/docs/integrations/mini-apps-platform/11-roadmap-and-milestones.md) next.
