# Plush Widget JavaScript Runtime Reference

Native package: `packages/PlushWidgetRuntime`
Public docs: https://easypush.app/runtime.md
SDK/CLI: `npm install @easypush/plush`

Plush widgets are stored as canonical JSON payloads. JS, TS, JSX, and TSX are authoring layers that render back into that JSON. The iOS app and WidgetKit extension evaluate compiled JavaScript with JavaScriptCore through a local Swift Package Manager package.

## Swift Package

- Package path: `packages/PlushWidgetRuntime`
- App adapter: `Shared/PlushWidgetScriptRuntime.swift`
- Test command: `cd packages/PlushWidgetRuntime && swift test`

Main native APIs:

- `PlushWidgetRuntime(configuration:)`
- `renderWidget(_ request: PlushWidgetRenderRequest) throws -> PlushWidgetRenderResult`
- `PlushWidgetRenderRequest(widgetId, groupId, family, widgetJSON, source, compiled, allowFetch, allowKeychain, process, now, timeout)`
- `PlushWidgetRuntimeConfiguration(storageFactory, keychainFactory, fetcher, logger)`
- `PlushWidgetRuntimeStorage`
- `PlushWidgetRuntimeFetching`
- `PlushWidgetFetchRequest`
- `PlushWidgetFetchResponse`
- `PlushWidgetConsoleLog`

Storage implementations:

- `PlushWidgetMemoryStorage`
- `PlushWidgetScopedMemoryStorageProvider`
- `PlushWidgetUserDefaultsStorage`
- `PlushWidgetDisabledStorage`

Errors are explicit: `missingCode`, `contextCreationFailed`, `contextEncodingFailed`, `bootstrapException`, `scriptException`, `renderException`, `noReturnValue`, `rejected`, `timedOut`, `nonJSONString`.

## JavaScript Entry

Export a default function or return a widget value. The runtime calls it with `{ props, context, widget }`.

```tsx
export default function DeployWidget({ props }) {
  const progress = props.progress ?? 0.68;
  return (
    <Widget title="Deploy" openUrl="https://status.example.com">
      <VStack spacing={8}>
        <Text text="Deploy" weight="semibold" />
        <Badge icon="shield" label={props.environment ?? "production"} />
        <Progress label="Rollout" value={progress} displayValue={`${Math.round(progress * 100)}%`} />
      </VStack>
    </Widget>
  );
}
```

## JSX Components

- `Widget`
- `Text`
- `Markdown`
- `Badge`
- `Progress`
- `Gauge`
- `Metric`
- `Image`
- `Divider`
- `Spacer`
- `Stack` / `VStack`
- `Row` / `HStack`
- `Grid`
- `List`
- `Item`
- `Fragment`
- `jsx`, `jsxs`, `jsxDEV`

Common props: `foreground`, `color`, `accent`, `font`, `weight`, `size`, `lineLimit`, `maxLines`, `spacing`, `padding`, `alignment`, `openUrl`, `open_url`.

`Progress` and `Gauge` require numeric `value` or `progress` from 0 to 1. Visible percent text belongs in `displayValue` or `caption`.

## Web APIs

Available globals:

- `fetch(input, init)`
- `Headers`, `Request`, `Response`
- `Blob`, `ReadableStream`
- `URL`, `URLSearchParams`
- `localStorage`
- `keychain`
- `console.log/info/warn/error/debug`
- `process`

`Request` and `Response` support `text()`, `json()`, `blob()`, `arrayBuffer()`, `clone()`, and `body.getReader().read()`.

`localStorage` is scoped to widget/group. `keychain` is scoped and disabled unless `allowKeychain` is true. `fetch` is disabled unless `allowFetch` is true; disabled fetch returns a synthetic 403 response.

`process` contains runtime info such as `platform`, `runtime`, `osVersion`, and `isLowPowerModeEnabled`.

## Limits And Safety

- Do not include Plush API keys, callback secrets, auth headers, APNs tokens, or private user data in widget source.
- Keep widgets useful without network access.
- Treat fetch as best effort.
- Use localStorage only for scoped UI state.
- Enable keychain only for scripts that need scoped secret lookup.
- Keep fallback JSON under payload caps.
- Respect low power and reduced motion.

## Verification

Runtime change:

```sh
cd packages/PlushWidgetRuntime
swift test
```

App integration change: build/run the iOS app with a seeded scripted widget and inspect a real simulator/device screenshot. It must show JavaScriptCore-rendered content, not fallback JSON.

Site/docs change:

```sh
cd server
npm run check
```
