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
2 changes: 1 addition & 1 deletion .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Record commands, results and relevant native evidence.

For a new or expanded compatibility implementation, name the complete module/API
unit and pinned Workers baseline. Keep the PR in draft until the
[acceptance gate](https://github.com/rdlabo-dev/cf-workers-mobile/blob/main/CONTRIBUTING.md#acceptance-gate) is met.
[acceptance gate](https://github.com/rdlabo-dev/cf-workers-native/blob/main/CONTRIBUTING.md#acceptance-gate) is met.
For documentation-only or unrelated changes, mark this section not applicable.

- [ ] Export/API inventory has no unresolved coverage decisions.
Expand Down
4 changes: 2 additions & 2 deletions .github/scripts/npm-release.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ export async function resolveRelease(github, context) {
ref: sha,
});
const pkg = JSON.parse(Buffer.from(file.content, "base64").toString("utf8"));
if (pkg.name !== "cf-workers-mobile")
if (pkg.name !== "cf-workers-native")
throw new Error("Unexpected package name");
return { sha, ...releaseVersion(pkg.version, tag, pr, sha) };
}
Expand Down Expand Up @@ -177,7 +177,7 @@ async function resolveCommentBeta(github, context) {
ref: "main",
});
const pkg = JSON.parse(Buffer.from(file.content, "base64").toString("utf8"));
if (pkg.name !== "cf-workers-mobile")
if (pkg.name !== "cf-workers-native")
throw new Error("Unexpected trusted package name");
return {
sha,
Expand Down
6 changes: 3 additions & 3 deletions .github/scripts/npm-release.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,17 @@ function fixture() {
},
],
runs: [{ run_number: 2, status: "completed", conclusion: "success" }],
pkg: { name: "cf-workers-mobile", version: "0.1.1" },
pkg: { name: "cf-workers-native", version: "0.1.1" },
};
const context = {
repo: { owner: "rdlabo-dev", repo: "cf-workers-mobile" },
repo: { owner: "rdlabo-dev", repo: "cf-workers-native" },
eventName: "workflow_run",
payload: {
workflow_run: {
event: "push",
head_branch: "main",
head_sha: sha,
head_repository: { full_name: "rdlabo-dev/cf-workers-mobile" },
head_repository: { full_name: "rdlabo-dev/cf-workers-native" },
path: ".github/workflows/ci.yml",
conclusion: "success",
},
Expand Down
96 changes: 96 additions & 0 deletions .github/workflows/android-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
name: Android release

on:
workflow_dispatch:
inputs:
version:
description: "Publish this package.json version to Maven Central (immutable)"
required: true
type: string

permissions:
contents: read

concurrency:
group: android-release
cancel-in-progress: false

jobs:
release:
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-24.04
timeout-minutes: 40
environment: maven-central
permissions:
contents: read
actions: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
- name: Require the tested main commit and explicit version
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd
env:
RELEASE_VERSION: ${{ inputs.version }}
with:
script: |
const { readFileSync } = await import('node:fs');
const pkg = JSON.parse(readFileSync('package.json', 'utf8'));
const { releaseVersion } = await import(`${process.env.GITHUB_WORKSPACE}/.github/scripts/npm-release.mjs`);
releaseVersion(pkg.version, `v${process.env.RELEASE_VERSION}`);
const { data: main } = await github.rest.repos.getBranch({...context.repo, branch: 'main'});
if (main.commit.sha !== context.sha) throw new Error('Run from the current main commit');
const runs = await github.paginate(github.rest.actions.listWorkflowRuns, {
...context.repo, workflow_id: 'ci.yml', head_sha: context.sha,
event: 'push', branch: 'main', per_page: 100,
});
const latest = runs.sort((a, b) => b.run_number - a.run_number)[0];
if (!latest || latest.status !== 'completed' || latest.conclusion !== 'success') {
throw new Error('This exact main commit must pass Compatibility first');
}
- name: Refuse an existing coordinate
env:
RELEASE_VERSION: ${{ inputs.version }}
run: |
code=$(curl --silent --show-error --output /dev/null --write-out '%{http_code}' "https://repo.maven.apache.org/maven2/io/github/rdlabo/cf-workers-native-android/$RELEASE_VERSION/cf-workers-native-android-$RELEASE_VERSION.pom")
test "$code" = 404 || { echo "Central returned HTTP $code; refusing publication"; exit 1; }
- uses: actions/setup-java@b843ebf9b913146b38da2eaa7aeddf76074ae1ad
with:
distribution: temurin
java-version: "21"
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1
with:
node-version: 22
- run: node --test scripts/android-release/publish.test.mjs
- name: Build the signed bundle
env:
MAVEN_SIGNING_KEY: ${{ secrets.MAVEN_SIGNING_KEY }}
MAVEN_SIGNING_PASSWORD: ${{ secrets.MAVEN_SIGNING_PASSWORD }}
run: demo/android/gradlew -p scripts/android-release --no-daemon -PsignRelease centralBundle --console=plain
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9
with:
name: android-central-bundle
path: scripts/android-release/build/distributions/*.zip
if-no-files-found: error
- name: Verify the staged Maven consumer before publication
run: demo/android/gradlew -p demo/tests/native-embedding/android -PsdkRepository="$GITHUB_WORKSPACE/scripts/android-release/build/repository" :app:assembleDebug --console=plain
- name: Upload, validate and publish
env:
CENTRAL_USERNAME: ${{ secrets.CENTRAL_USERNAME }}
CENTRAL_PASSWORD: ${{ secrets.CENTRAL_PASSWORD }}
run: node scripts/android-release/publish.mjs
- name: Wait for Maven Central distribution
env:
RELEASE_VERSION: ${{ inputs.version }}
run: |
for attempt in $(seq 1 40); do
echo "Checking Maven distribution ($attempt/40)"
code=$(curl --silent --show-error --output /dev/null --write-out '%{http_code}' "https://repo.maven.apache.org/maven2/io/github/rdlabo/cf-workers-native-android/$RELEASE_VERSION/cf-workers-native-android-$RELEASE_VERSION.pom")
if [ "$code" = 200 ]; then exit 0; fi
if [ "$code" != 404 ]; then echo "Central returned HTTP $code"; exit 1; fi
sleep 15
done
echo 'Publication completed but distribution is still pending; do not re-upload.'
exit 1
- name: Verify Maven Central consumer
run: demo/android/gradlew -p demo/tests/native-embedding/android -PcentralOnly --refresh-dependencies :app:assembleDebug --console=plain
2 changes: 2 additions & 0 deletions .github/workflows/native-embedding.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ on:
- ".github/workflows/native-embedding.yml"
- "native/**"
- "scripts/android-release/**"
- ".github/workflows/android-release.yml"
- "Package.swift"
- "package.json"
- "src/**"
Expand Down Expand Up @@ -70,6 +71,7 @@ jobs:
- run: npm run build
- name: Verify standalone Maven publication and consumer
run: |
node --test scripts/android-release/publish.test.mjs
python3 scripts/android-release/verify.py
demo/android/gradlew -p demo/tests/native-embedding/android -PsdkRepository="$GITHUB_WORKSPACE/scripts/android-release/build/repository" :app:assembleDebug --console=plain
- name: Enable emulator acceleration
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ jobs:
import assert from 'node:assert/strict';
const plan = JSON.parse(process.env.PLAN);
const pkg = JSON.parse(readFileSync(`${process.env.RUNNER_TEMP}/release-package.json`, 'utf8'));
assert.equal(pkg.name, 'cf-workers-mobile');
assert.equal(pkg.name, 'cf-workers-native');
assert.equal(pkg.version, plan.version);
assert.equal(pkg.publishConfig.tag, plan.channel);
assert.equal(pkg.publishConfig.access, 'public');
Expand Down
6 changes: 3 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ This exception applies only to existing implementations in 0.1.0 Preview. It doe

### 0.1.2 Preview follow-up

`cf-workers-mobile@0.1.2` Preview is explicitly authorized with the current limitations documented in the [compatibility inventory](docs/compatibility.md). This version-specific exception retains existing incomplete compatibility implementations; it does not certify complete Workers or Node.js compatibility, authorize new incomplete units, bypass required regression checks or CI, or extend to later releases. Keep unsupported behavior and verification boundaries visible.
`cf-workers-native@0.1.2` Preview is explicitly authorized with the current limitations documented in the [compatibility inventory](docs/compatibility.md). This version-specific exception retains existing incomplete compatibility implementations; it does not certify complete Workers or Node.js compatibility, authorize new incomplete units, bypass required regression checks or CI, or extend to later releases. Keep unsupported behavior and verification boundaries visible.

## Layout and tests

Expand Down Expand Up @@ -156,7 +156,7 @@ Direct pushes and PR branches do not automatically publish beta packages. A newe

For a tagged release, update `package.json` to the intended version in a PR, merge it and wait for Compatibility on main to validate reuse or finish the necessary full run. Then tag that commit and push the tag, for example `git tag v0.1.2` followed by `git push origin v0.1.2`. The tag must match the package version and refer to a commit on main with successful CI. Already published versions cannot be republished; create a new version when changing package contents.

The [release workflow](.github/workflows/release.yml) builds without npm publishing credentials, then publishes the resulting tarball in a separate job with lifecycle scripts disabled. It uses [npm Trusted Publishing](https://docs.npmjs.com/trusted-publishers/) rather than an npm token. Configure the npm package's Trusted Publisher once with GitHub owner `rdlabo-dev`, repository `cf-workers-mobile`, workflow filename `release.yml`, no environment name, and permission to publish directly. No `NPM_TOKEN` secret is required.
The [release workflow](.github/workflows/release.yml) builds without npm publishing credentials, then publishes the resulting tarball in a separate job with lifecycle scripts disabled. It uses [npm Trusted Publishing](https://docs.npmjs.com/trusted-publishers/) rather than an npm token. Configure the npm package's Trusted Publisher once with GitHub owner `rdlabo-dev`, repository `cf-workers-native`, workflow filename `release.yml`, no environment name, and permission to publish directly. No `NPM_TOKEN` secret is required.

### PR beta requests

Expand Down Expand Up @@ -186,7 +186,7 @@ branch/commit and tarball SHA-256. Preparation does not publish or use npm crede
The command prints the exact `npm publish <tarball> --ignore-scripts --access public
--tag beta --registry https://registry.npmjs.org` invocation. After reviewing the
artifact, use your local npm account (`npm login` if needed) with permission to
publish `cf-workers-mobile`, and run that printed command. Append `--dry-run` to
publish `cf-workers-native`, and run that printed command. Append `--dry-run` to
inspect it without publishing. Complete npm's authentication/2FA prompts when
requested; keep credentials outside the repository. Local publication does not
use the workflow's OIDC identity or its `--provenance` option.
Expand Down
4 changes: 2 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
import PackageDescription

let package = Package(
name: "CfWorkersMobile",
name: "CfWorkersNative",
platforms: [.iOS(.v15)],
products: [
.library(name: "CfWorkersMobile", targets: ["WorkersCapacitor"]),
.library(name: "CfWorkersNative", targets: ["WorkersCapacitor"]),
.library(name: "WorkersIOS", targets: ["WorkersIOS"])
],
dependencies: [
Expand Down
18 changes: 11 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# cf-workers-mobile
# cf-workers-native

**Bring the app you built with Workers or Void to iOS and Android.**

Expand All @@ -13,7 +13,7 @@ Reach people who prefer the browser and people who look for apps in the store. O
- **Create a new mobile app**
Use Capacitor to build an iOS and Android app for distribution through the App Store and Google Play. [Capacitor guide](docs/integration.md).

[Documentation](docs/README.md) · [Examples](demo/README.md) · [0.1.2 Preview release](https://github.com/rdlabo-dev/cf-workers-mobile/releases/tag/v0.1.2)
[Documentation](docs/README.md) · [Examples](demo/README.md)

## Keep building the app you know

Expand All @@ -39,14 +39,18 @@ Each guide describes its tested versions and boundaries. Already have a separate

Install the shared JavaScript package in your web project:

The renamed package is being prepared for publication. Until it is available on
npm, use the [source tarball instructions](docs/native-embedding.md#dependency-setup).
After publication:

```sh
npm install cf-workers-mobile@0.1.2
npm install cf-workers-native@0.1.2
```

The mobile entry selects the host. For example, an iOS web entry starts with:

```ts
import { workersIOS } from "cf-workers-mobile/ios";
import { workersIOS } from "cf-workers-native/ios";

const app = workersIOS({
baseURL: "https://your-worker.example.com/",
Expand All @@ -55,7 +59,7 @@ const app = workersIOS({
await app.ready;
```

Use `workersAndroid` from `cf-workers-mobile/android` or `workersCapacitor` from `cf-workers-mobile/capacitor` for the other hosts. All three share the same runtime options and [Vite/esbuild tooling](docs/build-setup.md).
Use `workersAndroid` from `cf-workers-native/android` or `workersCapacitor` from `cf-workers-native/capacitor` for the other hosts. All three share the same runtime options and [Vite/esbuild tooling](docs/build-setup.md).

This entry is one part of the setup: connect the existing Worker to a **separate mobile build**, then package its output with your chosen native host. The [embedding guide](docs/native-embedding.md) and [Capacitor guide](docs/integration.md) cover the complete path. For an existing native app, build the web assets in the web project or CI; the native project does not need Capacitor CLI commands or configuration.

Expand All @@ -66,9 +70,9 @@ This entry is one part of the setup: connect the existing Worker to a **separate
Open your application repository and give your assistant this prompt, replacing the target with **iOS**, **Android** or **Capacitor**:

```text
Read https://github.com/rdlabo-dev/cf-workers-mobile/blob/v0.1.2/README.md
Read https://github.com/rdlabo-dev/cf-workers-native/blob/main/README.md
and the integration guide for our target: <iOS, Android or Capacitor>.
Bring this Workers application to that target using cf-workers-mobile@0.1.2.
Bring this Workers application to that target using cf-workers-native@0.1.2.
Preserve its pages, routes, authentication and Cloudflare deployment configuration.
For an existing native app, preserve its entry point and navigation.
Verify the actual user flows on the target platform and report remaining limits.
Expand Down
4 changes: 2 additions & 2 deletions demo/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# cf-workers-mobile examples
# cf-workers-native examples

Choose the example closest to the experience you want to build:

Expand Down Expand Up @@ -42,7 +42,7 @@ The path is relative to the configured Vite root, `src/`. The example uses [the
[src/main.ts](src/main.ts) initializes the runtime and displays the HTML returned by `/`:

```ts
import { workersCapacitor } from "cf-workers-mobile/capacitor";
import { workersCapacitor } from "cf-workers-native/capacitor";

const runtime = workersCapacitor({
baseURL: "https://worker.invalid/",
Expand Down
2 changes: 1 addition & 1 deletion demo/android/app/capacitor.build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ android {

apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle"
dependencies {
implementation project(':cf-workers-mobile')
implementation project(':cf-workers-native')

}

Expand Down
4 changes: 2 additions & 2 deletions demo/android/capacitor.settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
include ':capacitor-android'
project(':capacitor-android').projectDir = new File('../node_modules/@capacitor/android/capacitor')

include ':cf-workers-mobile'
project(':cf-workers-mobile').projectDir = new File('../node_modules/cf-workers-mobile/native/android')
include ':cf-workers-native'
project(':cf-workers-native').projectDir = new File('../node_modules/cf-workers-native/native/android')
2 changes: 1 addition & 1 deletion demo/authentication/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Use your identity provider's native SDK when one is already installed. The small `login.ts` example demonstrates a public OAuth client using Capacitor App/Browser, authorization code + S256 PKCE, exact callback matching and state validation. It connects to an existing provider; it does not implement an identity provider or change the Worker's authentication middleware.

```ts
import { workersCapacitor } from "cf-workers-mobile/capacitor";
import { workersCapacitor } from "cf-workers-native/capacitor";
import { login } from "./login";
import { Capacitor, CapacitorHttp } from "@capacitor/core";

Expand Down
2 changes: 1 addition & 1 deletion demo/authentication/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"@capacitor/core": "8.5.3",
"@capacitor/app": "8.1.2",
"@capacitor/browser": "8.0.5",
"cf-workers-mobile": "file:../.."
"cf-workers-native": "file:../.."
},
"devDependencies": {
"@capacitor/cli": "8.5.3",
Expand Down
4 changes: 2 additions & 2 deletions demo/ios/App/CapApp-SPM/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@ let package = Package(
],
dependencies: [
.package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", exact: "8.5.3"),
.package(name: "CfWorkersMobile", path: "../../../node_modules/cf-workers-mobile")
.package(name: "CfWorkersNative", path: "../../../node_modules/cf-workers-native")
],
targets: [
.target(
name: "CapApp-SPM",
dependencies: [
.product(name: "Capacitor", package: "capacitor-swift-pm"),
.product(name: "Cordova", package: "capacitor-swift-pm"),
.product(name: "CfWorkersMobile", package: "CfWorkersMobile")
.product(name: "CfWorkersNative", package: "CfWorkersNative")
]
)
]
Expand Down
2 changes: 1 addition & 1 deletion demo/opennext/capacitor/main.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { workersCapacitor } from "cf-workers-mobile/capacitor";
import { workersCapacitor } from "cf-workers-native/capacitor";
const origin = import.meta.env.VITE_WORKER_ORIGIN;
if (!origin)
throw new Error(
Expand Down
2 changes: 1 addition & 1 deletion demo/opennext/capacitor/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { defineConfig } from "vite";
import { workersCapacitorPlugin } from "cf-workers-mobile/tooling/vite";
import { workersCapacitorPlugin } from "cf-workers-native/tooling/vite";
import { fileURLToPath } from "node:url";
const here = (path: string) => fileURLToPath(new URL(path, import.meta.url));
export default defineConfig({
Expand Down
2 changes: 1 addition & 1 deletion demo/opennext/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
},
"dependencies": {
"@capacitor/core": "8.5.3",
"cf-workers-mobile": "file:../..",
"cf-workers-native": "file:../..",
"next": "16.4.2",
"react": "19.3.1",
"react-dom": "19.3.1"
Expand Down
2 changes: 1 addition & 1 deletion demo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
},
"dependencies": {
"@capacitor/core": "^8.5.3",
"cf-workers-mobile": "file:..",
"cf-workers-native": "file:..",
"express": "^5.3.0",
"hono": "^4.13.13"
},
Expand Down
2 changes: 1 addition & 1 deletion demo/rendering/main.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { workersCapacitor } from "cf-workers-mobile/capacitor";
import { workersCapacitor } from "cf-workers-native/capacitor";
// The configured Worker supplies the complete HTML document and owns routing.
const app = workersCapacitor({
baseURL: "https://worker.invalid",
Expand Down
2 changes: 1 addition & 1 deletion demo/rendering/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { fileURLToPath } from "node:url";
import { defineConfig } from "vite";
import { workersCapacitorPlugin } from "cf-workers-mobile/tooling/vite";
import { workersCapacitorPlugin } from "cf-workers-native/tooling/vite";
export default defineConfig({
root: fileURLToPath(new URL(".", import.meta.url)),
plugins: [
Expand Down
2 changes: 1 addition & 1 deletion demo/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { workersCapacitor } from "cf-workers-mobile/capacitor";
import { workersCapacitor } from "cf-workers-native/capacitor";

// The Worker entry is configured in vite.config.ts.
const runtime = workersCapacitor({
Expand Down
Loading