# go-offline

The easiest way to make your website work offline.

`go-offline` is a production-ready npm package that helps developers add offline support to websites and frontend apps without hand-rolling service workers from scratch. It combines a small runtime API with an optional CLI so setup can stay simple without pretending browser code can generate project files by magic.

## Why go-offline

- Add offline support with a single import.
- Use a first-class React provider and hooks when you want a React-native DX.
- Keep service worker complexity behind sensible defaults.
- Generate starter files with `npx go-offline init` when a project needs them.
- Stay framework-friendly across React, Vite, vanilla JS, PHP, Laravel, and static sites.

## What problem it solves

Most teams want offline support, fallback pages, and PWA basics, but do not want to manually build:

- service worker registration
- caching strategies
- offline fallback handling
- update detection
- install prompt plumbing

`go-offline` gives you a lightweight v1 path that is technically honest:

- runtime code handles browser-side setup
- the CLI generates the files a runtime package cannot create

## Installation

```bash
npm install go-offline
```

If you want TypeScript, React hooks, and the CLI in the same package, that is already included.

## Quick Start

### Universal usage

```ts
import { setupOffline } from "go-offline";

void setupOffline();
```

### React usage

```tsx
import { GoOfflineProvider } from "go-offline";

function App() {
  return (
    <GoOfflineProvider>
      <YourApp />
    </GoOfflineProvider>
  );
}

export default App;
```

### Configured usage

```ts
import { setupOffline } from "go-offline";

void setupOffline({
  offlinePage: "/offline.html",
  pwa: true,
  debug: true,
  runtimeCaching: {
    pages: "network-first",
    images: "cache-first",
    api: "stale-while-revalidate",
    assets: "stale-while-revalidate"
  }
});
```

## How It Works

`go-offline` has two parts:

### Runtime

Use this inside your app code.

It handles:

- service worker registration
- online/offline detection
- update lifecycle hooks
- React provider/hooks
- install prompt support
- config normalization

### CLI

Use this when you want starter files.

```bash
npx go-offline init
```

It can generate:

- `offline.html`
- `manifest.webmanifest`
- `go-offline.config.ts`
- `sw.js`

## React Usage

### Provider

```tsx
import { GoOfflineProvider } from "go-offline";

export function App() {
  return (
    <GoOfflineProvider
      config={{
        offlinePage: "/offline.html",
        pwa: true,
        debug: true
      }}
    >
      <YourApp />
    </GoOfflineProvider>
  );
}
```

### `useOfflineStatus()`

```tsx
import { useOfflineStatus } from "go-offline";

function StatusBadge() {
  const { isOnline, isOffline } = useOfflineStatus();

  return <span>{isOffline ? "Offline" : "Online"}</span>;
}
```

### `usePWAInstall()`

```tsx
import { usePWAInstall } from "go-offline";

function InstallButton() {
  const { isInstallable, promptInstall } = usePWAInstall();

  if (!isInstallable) {
    return null;
  }

  return <button onClick={() => void promptInstall()}>Install app</button>;
}
```

### `useGoOffline()`

```tsx
import { useGoOffline } from "go-offline";

function Controls() {
  const { isOffline, update, promptInstall, isInstallable } = useGoOffline();

  return (
    <>
      <p>{isOffline ? "You are offline" : "You are online"}</p>
      <button onClick={() => void update()}>Check for update</button>
      {isInstallable ? <button onClick={() => void promptInstall()}>Install</button> : null}
    </>
  );
}
```

## CLI Usage

Run:

```bash
npx go-offline init
```

The CLI asks a few beginner-friendly questions and generates starter files in your public web root. The generated `sw.js` is a working service worker starter, not a fake placeholder.

Recommended generated files:

- `public/offline.html`
- `public/sw.js`
- `public/manifest.webmanifest`
- `go-offline.config.ts`

## Public API

```ts
setupOffline(config?)
registerServiceWorker(config?)
defineOfflineConfig(config)

GoOfflineProvider
useOfflineStatus()
usePWAInstall()
useGoOffline()
```

### `setupOffline(config?)`

Registers the service worker, sets up connectivity tracking, and enables install prompt support when `pwa` is enabled.

Returns a controller with:

- `isOnline()`
- `getRegistration()`
- `update()`
- `unregister()`
- `destroy()`

### `registerServiceWorker(config?)`

Lower-level service worker registration helper. Use this if you want registration behavior without the full `setupOffline()` runtime controller.

### `defineOfflineConfig(config)`

Type-safe helper for config files and editor autocomplete.

## Config Reference

```ts
type CacheStrategy =
  | "cache-first"
  | "network-first"
  | "stale-while-revalidate"
  | "network-only"
  | "cache-only";

interface GoOfflineConfig {
  enabled?: boolean;
  debug?: boolean;
  swPath?: string;
  scope?: string;
  version?: string;
  offlinePage?: string;
  offlineImage?: string;
  precache?: string[];
  pwa?: boolean | {
    name?: string;
    shortName?: string;
    description?: string;
    themeColor?: string;
    backgroundColor?: string;
    display?: "standalone" | "fullscreen" | "minimal-ui" | "browser";
    startUrl?: string;
    icons?: Array<{
      src: string;
      sizes: string;
      type: string;
      purpose?: string;
    }>;
  };
  runtimeCaching?: {
    pages?: CacheStrategy;
    images?: CacheStrategy;
    api?: CacheStrategy;
    assets?: CacheStrategy;
  };
  callbacks?: {
    onReady?: (registration: ServiceWorkerRegistration) => void;
    onInstalled?: (registration: ServiceWorkerRegistration) => void;
    onUpdateAvailable?: (registration: ServiceWorkerRegistration) => void;
    onOffline?: () => void;
    onOnline?: () => void;
    onError?: (error: unknown) => void;
  };
}
```

### Default behavior

Default runtime caching:

- `pages: "network-first"`
- `images: "cache-first"`
- `api: "network-first"`
- `assets: "stale-while-revalidate"`

Default generated paths:

- `swPath: "/sw.js"`
- `offlinePage: "/offline.html"`

## Automatic vs Configurable

### Automatic

- browser support checks
- service worker registration attempts
- online/offline event listeners
- default cache naming and versioning
- default fallback HTML response if no custom offline page is cached
- install prompt tracking when `pwa` is enabled

### Configurable

- service worker path and scope
- fallback page path
- offline image path
- explicit precache entries
- runtime caching strategies
- cache version
- PWA metadata
- lifecycle callbacks
- debug logging

## Examples

The repo includes starter examples for:

- React app
- Vite app
- plain HTML site
- vanilla JS site
- PHP website
- Laravel public frontend

See [examples/react-app](./examples/react-app), [examples/vite-app](./examples/vite-app), [examples/plain-html](./examples/plain-html), [examples/vanilla-js](./examples/vanilla-js), [examples/php-site](./examples/php-site), and [examples/laravel-public](./examples/laravel-public).

## Project Setup Tips

### Vite

- Import `setupOffline()` in your main client entry.
- Keep `sw.js`, `offline.html`, and `manifest.webmanifest` in `public/`.

### React

- Use `GoOfflineProvider` at the app root if you want hooks and shared state.
- Use `setupOffline()` directly if you want a lighter integration.

### PHP or Laravel

- Put generated static files inside the public web root.
- Register the runtime from your frontend bundle, not from server templates directly.

## Troubleshooting

### The service worker does not register

Check:

- `sw.js` is served from the correct public path
- the browser supports service workers
- the page is served over `https` or `http://localhost`
- `swPath` and `scope` match where the file actually lives

### Offline fallback is not showing

Check:

- the user has visited at least once while online
- `offline.html` exists and is reachable
- the service worker has been installed
- the request is a navigation/document request

### PWA install prompt never appears

That can be normal. Install prompts are browser-dependent and may require:

- HTTPS
- a valid manifest
- icons
- engagement heuristics
- a supported browser

### Development feels stale

Service workers can cache old files aggressively during local development. If needed:

- unregister the service worker in DevTools
- clear site storage
- bump `version` in config when testing cache changes

## Limitations

- A user cannot use the app offline before the service worker has been installed once online.
- A runtime package cannot create project files by itself, which is why the CLI exists.
- Automatic asset discovery across every framework is out of scope for v1.
- API caching is configurable, but should be used carefully for dynamic or authenticated data.
- Cross-origin assets may not cache reliably depending on response headers and browser behavior.

## Roadmap

Planned future directions:

- route-aware caching rules
- update notification helpers
- cache expiration controls
- offline form queue support
- background sync helpers
- IndexedDB utilities
- plugin architecture
- framework-specific adapters

## Publish Instructions

Before publishing:

```bash
npm install
npm run typecheck
npm run build
npm run verify
```

Then publish:

```bash
npm login
npm publish --access public
```

If the package name is already taken, rename it before publishing.

## Package Quality Checklist

- Replace placeholder repository URLs in `package.json`
- Replace `author` in `package.json`
- Replace the copyright holder in `LICENSE`
- Confirm generated examples match your final docs
- Test the CLI in a real sample app before first public release

## License

MIT

For open-source adoption, MIT is the right default for this package category.
