@ultimat3/pwa 17.0.0 → 19.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 +4 -2
- package/README.md +14 -2
- package/package.json +4 -4
- package/src/index.ts +1 -3
- package/src/manifest.ts +17 -16
- package/src/service-worker.ts +7 -5
package/CLAUDE.md
CHANGED
|
@@ -12,7 +12,8 @@ Tier 4. May import tiers 0–3: `core`, `schema`, `i18n`, `money`, `time`, `cach
|
|
|
12
12
|
| `sw.js` | generated, never hand-written, never committed. No file in this package is a SW. |
|
|
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
|
-
| A generated header | names something that regenerates the file. `sw.js`
|
|
15
|
+
| A generated header | names something that regenerates the file. `sw.js` says `regenerate: x build`, and `As of 2026-08` a build really writes it (#390); it named the CALL for as long as none did. |
|
|
16
|
+
| What is WIRED, and what is not | **every generator here has a caller, `As of 2026-08`.** `generateWebManifest`, `renderThemeColorMeta`, `planIcons`, `appleTouchLinks` and `BuiltinImagePipeline` through `pwa-artifacts.ts` / `dev-assets.ts` (#362); `generateServiceWorker` — and with it `buildPrecacheManifest`, `offlineFallbackSource`, `backgroundSyncSource` and `pushSource` — through `sw-artifacts.ts` (#390). So `x dev`, the container and the static export emit `manifest.webmanifest`, `sw.js` and `x-sw-register.js` alike. The worker half landed a release later because a bad `sw.js` is sticky: it waited on a real browser check (`packages/cli/e2e/service-worker.e2e.test.ts`), which nothing in the gate could run until #400. **`pwa.push` is the one capability still unwired** — the generator emits a push handler only when a VAPID key comes with it and there is no `pwa.vapid` config key, so `x build` reports it as a `serviceWorkerWarnings` entry rather than dropping it in silence. |
|
|
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 |
|
|
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,19 @@ 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 has a build behind it, `As of 2026-08`.** `generateServiceWorker` — and
|
|
113
|
+
through it `buildPrecacheManifest`, `offlineFallbackSource`, `backgroundSyncSource` and
|
|
114
|
+
`pushSource` — is called by `packages/cli/src/sw-artifacts.ts`, so `x dev`, the container and the
|
|
115
|
+
static export all emit `sw.js` and `x-sw-register.js`
|
|
116
|
+
([#390](https://github.com/developerz-ai/ultimate/issues/390)). It landed a release after the
|
|
117
|
+
manifest half because a bad `sw.js` is sticky: it waited on a real browser check
|
|
118
|
+
(`packages/cli/e2e/service-worker.e2e.test.ts`), which the tree could not run until #400.
|
|
107
119
|
- **Precache revisions are content hashes, never the build id.** Keying on the build id
|
|
108
120
|
re-downloads every asset on every deploy.
|
|
109
121
|
- **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": "
|
|
3
|
+
"version": "19.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.
|
|
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": "
|
|
35
|
-
"@ultimat3/seo": "
|
|
34
|
+
"@ultimat3/core": "19.0.0",
|
|
35
|
+
"@ultimat3/seo": "19.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
|
-
/**
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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:
|
|
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:
|
|
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:
|
|
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',
|
package/src/service-worker.ts
CHANGED
|
@@ -227,13 +227,15 @@ export function generateServiceWorker(
|
|
|
227
227
|
}
|
|
228
228
|
|
|
229
229
|
function header(buildId: string): string {
|
|
230
|
-
// `
|
|
231
|
-
//
|
|
232
|
-
//
|
|
233
|
-
//
|
|
230
|
+
// `x build`, `As of 2026-08-27`, and it took until then to become true. This said
|
|
231
|
+
// `generateServiceWorker(routes, config, buildId) from @ultimat3/pwa` — the CALL rather than a
|
|
232
|
+
// command — because nothing in the tree called it: `x build` emitted no service worker, so the
|
|
233
|
+
// line would have sent whoever found this file in a diff to a command that left it exactly as
|
|
234
|
+
// they found it. `packages/cli/src/sw-artifacts.ts` is the caller now (#390), and `x build`
|
|
235
|
+
// regenerates this file the way the comment always meant to say.
|
|
234
236
|
return `// GENERATED by @ultimat3/pwa from the route table — do not edit.
|
|
235
237
|
// build: ${buildId}
|
|
236
|
-
// regenerate:
|
|
238
|
+
// regenerate: x build`;
|
|
237
239
|
}
|
|
238
240
|
|
|
239
241
|
function constants(
|