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
95 changes: 87 additions & 8 deletions .github/workflows/browsers.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,94 @@ on:
permissions:
contents: read

concurrency:
group: browsers-${{ github.ref }}
cancel-in-progress: true

jobs:
e2e:
runs-on: ubuntu-latest
chromium:
runs-on: ubuntu-24.04
timeout-minutes: 30
steps:
- name: Check out repository
uses: actions/checkout@v7
- name: Set up Node.js
uses: actions/setup-node@v7
with:
node-version: 24
cache: npm
cache-dependency-path: package-lock.json
- name: Install dependencies
run: npm ci
- name: Install Chromium
run: npx playwright install --with-deps chromium
- name: Run Chromium tests
run: npm run test:e2e -- --project=chromium --reporter=line,html
- name: Upload failure evidence
if: failure()
uses: actions/upload-artifact@v7
with:
name: playwright-chromium
path: |
playwright-report
test-results
if-no-files-found: ignore
retention-days: 7

firefox:
runs-on: ubuntu-24.04
timeout-minutes: 30
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
- name: Check out repository
uses: actions/checkout@v7
- name: Set up Node.js
uses: actions/setup-node@v7
with:
node-version-file: .nvmrc
node-version: 24
cache: npm
- run: npm ci
- run: npx playwright install --with-deps chromium firefox webkit
- run: npm run test:e2e
cache-dependency-path: package-lock.json
- name: Install dependencies
run: npm ci
- name: Install Firefox
run: npx playwright install --with-deps firefox
- name: Run Firefox tests
run: npm run test:e2e -- --project=firefox --reporter=line,html
- name: Upload failure evidence
if: failure()
uses: actions/upload-artifact@v7
with:
name: playwright-firefox
path: |
playwright-report
test-results
if-no-files-found: ignore
retention-days: 7

webkit:
runs-on: ubuntu-24.04
timeout-minutes: 30
steps:
- name: Check out repository
uses: actions/checkout@v7
- name: Set up Node.js
uses: actions/setup-node@v7
with:
node-version: 24
cache: npm
cache-dependency-path: package-lock.json
- name: Install dependencies
run: npm ci
- name: Install WebKit
run: npx playwright install --with-deps webkit
- name: Run WebKit tests
run: npm run test:e2e -- --project=webkit --reporter=line,html
- name: Upload failure evidence
if: failure()
uses: actions/upload-artifact@v7
with:
name: playwright-webkit
path: |
playwright-report
test-results
if-no-files-found: ignore
retention-days: 7
22 changes: 16 additions & 6 deletions .github/workflows/quality.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,24 @@ on:
permissions:
contents: read

concurrency:
group: quality-${{ github.ref }}
cancel-in-progress: true

jobs:
check:
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
timeout-minutes: 30
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
- name: Check out repository
uses: actions/checkout@v7
- name: Set up Node.js
uses: actions/setup-node@v7
with:
node-version-file: .nvmrc
node-version: 24
cache: npm
- run: npm ci
- run: npm run check
cache-dependency-path: package-lock.json
- name: Install dependencies
run: npm ci
- name: Run quality gate
run: npm run check
111 changes: 102 additions & 9 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,22 +1,115 @@
name: Release

on:
workflow_dispatch:
release:
types:
- published

permissions:
contents: read
id-token: write

concurrency:
group: npm-nipe-solutions-react-pull-to-refresh-stable
cancel-in-progress: false

jobs:
verify:
if: github.event.release.prerelease == false
runs-on: ubuntu-24.04
timeout-minutes: 60
outputs:
tarball: ${{ steps.release.outputs.tarball }}
channel: ${{ steps.release.outputs.channel }}
steps:
- name: Check out repository
uses: actions/checkout@v7
- name: Set up Node.js
uses: actions/setup-node@v7
with:
node-version: 24
cache: npm
cache-dependency-path: package-lock.json
- name: Install dependencies
run: npm ci
- name: Run quality gate
run: npm run check
- name: Install browser engines
run: npx playwright install --with-deps chromium firefox webkit
- name: Run complete browser suite
run: npm run test:e2e
- name: Verify and preserve release artifact
id: release
run: npm run release:check -- --dry-run --output release-artifact
- name: Upload verified release artifact
uses: actions/upload-artifact@v7
with:
name: npm-package-stable
path: release-artifact
if-no-files-found: error
retention-days: 3

publish:
needs: verify
environment: npm
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
timeout-minutes: 10
permissions:
id-token: write
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
- name: Set up Node.js for trusted publishing
uses: actions/setup-node@v7
with:
node-version-file: .nvmrc
node-version: 24
registry-url: https://registry.npmjs.org
- run: npm ci
- run: npm run check
- run: npm publish --tag alpha --access public
- name: Download verified release artifact
uses: actions/download-artifact@v7
with:
name: npm-package-stable
path: release-artifact
- name: Validate and publish verified artifact
env:
RELEASE_TARBALL: ${{ needs.verify.outputs.tarball }}
RELEASE_CHANNEL: ${{ needs.verify.outputs.channel }}
working-directory: release-artifact
run: |
set -euo pipefail

if [[ "$RELEASE_CHANNEL" != "latest" ]]; then
echo "Release channel must be latest" >&2
exit 1
fi

expected_tarball="nipe-solutions-react-pull-to-refresh-1.0.0.tgz"
if [[ ! "$RELEASE_TARBALL" =~ ^[A-Za-z0-9._-]+$ ]] || [[ "$RELEASE_TARBALL" != "$expected_tarball" ]]; then
echo "Unsafe release tarball: expected $expected_tarball" >&2
exit 1
fi

manifest="${RELEASE_TARBALL}.sha512"
shopt -s nullglob
manifests=(*.sha512)
if (( ${#manifests[@]} != 1 )) || [[ "${manifests[0]}" != "$manifest" ]]; then
echo "Checksum manifest name must exactly match $manifest" >&2
exit 1
fi
if [[ ! -f "$RELEASE_TARBALL" ]] || [[ ! -f "$manifest" ]]; then
echo "Verified tarball or checksum manifest is missing" >&2
exit 1
fi
if [[ "$(wc -l < "$manifest")" -ne 1 ]]; then
echo "Checksum manifest must contain exactly one entry" >&2
exit 1
fi

manifest_line="$(<"$manifest")"
if [[ ! "$manifest_line" =~ ^([[:xdigit:]]{128})[[:space:]][[:space:]]([A-Za-z0-9._-]+)$ ]]; then
echo "Checksum manifest must contain one SHA-512 entry" >&2
exit 1
fi
if [[ "${BASH_REMATCH[2]}" != "$RELEASE_TARBALL" ]]; then
echo "Checksum manifest filename must match the release tarball" >&2
exit 1
fi

sha512sum --check --strict -- "$manifest"
npm publish --ignore-scripts --provenance --access public --tag "$RELEASE_CHANNEL" -- "$RELEASE_TARBALL"
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,18 @@ All notable changes follow Keep a Changelog.

## [Unreleased]

## [1.0.0] - 2026-09-29

### Added

- Verify packed ESM, CommonJS, TypeScript, CSS, and server-rendering contracts in isolated React 18 and React 19 consumers.
- Publish through a protected workflow that checks the exact tarball and SHA-512 manifest before npm provenance publishing.

### Changed

- Promote the established compound API and gesture lifecycle to the stable release channel.
- Run release policy checks alongside the existing unit, API, size, package, website, and three-engine browser suites.

## [0.1.0-alpha.1] - 2026-09-06

### Fixed
Expand Down
7 changes: 4 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@

Thanks for helping make pull-to-refresh boringly reliable.

1. Use Node 24 and install with npm ci.
1. Use Node 24 and install with `npm ci`.
2. Create a focused branch.
3. Add a failing behavior test before changing mechanics.
4. Run npm run check and npm run test:e2e.
5. Describe browser and device validation accurately.
4. Run `npm run check` and `npm run test:e2e`.
5. Distinguish automated browser results from physical-device and human
assistive-technology validation.

Do not combine application data behavior with gesture mechanics. New public
configuration needs a demonstrated composition use case.
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,10 @@ keyboard gesture, or focus movement.

Directional-capable browsers use Pointer Events with `pan-x pan-down pinch-zoom`.
Firefox/Safari use a session-scoped Touch Events compatibility adapter. Automated
engine tests do not verify physical iOS or Android behavior. Mobile QA remains
beta-blocking; use an application refresh button as the accessible alternative.
Chromium, Firefox, and WebKit tests cover mechanics, keyboard use, and axe rules.
They do not verify physical iOS or Android behavior. No physical-device or human
screen-reader session was performed for 1.0.0. Always provide an application
refresh button as the accessible alternative.

See [browser behavior](docs/BROWSER_BEHAVIOR.md),
[integrations](docs/INTEGRATIONS.md), and
Expand All @@ -121,8 +123,8 @@ npm run test:e2e
Requires Node 24. React 18.3 and React 19 are peer-supported. Runtime
dependencies are limited to React peers.

Current status: 0.1.0-alpha.1. Automated checks do not replace the manual device
matrix.
Current status: 1.0.0 stable. Automated checks do not replace the documented
manual device and assistive-technology matrix.

Part of [NIPE Open Source](https://opensource.nipesolutions.com).

Expand Down
5 changes: 3 additions & 2 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
Report vulnerabilities privately through GitHub Security Advisories for this
repository. Do not open a public issue containing exploit details.

Pre-release versions receive fixes on the latest alpha line. No version is
stable until explicitly announced.
Security fixes are provided for the current 1.x release line. Earlier
prerelease versions are unsupported; upgrade to the latest stable version before
reporting an issue.
3 changes: 2 additions & 1 deletion docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ UI and telemetry ownership.
Safari/Firefox mobile fallback and actual browser chrome interactions.

Directly invoking React handlers or `page.mouse` cannot establish mobile touch
support. Beta requires meaningful physical iOS and Android evidence.
support. The 1.0 release has no physical iOS/Android or human screen-reader
evidence; those remain separate manual validation activities.

Native listeners route through handlers published at React commit, so owner and
refresh callback changes are observed without resubscribing active sessions.
Expand Down
13 changes: 7 additions & 6 deletions docs/BROWSER_BEHAVIOR.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@ configuration. Safari may retain native browser Pull-to-Refresh ownership.**
| Environment | Logic / E2E | Real touch | Native PTR conflict |
| -------------------- | ----------------------------------- | --------------------------- | --------------------------- |
| Chromium desktop | Automated + trusted CDP touch input | Physical mobile not implied | Desktop n/a |
| Android Chrome | Chromium engine coverage | Manual required | Test page refresh |
| Android Chrome | Chromium engine coverage | Not run | Not run |
| Firefox desktop | Automated DOM and mouse traces | n/a | Desktop n/a |
| Firefox Android | Firefox engine coverage | Manual required | Test |
| Firefox Android | Firefox engine coverage | Not run | Not run |
| WebKit desktop | Automated DOM and mouse traces | n/a | Desktop n/a |
| iPhone / iPad Safari | WebKit evidence only | Manual required | Native page refresh may win |
| iPhone / iPad Safari | WebKit evidence only | Not run | Native page refresh may win |

See [real-device QA](REAL_DEVICE_QA.md) for actual run status and beta gates.
See [real-device QA](REAL_DEVICE_QA.md) for actual run status and the manual
evidence checklist.
`page.mouse` validates state mechanics, not mobile touch arbitration. Synthetic
DOM Touch Events validate adapter cancellation and cleanup; they do not start
native scrolling. Chromium CDP input exercises the browser's touch pipeline,
Expand Down Expand Up @@ -124,8 +125,8 @@ commits; mouse/pen still use Pointer Events.

Touch cancellation on the first relevant move is a compatibility strategy, not
proof that every Safari/Firefox mobile configuration will surrender its stream.
Real-device validation remains beta-blocking. No retry of a browser-owned stream
and no early `preventDefault()` are used.
Physical-device validation was not run for 1.0.0. No retry of a browser-owned
stream and no early `preventDefault()` are used.

## Composition limits and troubleshooting

Expand Down
Loading