Skip to content
Back to skills

Angular Pwa Offline

ASecurity

Reviews Angular PWA/offline-first setup for apps that need to work in the field with intermittent connectivity. Flags @angular/service-worker not configured, no offline fallback page/shell, a caching strategy that doesn't distinguish app-shell assets from API data, and no conflict-resolution story for data edited offline and synced later. Only relevant for shops shipping field/offline-capable apps — most internal line-of-business apps can skip this. Outputs findings with pilot-angular pwa-off...

  • 2 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added September 6, 2026
developmenttypescriptgoshellangularapiperformance

Works with

  • cli
  • api

Security analysis

A100/100

Scanned September 6, 2026

npx -y skills add AgenticPawan/FullStack-Pilot --skill angular-pwa-offline --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Angular Pwa Offline?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Angular Pwa Offline
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/agenticpawan-angular-pwa-offline/badge)](https://www.skillsdirectory.com/skills/agenticpawan-angular-pwa-offline)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: angular-pwa-offline
description: Reviews Angular PWA/offline-first setup for apps that need to work in the field with intermittent connectivity. Flags @angular/service-worker not configured, no offline fallback page/shell, a caching strategy that doesn't distinguish app-shell assets from API data, and no conflict-resolution story for data edited offline and synced later. Only relevant for shops shipping field/offline-capable apps — most internal line-of-business apps can skip this. Outputs findings with pilot-angular pwa-offline standard IDs.
when_to_use: PWA, service worker, offline first, ngsw-config, App Shell, offline fallback, background sync, IndexedDB cache, connectivity detection, field app offline
applies_to: angular>=17
---

## Standard IDs

| ID | Severity | What it checks |
|----|----------|-----------------|
| PWA-001 | P1 | `@angular/service-worker` not configured despite an offline requirement |
| PWA-002 | P2 | No offline fallback page/shell shown when the network is unreachable |
| PWA-003 | P2 | Caching strategy doesn't distinguish static app-shell assets from dynamic API data |
| PWA-004 | P1 | No conflict-resolution story for data edited offline and synced later |

---

## Check A — Service worker not configured (PWA-001)

### Detection

For an app with a stated offline requirement (field technicians, warehouse scanning apps),
check whether `@angular/service-worker` is added (`ng add @angular/pwa`) and registered in
`app.config.ts`. Without it, the app simply fails to load with no cached shell the moment
connectivity drops.

### BAD — no service worker despite an offline requirement

```typescript
export const appConfig: ApplicationConfig = {
  providers: [provideHttpClient(), provideRouter(routes)],
  // No provideServiceWorker() — the app is unusable the moment the network drops.
};
```

### GOOD — service worker registered, enabled only in production

```typescript
export const appConfig: ApplicationConfig = {
  providers: [
    provideServiceWorker('ngsw-worker.js', {
      enabled: !isDevMode(),
      registrationStrategy: 'registerWhenStable:30000',
    }),
  ],
};
```

---

## Check B — No offline fallback UI (PWA-002)

### Detection

Check whether the app shows a dedicated "you're offline" state (via
`SwUpdate`/`navigator.onLine` + a connectivity service) versus letting failed HTTP calls
surface as generic errors indistinguishable from a server-side failure (overlaps
`angular-error-handling` AEH-004's recoverable-vs-crash distinction — offline is a third,
distinct state).

### BAD — offline looks identical to a generic server error

```typescript
this.http.get('/api/orders').pipe(
  catchError(() => { this.toast.show('Something went wrong'); return of([]); })
).subscribe();
```

### GOOD — a distinct offline state, informed by a connectivity service

```typescript
@Injectable({ providedIn: 'root' })
export class ConnectivityService {
  readonly online = toSignal(
    merge(fromEvent(window, 'online').pipe(map(() => true)),
          fromEvent(window, 'offline').pipe(map(() => false))),
    { initialValue: navigator.onLine });
}
```

```html
@if (!connectivity.online()) {
  <app-offline-banner />
}
```

---

## Check C — Caching strategy doesn't distinguish shell from API data (PWA-003)

### Detection

Check `ngsw-config.json` for whether static app-shell assets (JS/CSS bundles, fonts) use a
`prefetch`/`install` strategy while dynamic API responses use a separate `dataGroups`
entry with an appropriate freshness/performance strategy — treating everything as one
undifferentiated cache either serves stale API data indefinitely or never caches the shell
at all.

### BAD — one undifferentiated asset group

```json
{
  "assetGroups": [
    { "name": "app", "installMode": "prefetch", "resources": { "files": ["/**"] } }
  ]
  // No dataGroups — API responses are either never cached or accidentally caught by the wildcard.
}
```

### GOOD — separate strategies for shell assets vs. API data

```json
{
  "assetGroups": [
    { "name": "app", "installMode": "prefetch", "resources": { "files": ["/*.css", "/*.js", "/index.html"] } }
  ],
  "dataGroups": [
    {
      "name": "orders-api",
      "urls": ["/api/orders/**"],
      "cacheConfig": { "strategy": "freshness", "maxAge": "1h", "timeout": "5s" }
    }
  ]
}
```

---

## Check D — No conflict resolution for offline edits (PWA-004)

### Detection

For an app that lets a user edit data while offline (not just read cached data), check
whether a sync mechanism exists for reconciling those edits once connectivity returns —
and whether it accounts for the same optimistic-concurrency conflicts `dotnet-concurrency`
CCY-001/CCY-002 handles server-side (the offline edit's `RowVersion` may be stale by the
time it syncs).

### BAD — offline edits queued with no conflict handling on sync

```typescript
async syncPendingEdits() {
  for (const edit of this.offlineQueue.getAll()) {
    await this.http.put(`/api/orders/${edit.id}`, edit.payload).toPromise();
    // No handling for a 409 Conflict if the record changed server-side while offline.
  }
}
```

### GOOD — conflicts surfaced for user resolution instead of silently overwritten

```typescript
async syncPendingEdits() {
  for (const edit of this.offlineQueue.getAll()) {
    try {
      await this.http.put(`/api/orders/${edit.id}`, edit.payload,
        { headers: { 'If-Match': edit.rowVersion } }).toPromise(); // ties to dotnet-concurrency CCY-003
      this.offlineQueue.remove(edit.id);
    } catch (err) {
      if (err.status === 409) this.conflictResolver.queue(edit); // user resolves manually, not silent overwrite
    }
  }
}
```

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…