Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 32 additions & 35 deletions .agents/skills/ts-js-validation/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,50 +1,46 @@
---
name: ts-js-validation
description: Validate TypeScript and JavaScript changes in React Native Firebase by running the root prepare, TypeScript compile, API reference, Jest, formatting, and compare-types scripts. Use when a developer updates TS/JS code and wants the standard RNFB validation pass before handing off or committing.
description: Validate React Native Firebase changes by running the CI-equivalent prepare, TypeScript, lint, Jest, formatting, and compare-types scripts. Use before handoff, commit, or push when package sources, native bridge code, or docs changed.
metadata:
owner_team: react-native-firebase
maintainer: russell.wheatley
status: draft
tags: typescript,javascript,testing,validation,formatting,react-native-firebase
last_reviewed: "2026-05-08"
version: "0.1.0"
last_reviewed: "2026-07-06"
version: "0.2.0"
---

# React Native Firebase TS/JS Validation

## Scope

Use this skill to validate changes to TypeScript or JavaScript code in the React Native Firebase monorepo.
Use this skill to validate changes in the React Native Firebase monorepo before handoff, commit, or push.

It is for developer-facing validation after editing package JS/TS sources, type tests, Jest tests, or shared TypeScript configuration. It runs the standard root validation commands that catch generated package setup issues, TypeScript errors, consumer type regressions, API reference regressions, Jest failures, formatting drift, and firebase-js-sdk type parity drift.
It runs the same static-analysis and compile/test commands CI uses for the Lint, TypeScript, Jest, docs, and compare-types jobs — not a narrowed JS-only subset.

## Triggers

Use this skill when the user asks for:

- testing TS or JS changes
- validating TypeScript or JavaScript edits before commit or handoff
- running the standard JS validation pass for RNFB
- checking whether package JS/TS changes compile and pass Jest
- formatting package JS/TS code and then running validation
- running the standard validation pass for RNFB
- checking whether package changes compile and pass Jest
- formatting and linting before push
- CI-equivalent validation before publication

## Out-of-scope boundaries

Do not use this skill for:

- Android Java/Kotlin formatting or build validation
- iOS Objective-C, Objective-C++, C++, Swift, or pod validation
- documentation-only changes that do not affect JS/TS behavior
- release validation that requires the full platform, emulator, Detox, or packaging matrix
- release validation that requires the full platform, emulator, Detox, or packaging matrix (see [validation checklist § e2e](../../../okf-bundle/testing/validation-checklist.md))
- migrating a package from JavaScript to TypeScript; use the TypeScript refactor or migration skills instead

## Defaults

Set one clear default path so the agent does not choose randomly between options.

- Default tool or method: run the canonical command sequence below from the repository root
- Fallback when default fails: if a command fails, stop the sequence, inspect the failure, fix issues only when the user asked for fixes or the fix is clearly in the current change set, then rerun the failed command and any later commands
- Why this default exists: `lerna:prepare`, both TypeScript compiles, API reference generation, Jest, formatting, and compare-types cover the JS/TS surfaces most likely to regress in this monorepo
- Fallback when default fails: stop, inspect the failure, fix issues in the current change set when authorized, then rerun the failed command and any later commands
- Why this default exists: the sequence mirrors CI Lint + TypeScript + Jest + docs + compare-types gates

## Command sequence

Expand All @@ -54,43 +50,42 @@ Run these root `package.json` scripts in order. **Canonical checklist:** [valida
2. `yarn tsc:compile`
3. `yarn tsc:compile:consumer`
4. `yarn reference:api`
5. `yarn lint:js` (use `yarn lint:js --fix` then re-run until clean)
6. `yarn tests:jest`
7. `yarn format:js`
8. `yarn compare:types`
5. `yarn lint` — **CI Lint job** (`lint:js` + `lint:android` + `lint:ios:check`). When `lint:android` reformats Java, commit the formatter output and rerun until exit 0.
6. When `docs/**` changed: `yarn lint:markdown` then `yarn lint:spellcheck` — **CI docs job**
7. `yarn lint:js --fix` then `yarn lint:js` when step 5 reported ESLint issues only (optional shortcut before re-running full `yarn lint`)
8. `yarn tests:jest`
9. `yarn format:js` — inspect diff; rerun `yarn lint` if formatting touched files
10. `yarn compare:types`

## Gotchas

- **Forbidden:** `yarn workspace … prepare`, `cd packages/<pkg> && yarn prepare/build`, `yarn jet`, `npx jet` — see [agent command policy](../../../okf-bundle/testing/agent-command-policy.md). On failure, fix product code and re-run the **same** canonical command.
- **`yarn lint` is not optional** when native Java or iOS sources are in the diff — `lint:js` alone does not match CI.
- **`yarn lint:spellcheck` is not optional** when `docs/**` is in the diff — `lint:markdown` alone does not match CI.
- `yarn format:js` writes changes across `packages/**/*.{js,ts,tsx}`. Check the diff after formatting and do not revert user changes.
- Run commands from the repository root so workspace resolution, root `tsconfig.json`, and Jest configuration are consistent.
- `yarn lerna:prepare` may rebuild or refresh package artifacts needed before TypeScript or tests run.
- `yarn tsc:compile` checks the repository TypeScript project, while `yarn tsc:compile:consumer` checks the consumer-facing TypeScript project. Run both.
- `yarn reference:api` runs TypeDoc and should come after consumer TypeScript compilation.
- `yarn tests:jest` is the root Jest entrypoint. If it fails, report the failing test file or suite and the first actionable error rather than dumping the full output.
- `yarn compare:types` installs dependencies under `.github/scripts/compare-types` before running the type parity comparison. Keep it last.
- If validation is slow, keep the command running rather than replacing it with a narrower command unless the user explicitly asks for targeted validation.
- If validation is slow, keep the command running rather than replacing it with a narrower command unless the user explicitly narrows validation.

## Workflow

1. Confirm the task is TS/JS validation and note any specific changed package or test files the user mentioned.
1. Confirm the task scope and note changed packages, native paths, or docs.
2. Check whether the worktree has unrelated dirty files if the current task includes code edits or commit preparation.
3. Run the command sequence from the repository root.
4. If `yarn format:js` changes files, include those formatting changes in the validation context and inspect the relevant diff before continuing.
4. If `yarn lint:android` or `yarn format:js` changes files, include those changes and rerun `yarn lint` before continuing.
5. If a command fails:
- stop before running later commands
- identify whether the failure belongs to the current TS/JS changes, pre-existing repo state, or missing local setup
- identify whether the failure belongs to the current changes, pre-existing repo state, or missing local setup
- fix current-change failures when authorized, then rerun the failed command and continue the remaining sequence
6. Return a concise result with the commands run, pass/fail status, and any remaining blockers.

## Validation loop

Use this loop before finalizing:

1. Run the command sequence.
2. If validation fails because of current TS/JS changes and fixing is in scope, fix the issue and rerun the failed command plus all later commands.
2. If validation fails because of current changes and fixing is in scope, fix the issue and rerun the failed command plus all later commands.
3. If validation fails for unrelated or environment-specific reasons, stop and report the blocker with the command that failed and the shortest useful error summary.
4. Only report success when every command in the sequence completes successfully.
4. Only report success when every applicable command in the sequence completes successfully.

## Output format

Expand All @@ -108,7 +103,9 @@ Use this template:
- `yarn tsc:compile`: passed | failed | not run
- `yarn tsc:compile:consumer`: passed | failed | not run
- `yarn reference:api`: passed | failed | not run
- `yarn lint:js`: passed | failed | not run
- `yarn lint`: passed | failed | not run
- `yarn lint:markdown`: passed | failed | not run | n/a
- `yarn lint:spellcheck`: passed | failed | not run | n/a
- `yarn tests:jest`: passed | failed | not run
- `yarn format:js`: passed | failed | changed files
- `yarn compare:types`: passed | failed | not run
Expand All @@ -123,7 +120,7 @@ Use this template:
## Constraints

- Keep responses factual and grounded in command output.
- Do not skip any command in the default sequence unless the user explicitly narrows validation.
- Do not skip `yarn lint` or docs lint rows when the diff requires them.
- Do not claim validation passed unless `compare:types` ran after any formatting changes.
- Do not revert unrelated local changes.
- Avoid broad refactors while fixing validation failures.
Expand All @@ -132,6 +129,6 @@ Use this template:

Load files only when needed:

- **`okf-bundle/testing/validation-checklist.md`** — full validation command list including e2e, coverage, and doc lints
- **`okf-bundle/testing/validation-checklist.md`** — full validation command list including e2e, coverage, and CI job mapping
- Read `package.json` if command names or script definitions need to be confirmed.
- Read affected package `package.json`, `type-test.ts`, or nearby `__tests__/` files only when a failure needs package-specific diagnosis.
- Read affected package `type-test.ts` or nearby `__tests__/` files only when a failure needs package-specific diagnosis.
12 changes: 12 additions & 0 deletions .github/scripts/compare-types/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,18 @@ Every difference must have an entry in the package's `configs/<package-name>.ts`
- A difference is **undocumented** — add it to `configs/<package-name>.ts` with a reason, or fix the RN Firebase types to match.
- A config entry is **stale** — the API now matches the firebase-js-sdk, so the entry should be removed from `configs/<package-name>.ts`.

<a id="justification-bar"></a>

### Justification bar

A documented difference is not a free pass. Every `missingInRN`, `extraInRN`, and `differentShape` entry must clear one of the OKF change-authoring [acceptable exceptions](../../../okf-bundle/testing/change-authoring-workflow.md#acceptable-exceptions): an evidence-backed **intractable technical reason** (platform SDK, native bridge, Hermes/Metro, or toolchain limitation), **or** a **user-accepted deferral** with a documented rationale (e.g. an alignment that needs architectural review). Both require the user's explicit acceptance — convenience, "harmless", or optional-parameter drift never qualifies on its own:

- **`missingInRN`** — an export we simply have not built yet is a backlog item, not a documented difference; only keep it here when it clears an acceptable exception.
- **`extraInRN`** — an RN-only export, overload, or parameter is **drift, not a feature**. If firebase-js-sdk does not expose it and no acceptable exception applies, remove it rather than documenting it.
- **`differentShape`** — align the RN Firebase types to firebase-js-sdk and remove the entry unless the shape difference is forced by an intractable reason (e.g. a native module that can only resolve `Promise<null>`) or a user-accepted deferral.

When implementing a `missingInRN` export, do not introduce new `extraInRN` / `differentShape` drift that would not itself clear an acceptable exception.

## Prerequisites

The root repository dependencies must be installed because the script reads the Firebase JS SDK types from the installed root `node_modules/firebase` package.
Expand Down
5 changes: 3 additions & 2 deletions .github/scripts/compare-types/configs/analytics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,9 @@ const config: PackageConfig = {
{
name: 'logEvent',
reason:
'RN Firebase `logEvent` returns `Promise<void>` and RN overloads omit the web `[key: string]: any` index signature on event params. ' +
'Phase S hint: **Promise that could maybe sync-void+queue** (ordered native fire-and-forget; see PS-S2-gap).',
'Return type aligned to firebase-js-sdk sync `void` (Phase B). Remaining drift is ' +
'cosmetic: firebase-js-sdk overloads include `[key: string]: any` index signatures and ' +
'slightly different property ordering on typed event params; behaviour is equivalent.',
},
{
name: 'setAnalyticsCollectionEnabled',
Expand Down
11 changes: 0 additions & 11 deletions .github/scripts/compare-types/configs/app-check.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,6 @@ import type { PackageConfig } from '../src/types';
const config: PackageConfig = {
nameMapping: {},
missingInRN: [
{
name: 'AppCheckTokenListener',
reason:
'RN Firebase exposes token change listeners through the `onTokenChanged` overloads but does not export the standalone listener type alias from the firebase-js-sdk.',
},
{
name: 'PartialObserver',
reason:
Expand Down Expand Up @@ -86,12 +81,6 @@ const config: PackageConfig = {
},
],
differentShape: [
{
name: 'initializeAppCheck',
reason:
'RN Firebase returns `Promise<AppCheck>` whereas the firebase-js-sdk returns `AppCheck` synchronously. ' +
'Phase S hint: **Promise that could maybe sync-void+queue** after provider/refresh state is installed synchronously (see PS-S2-gap).',
},
{
name: 'AppCheckOptions',
reason:
Expand Down
72 changes: 3 additions & 69 deletions .github/scripts/compare-types/configs/firestore-pipelines.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,75 +21,9 @@ import type { PackageConfig } from '../src/types';

const config: PackageConfig = {
nameMapping: {},
missingInRN: [
{
name: 'documentMatches',
reason:
'Newer firebase-js-sdk document expression helper not yet exposed by RN Firebase pipelines.',
},
{
name: 'geoDistance',
reason:
'Newer firebase-js-sdk geospatial expression helper not yet exposed by RN Firebase pipelines.',
},
{
name: 'score',
reason:
'Newer firebase-js-sdk search score expression helper not yet exposed by RN Firebase pipelines.',
},
{
name: 'DefineStageOptions',
reason: 'Newer firebase-js-sdk stage options type not yet exposed by RN Firebase pipelines.',
},
{
name: 'parent',
reason: 'Newer firebase-js-sdk parent stage helper not yet exposed by RN Firebase pipelines.',
},
{
name: 'SearchStageOptions',
reason:
'Newer firebase-js-sdk search stage options type not yet exposed by RN Firebase pipelines.',
},
],
extraInRN: [
{
name: 'Type',
reason:
'RN Firebase exposes a local type discriminator alias for pipeline expression helpers.',
},
],
differentShape: [
{
name: 'constant',
reason:
'firebase-js-sdk added an optional `preferIntegers` option to pipeline constant expressions; not yet exposed by RN Firebase pipelines.',
},
{
name: 'isType',
reason:
'RN Firebase accepts its local `Type` alias where the firebase-js-sdk declaration accepts a string.',
},
{
name: 'ExpressionType',
reason:
'Declaration member ordering differs from the firebase-js-sdk file, but the same expression kinds are supported (including `PipelineValue`).',
},
{
name: 'StageOptions',
reason:
'Declaration formatting differs for the raw options object, but the public shape is equivalent.',
},
{
name: 'TimeGranularity',
reason:
'RN Firebase uses the existing `isoWeek` and `isoYear` casing while the firebase-js-sdk declaration includes lowercase variants.',
},
{
name: 'timestampDiff',
reason:
'Declaration overload ordering differs from the firebase-js-sdk file, but all four end/start field and expression combinations are supported.',
},
],
missingInRN: [],
extraInRN: [],
differentShape: [],
};

export default config;
Loading
Loading