@ultimat3/pwa 17.0.0 → 18.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CLAUDE.md CHANGED
@@ -13,6 +13,7 @@ Tier 4. May import tiers 0–3: `core`, `schema`, `i18n`, `money`, `time`, `cach
13
13
  | Determinism | no `Date.now()`, no randomness, sort every collection. Same input → same bytes. `subscriptionState` took `now = Date.now()` and was the one exception; it takes a `Clock` `As of 2026-08-23`, so a frozen clock freezes this package too. |
14
14
  | Rule ORDER | most specific first, then the path. `ruleFor` in the emitted worker returns the FIRST pattern that matches and has no notion of specificity, so the sort IS the routing decision. Alphabetical, `:` (0x3A) and `*` (0x2A) sort before every letter: `/posts/:id` shadowed `/posts/new`, and one `/*` catch-all shadowed the whole table — every `PRECACHE_MANIFEST` entry downloaded at install and never looked up, and a cacheable route served `network-only`, which offline is the `/offline` document. Weights are `@ultimat3/render`'s `compilePattern` (literal 100, `:param` 10, `*` 1), **duplicated** because both packages are tier 4; a wildcard sorts last outright, because a sum over segments cannot say that `/` (0 segments) outranks `/*rest` (1). The shared home is `@ultimat3/core`'s `route-vocabulary.ts`. |
15
15
  | A generated header | names something that regenerates the file. `sw.js` said `regenerate: x build` and no `x build` target emits a service worker — `generateServiceWorker` has no caller anywhere in the tree — so it names the CALL until a build writes it. |
16
+ | What is WIRED, and what is not | `generateWebManifest`, `renderThemeColorMeta`, `planIcons`, `appleTouchLinks` and `BuiltinImagePipeline` are called by `@ultimat3/cli` (`pwa-artifacts.ts`, `dev-assets.ts`), so `x dev`, the container and the static export all emit `manifest.webmanifest` and the `<head>` that names it, `As of 2026-08-27`. `generateServiceWorker`, `buildPrecacheManifest`, `offlineFallbackSource`, `backgroundSyncSource` and `pushSource` still have **zero** callers outside this package (#362) — a bad `sw.js` is sticky and nothing in the gate can drive a real service worker, so the worker lands behind a real browser check rather than beside the manifest. |
16
17
  | Route input | `PwaRoute` is a **structural** view of render's `RouteDescriptor`. Never import render. |
17
18
  | Strategy choice | derived from render mode via `MODE_STRATEGY`. Per-route override only. |
18
19
  | Precache revision | content hash. Never the build id — that re-downloads everything per deploy. |
@@ -30,7 +31,8 @@ Tier 4. May import tiers 0–3: `core`, `schema`, `i18n`, `money`, `time`, `cach
30
31
  | Outbox | queue lives in `@ultimat3/realtime`. This package only registers the sync trigger — and schedules **no retry**: the handler rejects and the PLATFORM decides when to wake it again. `RetryPolicy`, `DEFAULT_RETRY`, `retryDelayMs`, `shouldRetry` and `BackgroundSyncOptions.retry` were deleted 2026-08-23; of the policy only `maxAttempts` ever reached the worker, as a `SYNC_MAX_ATTEMPTS` constant nothing read, and the emitted `X_PWA_SYNC_INCOMPLETE` fix told the reader to raise `pwa.backgroundSync.retry.maxAttempts`, a key `PwaConfig` has never had. `background-sync.test.ts` asserts that every constant the worker declares is one the worker reads. |
31
32
  | Push strings | i18n keys only, rendered per subscriber locale. Never a literal. |
32
33
  | Push URLs | `PushPayload.url` is a PATH and `WindowClient.url` is absolute, so `notificationclick` resolves against `self.location.origin` before comparing. Unresolved, the focus-existing-tab loop matched nothing and every tap opened a second window. |
33
- | Colours | token values passed in via `PwaConfig.tokens`. Never a hex literal in this package — a test fixture asserting the parser is the one exception. |
34
+ | Colours | `@ultimat3/core`'s `PwaColors`, passed in as `WebManifestInput.tokens`. Never a hex literal in this package — a test fixture asserting the parser is the one exception. This package declared `ThemeTokens`/`SchemeColors` itself until 2026-08-27: structurally identical to what `app.config.ts` already carried, with no map between them and nothing asserting they agreed, which is the axiom-1 defect the two `PwaConfig`s were. Deleted; tier 0 owns the shape. |
35
+ | `WebManifestInput` | what `generateWebManifest` takes, and **not** the `pwa` block of `app.config.ts` — it was called `PwaConfig` and said it was, while `@ultimat3/core` exported a different type of the same name that really is the block. An app declares `name` and `colors`; every other member is a caller's (`icons` from `planIcons`, `capabilities` from the build). `packages/cli/src/pwa-artifacts.ts` is the one composer of the two, and the only caller of `generateWebManifest` in the tree. |
34
36
  | Icons | one source image → `BuiltinImagePipeline` → a square PNG per `ICON_MATRIX` entry, rendered by `@ultimat3/core`'s image pipeline. Never a second scaler, never `sharp`, never a vendor CDN. |
35
37
  | Icon source | a **PNG** (core decodes PNG and JPEG only). `x new` scaffolds one; `@ultimat3/cli`'s `dev-assets.ts` reads it at `ICON_SOURCE` and serves every entry at its `outputPath`. This package renders bytes and mounts nothing. |
36
38
  | Icon background | `IconSourceConfig.background`, hex or `transparent` — core's grammar has no named colours. Default transparent. |
package/README.md CHANGED
@@ -89,7 +89,7 @@ capability nothing implements.
89
89
  | `strategyFor`, `MODE_STRATEGY`, `cacheFirst`, … | the four strategies + the mapping table |
90
90
  | `buildPrecacheManifest` | precache entries (url + content-hash revision), size warnings |
91
91
  | `buildId`, `detectSkew`, `retentionPlan` | version skew |
92
- | `generateWebManifest` | the manifest + `theme-color` metas for both schemes |
92
+ | `generateWebManifest` | the manifest + `theme-color` metas for both schemes, from a `WebManifestInput`. Called by `@ultimat3/cli` (`pwa-artifacts.ts`) `As of 2026-08-27`, so `x dev`, the container and the static export all emit `manifest.webmanifest` |
93
93
  | `planIcons`, `requireSourceIcon`, `maskableSafeZone` | icons and splashes from one source |
94
94
  | `BuiltinImagePipeline` | renders that plan: one square PNG per entry, deterministic |
95
95
  | `requireOfflineFallback` | the mandatory offline route |
@@ -103,7 +103,16 @@ capability nothing implements.
103
103
  - **Theme colours come from the design tokens for both schemes.** The manifest spec carries
104
104
  one `theme_color`, so the dark value is emitted as a media-scoped
105
105
  `<meta name="theme-color">` — otherwise an installed dark app launches with a light status
106
- bar every time.
106
+ bar every time. The shape is `@ultimat3/core`'s `PwaColors`, which is what an app writes in
107
+ `app.config.ts` as `pwa.colors`; this package declared its own identical `ThemeTokens` until
108
+ 2026-08-27, with nothing asserting the two agreed.
109
+ - **`WebManifestInput` is not the `pwa` block of `app.config.ts`.** It was called `PwaConfig` and
110
+ said it was, while `@ultimat3/core` exported a different type of that name that really is the
111
+ block. An app writes `name` and `colors`; every other member is a caller's.
112
+ - **The service worker still has no build behind it, `As of 2026-08-27`.** `generateServiceWorker`,
113
+ `buildPrecacheManifest`, `offlineFallbackSource`, `backgroundSyncSource` and `pushSource` have no
114
+ caller outside this package ([#362](https://github.com/developerz-ai/ultimate/issues/362)) — the
115
+ manifest half is wired, the worker half is not.
107
116
  - **Precache revisions are content hashes, never the build id.** Keying on the build id
108
117
  re-downloads every asset on every deploy.
109
118
  - **The mutation queue lives in `@ultimat3/realtime`, not here** (SRP). This package owns
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ultimat3/pwa",
3
- "version": "17.0.0",
3
+ "version": "18.0.0",
4
4
  "description": "Generated service worker, web manifest, icons, push and version-skew handling.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -24,14 +24,14 @@
24
24
  "LICENSE"
25
25
  ],
26
26
  "engines": {
27
- "bun": ">=1.3.0"
27
+ "bun": ">=1.4.0"
28
28
  },
29
29
  "scripts": {
30
30
  "typecheck": "tsc --noEmit -p tsconfig.json",
31
31
  "test": "bun test"
32
32
  },
33
33
  "dependencies": {
34
- "@ultimat3/core": "17.0.0",
35
- "@ultimat3/seo": "17.0.0"
34
+ "@ultimat3/core": "18.0.0",
35
+ "@ultimat3/seo": "18.0.0"
36
36
  }
37
37
  }
package/src/index.ts CHANGED
@@ -79,12 +79,10 @@ export type {
79
79
  ManifestShortcut,
80
80
  Orientation,
81
81
  ProtocolHandler,
82
- PwaConfig,
83
- SchemeColors,
84
82
  ShareTarget,
85
83
  ThemeColorMeta,
86
- ThemeTokens,
87
84
  WebManifest,
85
+ WebManifestInput,
88
86
  WebManifestResult,
89
87
  } from './manifest';
90
88
  export { generateWebManifest, renderThemeColorMeta, serializeWebManifest } from './manifest';
package/src/manifest.ts CHANGED
@@ -5,6 +5,11 @@
5
5
  * app gets a light status bar on every launch.
6
6
  */
7
7
 
8
+ // `PwaColors`/`PwaSchemeColors` are tier 0's, not this package's. They were declared here as
9
+ // `ThemeTokens`/`SchemeColors` — structurally identical to what `app.config.ts` already carried,
10
+ // with no map between them and nothing asserting they agreed, which is the same axiom-1 defect the
11
+ // two `PwaConfig`s were. An app writes them once, in the one config file.
12
+ import type { PwaColors } from '@ultimat3/core';
8
13
  import { escapeAttribute } from '@ultimat3/seo';
9
14
  import type { CapabilityFlags, ResolvedCapabilities } from './capabilities';
10
15
  import { isEnabled, resolveCapabilities } from './capabilities';
@@ -81,19 +86,15 @@ export interface WebManifest {
81
86
  readonly protocol_handlers?: readonly ProtocolHandler[];
82
87
  }
83
88
 
84
- /** Resolved token values for one colour scheme. Never a hex literal in framework code. */
85
- export interface SchemeColors {
86
- readonly themeColor: string;
87
- readonly backgroundColor: string;
88
- }
89
-
90
- export interface ThemeTokens {
91
- readonly light: SchemeColors;
92
- readonly dark: SchemeColors;
93
- }
94
-
95
- /** The `pwa` block of `app.config.ts`. */
96
- export interface PwaConfig {
89
+ /**
90
+ * Everything `generateWebManifest` needs, which is NOT the `pwa` block of `app.config.ts` — this
91
+ * type was called `PwaConfig` and said it was, while `@ultimat3/core` exported a different type of
92
+ * the same name that really is the block. Two exported `PwaConfig`s with no map between them is
93
+ * axiom 1, and the one that lied is this one: an app declares `name` and `colors`, and every other
94
+ * member here is a caller's — `icons` comes from `planIcons`, `capabilities` from the build.
95
+ * `packages/cli/src/pwa-artifacts.ts` is what composes the two into this.
96
+ */
97
+ export interface WebManifestInput {
97
98
  readonly name: string;
98
99
  readonly shortName?: string;
99
100
  readonly description?: string;
@@ -104,7 +105,7 @@ export interface PwaConfig {
104
105
  readonly orientation?: Orientation;
105
106
  readonly lang?: string;
106
107
  readonly dir?: 'ltr' | 'rtl' | 'auto';
107
- readonly tokens: ThemeTokens;
108
+ readonly tokens: PwaColors;
108
109
  readonly categories?: readonly string[];
109
110
  readonly icons?: readonly ManifestIcon[];
110
111
  readonly shortcuts?: readonly ManifestShortcut[];
@@ -127,7 +128,7 @@ export interface WebManifestResult {
127
128
  readonly capabilities: ResolvedCapabilities;
128
129
  }
129
130
 
130
- export function generateWebManifest(config: PwaConfig): WebManifestResult {
131
+ export function generateWebManifest(config: WebManifestInput): WebManifestResult {
131
132
  assertValid(config);
132
133
  const capabilities = resolveCapabilities(config.capabilities);
133
134
  const scope = config.scope ?? '/';
@@ -178,7 +179,7 @@ export function generateWebManifest(config: PwaConfig): WebManifestResult {
178
179
 
179
180
  type MutableManifest = { -readonly [K in keyof WebManifest]?: WebManifest[K] };
180
181
 
181
- function assertValid(config: PwaConfig): void {
182
+ function assertValid(config: WebManifestInput): void {
182
183
  if (config.name.trim() === '') {
183
184
  throw new PwaManifestInvalidError(
184
185
  'pwa.name is empty, so the install prompt would have no title',