# Cloudflare Worker catalog relay for Android TV catalog apps

Use this reference when adding an out-of-the-box catalog service to an Android streaming/catalog app while keeping third-party API keys out of the APK.

## Durable pattern

- Keep user/system addon catalogs first. The official relay is a fallback, not a replacement.
- App fallback chain should be:
  1. User/system Stremio/addon catalogs
  2. First-party catalog relay
  3. Direct local/API-key fallback, if configured
- Put provider keys only in backend secrets. Do not commit keys, `.dev.vars`, `.env`, generated `BuildConfig`, or build outputs.
- Make backend catalog logic portable: keep Worker-specific routing/cache/env in `src/index.ts`; put reusable logic in `src/core/*`.

## Worker skeleton shape

```text
services/catalog-worker/
  src/index.ts              # Cloudflare adapter: routes, Cache API, env bindings
  src/core/catalogs.ts      # declarative catalog registry
  src/core/tmdb.ts          # provider client + response mapping
  src/core/keyRing.ts       # comma-separated multi-key parsing/selection
  src/core/models.ts        # DTO/domain types
  wrangler.toml.example
  package.json
```

Recommended endpoints:

```text
GET /v1/health
GET /v1/catalog/{type}/{catalogId}?skip=0&limit=20
```

Response should map directly to the app's preview model and include `items`, `hasMore`, `source`, and `cacheTtlSeconds`.

## Cloudflare deployment notes

- Use a scoped API token, never the account password.
- For `workers.dev`, token permissions generally need:
  - Account → Workers Scripts → Edit
  - Account → Account Settings → Read
- Store secrets with Wrangler:

```bash
printf '%s' "$TMDB_API_KEYS" | npx wrangler secret put TMDB_API_KEYS
printf '%s' "$MDBLIST_API_KEYS" | npx wrangler secret put MDBLIST_API_KEYS
npx wrangler deploy
```

- After a token is pasted into chat or logs, tell the user to revoke it immediately after deployment.

## Cloudflare Worker runtime pitfall: illegal invocation

Do not pass the global `fetch` function as a bare method reference into helper objects. Cloudflare Workers can throw:

```text
Illegal invocation: function called with incorrect `this` reference
```

Wrong:

```ts
await getCatalogRow(env, { fetch }, type, catalogId, extra)
```

Right:

```ts
await getCatalogRow(
  env,
  { fetch: (input, init) => fetch(input, init) },
  type,
  catalogId,
  extra,
)
```

## Worker Cache API typing pitfall

Cloudflare/TypeScript versions can disagree about `caches.default`. If `tsc` complains, use a narrow cast in the Worker adapter only:

```ts
const cache = (caches as unknown as { default: Cache }).default
```

Keep this Cloudflare-specific cast out of portable `src/core/*`.

## URLSearchParams portability pitfall

If the configured TS libs do not expose `URLSearchParams.entries()`, use `forEach`:

```ts
const extra: Record<string, string> = {}
url.searchParams.forEach((value, key) => {
  extra[key] = value
})
```

## Android integration notes

- Add a `NEXSTREAM_CATALOG_BASE_URL` Gradle/env config and default it to blank, not a hardcoded secret-bearing URL.
- Repository should cleanly disable itself when base URL is blank so local/direct fallback still works.
- Catch only recoverable network/protocol/serialization failures in the relay repository:
  - `CancellationException` → rethrow
  - `IOException` → empty fallback
  - `HttpException` → empty fallback
  - `SerializationException` → empty fallback
- Do not catch broad `Exception` around Retrofit/OkHttp unless deliberately at a high-level UI boundary.

## Verification gates

```bash
cd services/catalog-worker
npm run typecheck

cd ../..
./gradlew compileDebugKotlin --console=plain
./gradlew assembleDebug testDebugUnitTest --console=plain
```

Live endpoint smoke tests:

```bash
curl -fsS "$CATALOG_BASE_URL/v1/health"
curl -fsS "$CATALOG_BASE_URL/v1/catalog/movie/tmdb.trending_week?skip=0&limit=5"
```

When using local Gradle secrets, remember generated debug `BuildConfig` and build intermediates may contain local secrets. Before reviewing diffs or committing, clean/reset generated build outputs and inspect only source/config files.
