jg-sw 1.0.1 → 1.1.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.
Files changed (34) hide show
  1. package/API.md +576 -0
  2. package/CHANGELOG.md +18 -25
  3. package/README.md +2 -34
  4. package/dist/build/config/config.d.ts +2 -0
  5. package/dist/build/config/config.js +11 -6
  6. package/dist/build/config/config.js.map +1 -1
  7. package/dist/build/config/firebase.js +8 -5
  8. package/dist/build/config/firebase.js.map +1 -1
  9. package/dist/build/config/schema.d.ts +2 -0
  10. package/dist/build/config/schema.js +2 -0
  11. package/dist/build/config/schema.js.map +1 -1
  12. package/dist/build/precache/precache.js +6 -2
  13. package/dist/build/precache/precache.js.map +1 -1
  14. package/dist/build/service-worker/service-worker.d.ts +4 -0
  15. package/dist/build/service-worker/service-worker.js +4 -0
  16. package/dist/build/service-worker/service-worker.js.map +1 -1
  17. package/dist/runtime/installation/installation.d.ts +4 -0
  18. package/dist/runtime/installation/installation.js +10 -3
  19. package/dist/runtime/installation/installation.js.map +1 -1
  20. package/dist/runtime/registration/registration.d.ts +5 -2
  21. package/dist/runtime/registration/registration.js +18 -6
  22. package/dist/runtime/registration/registration.js.map +1 -1
  23. package/dist/runtime/shared/errors.d.ts +1 -0
  24. package/dist/runtime/shared/errors.js +1 -0
  25. package/dist/runtime/shared/errors.js.map +1 -1
  26. package/dist/runtime/shared/types.d.ts +2 -1
  27. package/dist/runtime/shared/types.js.map +1 -1
  28. package/dist/runtime/state/state.d.ts +4 -0
  29. package/dist/runtime/state/state.js +4 -0
  30. package/dist/runtime/state/state.js.map +1 -1
  31. package/dist/runtime/update/update.d.ts +2 -0
  32. package/dist/runtime/update/update.js +2 -0
  33. package/dist/runtime/update/update.js.map +1 -1
  34. package/package.json +9 -6
package/API.md ADDED
@@ -0,0 +1,576 @@
1
+ # <a id="api-reference"></a>jg-sw API reference
2
+
3
+ Every export of `jg-sw@1.1.0`, 26 in all, with its signature, its errors, and an example. It's generated from the package's TSDoc.
4
+
5
+ ## <a id="api-entry-main"></a>`jg-sw`
6
+
7
+ - Functions: [`canInstallApp`](#api-canInstallApp), [`getServiceWorkerSnapshot`](#api-getServiceWorkerSnapshot), [`installApp`](#api-installApp), [`registerServiceWorker`](#api-registerServiceWorker), [`subscribeToServiceWorker`](#api-subscribeToServiceWorker), [`updateApp`](#api-updateApp)
8
+ - Constants: [`DEFAULT_SERVICE_WORKER_PATH`](#api-DEFAULT_SERVICE_WORKER_PATH), [`DEFAULT_SERVICE_WORKER_SCOPE`](#api-DEFAULT_SERVICE_WORKER_SCOPE), [`ERRORS`](#api-ERRORS), [`REGISTRATION_DURATION_SECONDS`](#api-REGISTRATION_DURATION_SECONDS), [`SKIP_WAITING_MESSAGE_TYPE`](#api-SKIP_WAITING_MESSAGE_TYPE), [`UPDATE_INSTALL_TIMEOUT_SECONDS`](#api-UPDATE_INSTALL_TIMEOUT_SECONDS)
9
+ - Interfaces: [`IBeforeInstallPromptEvent`](#api-IBeforeInstallPromptEvent)
10
+ - Types: [`IAppInstallationState`](#api-IAppInstallationState), [`IInstallationPromptOutcome`](#api-IInstallationPromptOutcome), [`IRegisterServiceWorkerOptions`](#api-IRegisterServiceWorkerOptions), [`IServiceWorkerSnapshot`](#api-IServiceWorkerSnapshot), [`IUserChoice`](#api-IUserChoice)
11
+
12
+ ### <a id="api-functions"></a>Functions in `jg-sw`
13
+
14
+ #### <a id="api-canInstallApp"></a>`canInstallApp`
15
+
16
+ *Function*
17
+
18
+ Checks whether installApp can show the installation prompt now: the browser offered one, and the page doesn't already run as the installed app. Only Chromium-based browsers offer it.
19
+
20
+ ```ts
21
+ canInstallApp(): boolean
22
+ ```
23
+
24
+ **Returns** `boolean`: Whether the app can be installed.
25
+
26
+ **Example**
27
+
28
+ ```ts
29
+ import { canInstallApp } from 'jg-sw';
30
+
31
+ installButton.hidden = !canInstallApp();
32
+ ```
33
+
34
+ #### <a id="api-getServiceWorkerSnapshot"></a>`getServiceWorkerSnapshot`
35
+
36
+ *Function*
37
+
38
+ Reads the service worker's state: its registration, its newest worker, whether an update waits, and whether the app can be installed. The same object comes back until something changes, so it can be passed to React's useSyncExternalStore, during server rendering too.
39
+
40
+ ```ts
41
+ getServiceWorkerSnapshot(): IServiceWorkerSnapshot
42
+ ```
43
+
44
+ **Returns** `IServiceWorkerSnapshot`: The snapshot.
45
+
46
+ **Example**
47
+
48
+ ```ts
49
+ import { getServiceWorkerSnapshot, subscribeToServiceWorker } from 'jg-sw';
50
+
51
+ const { hasWaitingWorker, installation } = useSyncExternalStore(
52
+ subscribeToServiceWorker,
53
+ getServiceWorkerSnapshot,
54
+ getServiceWorkerSnapshot,
55
+ );
56
+ ```
57
+
58
+ #### <a id="api-installApp"></a>`installApp`
59
+
60
+ *Function*
61
+
62
+ Shows the browser's installation prompt and resolves to the user's answer, also kept in the snapshot as installation.promptOutcome. The browser offers each prompt once, so canInstallApp is false afterwards until it offers another.
63
+
64
+ ```ts
65
+ installApp(): Promise<IInstallationPromptOutcome>
66
+ ```
67
+
68
+ **Returns** `Promise<IInstallationPromptOutcome>`: A promise that resolves to 'accepted' or 'dismissed'.
69
+
70
+ **Throws**
71
+
72
+ - `NO_APP_INSTALLER`: The app can't be installed because the browser didn't offer the prompt.
73
+ - `UNKNOWN_INSTALLATION_PROMPT_ERROR`: The installation prompt failed.
74
+
75
+ **Example**
76
+
77
+ ```ts
78
+ import { installApp } from 'jg-sw';
79
+
80
+ installButton.addEventListener('click', async () => {
81
+ const outcome = await installApp(); // 'accepted' or 'dismissed'
82
+ });
83
+ ```
84
+
85
+ #### <a id="api-registerServiceWorker"></a>`registerServiceWorker`
86
+
87
+ *Function*
88
+
89
+ Registers the service worker and starts following its updates and whether the app can be installed. Later calls return the first call's promise. It never rejects: a failure lands in the snapshot's registrationError, an Exception with SW_NO_BROWSER_SUPPORT, EMPTY_SW_REGISTRATION, or SW_REGISTRATION_FAILED as its code, and the browser's error as its cause.
90
+
91
+ ```ts
92
+ registerServiceWorker(
93
+ options?: IRegisterServiceWorkerOptions,
94
+ ): Promise<IServiceWorkerSnapshot>
95
+ ```
96
+
97
+ **Parameters**
98
+
99
+ - `options?` `IRegisterServiceWorkerOptions`: The worker's path and registration options, and whether to log. Defaults to `{}`.
100
+
101
+ **Returns** `Promise<IServiceWorkerSnapshot>`: A promise that resolves to the snapshot once the registration settles.
102
+
103
+ **Example**
104
+
105
+ ```ts
106
+ import { hasErrorCode } from 'jg-exception';
107
+ import { ERRORS, registerServiceWorker } from 'jg-sw';
108
+
109
+ const { registration, registrationError } = await registerServiceWorker();
110
+ if (hasErrorCode(registrationError, ERRORS.SW_NO_BROWSER_SUPPORT)) {
111
+ // the browser has no service workers
112
+ }
113
+ ```
114
+
115
+ #### <a id="api-subscribeToServiceWorker"></a>`subscribeToServiceWorker`
116
+
117
+ *Function*
118
+
119
+ Calls a listener whenever the snapshot changes, such as when an update finishes installing or the app becomes installable.
120
+
121
+ ```ts
122
+ subscribeToServiceWorker(listener: () => void): () => void
123
+ ```
124
+
125
+ **Parameters**
126
+
127
+ - `listener` `() => void`: The function to call, without arguments; read the new state with getServiceWorkerSnapshot.
128
+
129
+ **Returns** `() => void`: A function that stops the calls.
130
+
131
+ **Example**
132
+
133
+ ```ts
134
+ import { getServiceWorkerSnapshot, subscribeToServiceWorker } from 'jg-sw';
135
+
136
+ const unsubscribe = subscribeToServiceWorker(() => {
137
+ if (getServiceWorkerSnapshot().hasWaitingWorker) {
138
+ showUpdateBanner();
139
+ }
140
+ });
141
+ ```
142
+
143
+ #### <a id="api-updateApp"></a>`updateApp`
144
+
145
+ *Function*
146
+
147
+ Updates the app to its newest version and reloads the page once, never in a loop. When a new version waits, it activates it and reloads once the new worker controls the page. Otherwise, it checks for one, waits up to UPDATE_INSTALL_TIMEOUT_SECONDS for it to install, then activates it the same way. When nothing new is found, the installation fails, or the time runs out, it reloads the page as it is.
148
+
149
+ ```ts
150
+ updateApp(): Promise<void>
151
+ ```
152
+
153
+ **Returns** `Promise<void>`: A promise that resolves once the new worker was asked to activate, or the page started reloading.
154
+
155
+ **Example**
156
+
157
+ ```ts
158
+ import { getServiceWorkerSnapshot, updateApp } from 'jg-sw';
159
+
160
+ if (getServiceWorkerSnapshot().hasWaitingWorker) {
161
+ updateButton.addEventListener('click', () => updateApp());
162
+ }
163
+ ```
164
+
165
+ ### <a id="api-constants"></a>Constants in `jg-sw`
166
+
167
+ #### <a id="api-DEFAULT_SERVICE_WORKER_PATH"></a>`DEFAULT_SERVICE_WORKER_PATH`
168
+
169
+ *Constant*
170
+
171
+ the path the runtime registers the worker from, and the Vite plugin serves it at in development
172
+
173
+ ```ts
174
+ const DEFAULT_SERVICE_WORKER_PATH: "/sw.js"
175
+ ```
176
+
177
+ #### <a id="api-DEFAULT_SERVICE_WORKER_SCOPE"></a>`DEFAULT_SERVICE_WORKER_SCOPE`
178
+
179
+ *Constant*
180
+
181
+ the scope the runtime registers the worker with
182
+
183
+ ```ts
184
+ const DEFAULT_SERVICE_WORKER_SCOPE: "/"
185
+ ```
186
+
187
+ #### <a id="api-ERRORS"></a>`ERRORS`
188
+
189
+ *Constant*
190
+
191
+ the codes for every error thrown or reported by the runtime
192
+
193
+ ```ts
194
+ const ERRORS: { SW_NO_BROWSER_SUPPORT: "SW_NO_BROWSER_SUPPORT"; EMPTY_SW_REGISTRATION: "EMPTY_SW_REGISTRATION"; SW_REGISTRATION_FAILED: "SW_REGISTRATION_FAILED"; NO_APP_INSTALLER: "NO_APP_INSTALLER"; UNKNOWN_INSTALLATION_PROMPT_ERROR: "UNKNOWN_INSTALLATION_PROMPT_ERROR" }
195
+ ```
196
+
197
+ #### <a id="api-REGISTRATION_DURATION_SECONDS"></a>`REGISTRATION_DURATION_SECONDS`
198
+
199
+ *Constant*
200
+
201
+ the seconds a registration usually takes, for apps that wait before reading the snapshot
202
+
203
+ ```ts
204
+ const REGISTRATION_DURATION_SECONDS: 5
205
+ ```
206
+
207
+ #### <a id="api-SKIP_WAITING_MESSAGE_TYPE"></a>`SKIP_WAITING_MESSAGE_TYPE`
208
+
209
+ *Constant*
210
+
211
+ the type of the message the runtime posts to make a waiting worker activate
212
+
213
+ ```ts
214
+ const SKIP_WAITING_MESSAGE_TYPE: "JG_SW_SKIP_WAITING"
215
+ ```
216
+
217
+ #### <a id="api-UPDATE_INSTALL_TIMEOUT_SECONDS"></a>`UPDATE_INSTALL_TIMEOUT_SECONDS`
218
+
219
+ *Constant*
220
+
221
+ the seconds updateApp waits for a new worker to finish installing before it reloads anyway
222
+
223
+ ```ts
224
+ const UPDATE_INSTALL_TIMEOUT_SECONDS: 30
225
+ ```
226
+
227
+ ### <a id="api-interfaces"></a>Interfaces in `jg-sw`
228
+
229
+ #### <a id="api-IBeforeInstallPromptEvent"></a>`IBeforeInstallPromptEvent`
230
+
231
+ *Interface*
232
+
233
+ the event Chromium-based browsers fire when the app can be installed, which lets the app show the installation prompt when it chooses
234
+
235
+ ```ts
236
+ interface IBeforeInstallPromptEvent extends Event {
237
+ readonly platforms: string[];
238
+ readonly userChoice: Promise<IUserChoice>;
239
+ prompt(): Promise<void>;
240
+ }
241
+ ```
242
+
243
+ **Members**
244
+
245
+ - `platforms: string[]` (property): the platforms the event was dispatched for, such as 'web' or 'play'
246
+ - `userChoice: Promise<IUserChoice>` (property): resolves to the user's answer once the prompt closes
247
+ - `prompt(): Promise<void>` (method): shows the installation prompt
248
+
249
+ ### <a id="api-types"></a>Types in `jg-sw`
250
+
251
+ #### <a id="api-IAppInstallationState"></a>`IAppInstallationState`
252
+
253
+ *Type*
254
+
255
+ whether the app can be installed, and what happened when it was offered
256
+
257
+ ```ts
258
+ type IAppInstallationState = {
259
+ canInstall: boolean;
260
+ isAppInstalled: boolean | undefined;
261
+ isRunningInstalledApp: boolean;
262
+ promptOutcome: IInstallationPromptOutcome | undefined;
263
+ };
264
+ ```
265
+
266
+ **Members**
267
+
268
+ - `canInstall: boolean` (property): whether installApp can show the installation prompt now
269
+ - `isAppInstalled: boolean | undefined` (property): true once the browser reports the app was installed, undefined until then
270
+ - `isRunningInstalledApp: boolean` (property): whether the page runs as the installed app, in a standalone window
271
+ - `promptOutcome: IInstallationPromptOutcome | undefined` (property): the user's answer to the last prompt installApp showed, undefined until one is answered
272
+
273
+ #### <a id="api-IInstallationPromptOutcome"></a>`IInstallationPromptOutcome`
274
+
275
+ *Type*
276
+
277
+ the action the user took on the installation prompt
278
+
279
+ ```ts
280
+ type IInstallationPromptOutcome = 'accepted' | 'dismissed';
281
+ ```
282
+
283
+ #### <a id="api-IRegisterServiceWorkerOptions"></a>`IRegisterServiceWorkerOptions`
284
+
285
+ *Type*
286
+
287
+ the options registerServiceWorker accepts
288
+
289
+ ```ts
290
+ type IRegisterServiceWorkerOptions = {
291
+ path?: string;
292
+ options?: RegistrationOptions;
293
+ debugMode?: boolean;
294
+ };
295
+ ```
296
+
297
+ **Members**
298
+
299
+ - `path: string` (property): the worker's script. Defaults to DEFAULT_SERVICE_WORKER_PATH, '/sw.js'
300
+ - `options: RegistrationOptions` (property): the options passed to navigator.serviceWorker.register. Defaults to { scope: '/' }
301
+ - `debugMode: boolean` (property): logs the registration, update, and installation events to the console
302
+
303
+ #### <a id="api-IServiceWorkerSnapshot"></a>`IServiceWorkerSnapshot`
304
+
305
+ *Type*
306
+
307
+ the service worker's state. The object keeps its identity until something changes, so useSyncExternalStore(subscribeToServiceWorker, getServiceWorkerSnapshot) re-renders only on changes
308
+
309
+ ```ts
310
+ type IServiceWorkerSnapshot = {
311
+ registration: ServiceWorkerRegistration | undefined;
312
+ worker: ServiceWorker | undefined;
313
+ registrationError: Exception | undefined;
314
+ hasWaitingWorker: boolean;
315
+ installation: IAppInstallationState;
316
+ };
317
+ ```
318
+
319
+ **Members**
320
+
321
+ - `registration: ServiceWorkerRegistration | undefined` (property): the registration, once registerServiceWorker succeeds
322
+ - `worker: ServiceWorker | undefined` (property): the newest worker: the installing one, else the waiting one, else the active one
323
+ - `registrationError: Exception | undefined` (property): why the registration failed, with one of the ERRORS codes
324
+ - `hasWaitingWorker: boolean` (property): whether a new version is installed and waits to activate, which updateApp does
325
+ - `installation: IAppInstallationState` (property): whether the app can be installed, and what happened when it was offered
326
+
327
+ #### <a id="api-IUserChoice"></a>`IUserChoice`
328
+
329
+ *Type*
330
+
331
+ the user's answer to the installation prompt
332
+
333
+ ```ts
334
+ type IUserChoice = {
335
+ outcome: IInstallationPromptOutcome;
336
+ platform: string;
337
+ };
338
+ ```
339
+
340
+ **Members**
341
+
342
+ - `outcome: IInstallationPromptOutcome` (property)
343
+ - `platform: string` (property)
344
+
345
+ ## <a id="api-entry-build"></a>`jg-sw/build`
346
+
347
+ - Functions: [`buildServiceWorker`](#api-build-buildServiceWorker), [`readServiceWorkerConfig`](#api-build-readServiceWorkerConfig), [`serviceWorkerPlugin`](#api-build-serviceWorkerPlugin), [`writeServiceWorker`](#api-build-writeServiceWorker)
348
+ - Constants: [`ERRORS`](#api-build-ERRORS), [`ServiceWorkerConfigSchema`](#api-build-ServiceWorkerConfigSchema)
349
+ - Types: [`IBuildServiceWorkerOptions`](#api-build-IBuildServiceWorkerOptions), [`IServiceWorkerConfig`](#api-build-IServiceWorkerConfig)
350
+
351
+ ### <a id="api-build-functions"></a>Functions in `jg-sw/build`
352
+
353
+ #### <a id="api-build-buildServiceWorker"></a>`buildServiceWorker`
354
+
355
+ *Function*
356
+
357
+ Generates the service worker's source from a config, without writing it. Every generated worker gets a new cache name, `<cacheNamePrefix>--<10 random characters>`, so a new deployment replaces the previous one's cache when it activates.
358
+
359
+ ```ts
360
+ buildServiceWorker(
361
+ config: { outDir: string; cacheNamePrefix: string; includeToPrecache: string[]; excludeFilesFromPrecache: string[]; excludeMIMETypesFromCache: string[]; template: "base" } | { outDir: string; cacheNamePrefix: string; includeToPrecache: string[]; excludeFilesFromPrecache: string[]; excludeMIMETypesFromCache: string[]; template: "firebase-fcm"; firebaseConfigProcessEnvKey: string; firebaseSdkVersion: string },
362
+ options?: IBuildServiceWorkerOptions,
363
+ ): Promise<string>
364
+ ```
365
+
366
+ **Parameters**
367
+
368
+ - `config` `{ outDir: string; cacheNamePrefix: string; includeToPrecache: string[]; excludeFilesFromPrecache: string[]; excludeMIMETypesFromCache: string[]; template: "base" } | { outDir: string; cacheNamePrefix: string; includeToPrecache: string[]; excludeFilesFromPrecache: string[]; excludeMIMETypesFromCache: string[]; template: "firebase-fcm"; firebaseConfigProcessEnvKey: string; firebaseSdkVersion: string }`: The service worker config, as read by readServiceWorkerConfig or written inline.
369
+ - `options?` `IBuildServiceWorkerOptions`: The environment the firebase-fcm template reads its Firebase config from, whether the worker caches (false for development), and the directory paths are resolved from. Defaults to `{}`.
370
+
371
+ **Returns** `Promise<string>`: A promise that resolves to the worker's source.
372
+
373
+ **Throws**
374
+
375
+ - `FAILED_TO_READ_BASE_CONFIG`: The service worker config is invalid.
376
+ - `INVALID_TEMPLATE_NAME`: The template must be 'base' or 'firebase-fcm'.
377
+ - `INVALID_FIREBASE_SDK_VERSION`: The Firebase SDK version must be a version such as 12.4.0.
378
+ - `INVALID_ENVIRONMENT`: The environment isn't supported, or the firebase-fcm template needs one.
379
+ - `NOT_A_PATH_ELEMENT`: An asset to precache is outside outDir, or isn't a file or a directory.
380
+ - `FAILED_TO_BUILD_CONFIG`: Unable to load the environment file.
381
+ - `INVALID_FIREBASE_CONFIG`: The environment variable that holds the Firebase config is missing or
382
+ - empty.
383
+ - `FAILED_TO_READ_FIREBASE_CONFIG`: Unable to read the Firebase config from the environment
384
+ - variable.
385
+
386
+ **Example**
387
+
388
+ ```ts
389
+ import { buildServiceWorker } from 'jg-sw/build';
390
+
391
+ const source = await buildServiceWorker(
392
+ {
393
+ outDir: 'dist',
394
+ cacheNamePrefix: 'my-app',
395
+ template: 'base',
396
+ includeToPrecache: ['/', '/assets'],
397
+ excludeFilesFromPrecache: [],
398
+ excludeMIMETypesFromCache: ['application/json'],
399
+ },
400
+ { caching: true },
401
+ );
402
+ ```
403
+
404
+ #### <a id="api-build-readServiceWorkerConfig"></a>`readServiceWorkerConfig`
405
+
406
+ *Function*
407
+
408
+ Reads and validates a service worker config file, such as `jg-sw.config.json`.
409
+
410
+ ```ts
411
+ readServiceWorkerConfig(
412
+ path: string,
413
+ ): Promise<{ outDir: string; cacheNamePrefix: string; includeToPrecache: string[]; excludeFilesFromPrecache: string[]; excludeMIMETypesFromCache: string[]; template: "base" } | { outDir: string; cacheNamePrefix: string; includeToPrecache: string[]; excludeFilesFromPrecache: string[]; excludeMIMETypesFromCache: string[]; template: "firebase-fcm"; firebaseConfigProcessEnvKey: string; firebaseSdkVersion: string }>
414
+ ```
415
+
416
+ **Parameters**
417
+
418
+ - `path` `string`: The config file's path.
419
+
420
+ **Returns** `Promise<{ outDir: string; cacheNamePrefix: string; includeToPrecache: string[]; excludeFilesFromPrecache: string[]; excludeMIMETypesFromCache: string[]; template: "base" } | { outDir: string; cacheNamePrefix: string; includeToPrecache: string[]; excludeFilesFromPrecache: string[]; excludeMIMETypesFromCache: string[]; template: "firebase-fcm"; firebaseConfigProcessEnvKey: string; firebaseSdkVersion: string }>`: A promise that resolves to the config.
421
+
422
+ **Throws**
423
+
424
+ - `FAILED_TO_READ_BASE_CONFIG`: Unable to read the service worker config, or it is invalid.
425
+ - `INVALID_TEMPLATE_NAME`: The template must be 'base' or 'firebase-fcm'.
426
+ - `INVALID_FIREBASE_SDK_VERSION`: The Firebase SDK version must be a version such as 12.4.0.
427
+
428
+ **Example**
429
+
430
+ ```ts
431
+ import { readServiceWorkerConfig } from 'jg-sw/build';
432
+
433
+ const config = await readServiceWorkerConfig('jg-sw.config.json');
434
+ config.template; // 'base'
435
+ ```
436
+
437
+ #### <a id="api-build-serviceWorkerPlugin"></a>`serviceWorkerPlugin`
438
+
439
+ *Function*
440
+
441
+ Creates a Vite plugin that generates the service worker. After a production build, it writes `sw.js` into the config's outDir, once the client bundle is written. During development, the dev server answers `/sw.js` with a worker generated with `caching: false`, so every request reaches the dev server and updates show up without stale caches. Paths are resolved from Vite's root unless `cwd` is set. When generating fails, the build fails with writeServiceWorker's error, and the dev server answers the worker's request with it.
442
+
443
+ ```ts
444
+ serviceWorkerPlugin(
445
+ options: { config: string | { outDir: string; cacheNamePrefix: string; includeToPrecache: string[]; excludeFilesFromPrecache: string[]; excludeMIMETypesFromCache: string[]; template: "base" } | { outDir: string; cacheNamePrefix: string; includeToPrecache: string[]; excludeFilesFromPrecache: string[]; excludeMIMETypesFromCache: string[]; template: "firebase-fcm"; firebaseConfigProcessEnvKey: string; firebaseSdkVersion: string } } & IBuildServiceWorkerOptions,
446
+ ): Plugin
447
+ ```
448
+
449
+ **Parameters**
450
+
451
+ - `options` `{ config: string | { outDir: string; cacheNamePrefix: string; includeToPrecache: string[]; excludeFilesFromPrecache: string[]; excludeMIMETypesFromCache: string[]; template: "base" } | { outDir: string; cacheNamePrefix: string; includeToPrecache: string[]; excludeFilesFromPrecache: string[]; excludeMIMETypesFromCache: string[]; template: "firebase-fcm"; firebaseConfigProcessEnvKey: string; firebaseSdkVersion: string } } & IBuildServiceWorkerOptions`: The config, as a file path or an object, plus the build options. The dev server uses the environment option, or `development` when it isn't set.
452
+
453
+ **Returns** `Plugin`: The Vite plugin.
454
+
455
+ **Example**
456
+
457
+ ```ts
458
+ // vite.config.ts
459
+ import { defineConfig } from 'vite';
460
+ import { serviceWorkerPlugin } from 'jg-sw/build';
461
+
462
+ export default defineConfig({
463
+ plugins: [serviceWorkerPlugin({ config: 'jg-sw.config.json', environment: 'production' })],
464
+ });
465
+ ```
466
+
467
+ #### <a id="api-build-writeServiceWorker"></a>`writeServiceWorker`
468
+
469
+ *Function*
470
+
471
+ Generates the service worker and writes it to `sw.js` in the config's outDir, after the app's build has written its files there.
472
+
473
+ ```ts
474
+ writeServiceWorker(
475
+ config: { outDir: string; cacheNamePrefix: string; includeToPrecache: string[]; excludeFilesFromPrecache: string[]; excludeMIMETypesFromCache: string[]; template: "base" } | { outDir: string; cacheNamePrefix: string; includeToPrecache: string[]; excludeFilesFromPrecache: string[]; excludeMIMETypesFromCache: string[]; template: "firebase-fcm"; firebaseConfigProcessEnvKey: string; firebaseSdkVersion: string },
476
+ options?: IBuildServiceWorkerOptions,
477
+ ): Promise<string>
478
+ ```
479
+
480
+ **Parameters**
481
+
482
+ - `config` `{ outDir: string; cacheNamePrefix: string; includeToPrecache: string[]; excludeFilesFromPrecache: string[]; excludeMIMETypesFromCache: string[]; template: "base" } | { outDir: string; cacheNamePrefix: string; includeToPrecache: string[]; excludeFilesFromPrecache: string[]; excludeMIMETypesFromCache: string[]; template: "firebase-fcm"; firebaseConfigProcessEnvKey: string; firebaseSdkVersion: string }`: The service worker config, as read by readServiceWorkerConfig or written inline.
483
+ - `options?` `IBuildServiceWorkerOptions`: The environment the firebase-fcm template reads its Firebase config from, whether the worker caches (false for development), and the directory paths are resolved from. Defaults to `{}`.
484
+
485
+ **Returns** `Promise<string>`: A promise that resolves to the written file's path.
486
+
487
+ **Throws**
488
+
489
+ - `FAILED_TO_READ_BASE_CONFIG`: The service worker config is invalid.
490
+ - `INVALID_TEMPLATE_NAME`: The template must be 'base' or 'firebase-fcm'.
491
+ - `INVALID_FIREBASE_SDK_VERSION`: The Firebase SDK version must be a version such as 12.4.0.
492
+ - `INVALID_ENVIRONMENT`: The environment isn't supported, or the firebase-fcm template needs one.
493
+ - `NOT_A_PATH_ELEMENT`: An asset to precache is outside outDir, or isn't a file or a directory.
494
+ - `FAILED_TO_BUILD_CONFIG`: Unable to load the environment file.
495
+ - `INVALID_FIREBASE_CONFIG`: The environment variable that holds the Firebase config is missing or
496
+ - empty.
497
+ - `FAILED_TO_READ_FIREBASE_CONFIG`: Unable to read the Firebase config from the environment
498
+ - variable.
499
+
500
+ **Example**
501
+
502
+ ```ts
503
+ import { readServiceWorkerConfig, writeServiceWorker } from 'jg-sw/build';
504
+
505
+ const config = await readServiceWorkerConfig('jg-sw.config.json');
506
+ await writeServiceWorker(config, { environment: 'production' }); // '/app/dist/sw.js'
507
+ ```
508
+
509
+ ### <a id="api-build-constants"></a>Constants in `jg-sw/build`
510
+
511
+ #### <a id="api-build-ERRORS"></a>`ERRORS`
512
+
513
+ *Constant*
514
+
515
+ the codes for every error thrown by the build side
516
+
517
+ ```ts
518
+ const ERRORS: { INVALID_TEMPLATE_NAME: "INVALID_TEMPLATE_NAME"; NOT_A_PATH_ELEMENT: "NOT_A_PATH_ELEMENT"; FAILED_TO_READ_BASE_CONFIG: "FAILED_TO_READ_BASE_CONFIG"; INVALID_FIREBASE_CONFIG: "INVALID_FIREBASE_CONFIG"; INVALID_ENVIRONMENT: "INVALID_ENVIRONMENT"; FAILED_TO_BUILD_CONFIG: "FAILED_TO_BUILD_CONFIG"; FAILED_TO_READ_FIREBASE_CONFIG: "FAILED_TO_READ_FIREBASE_CONFIG"; INVALID_FIREBASE_SDK_VERSION: "INVALID_FIREBASE_SDK_VERSION" }
519
+ ```
520
+
521
+ #### <a id="api-build-ServiceWorkerConfigSchema"></a>`ServiceWorkerConfigSchema`
522
+
523
+ *Constant*
524
+
525
+ The schema of a service worker config: the `jg-sw.config.json` file the CLI reads, or the object passed to `buildServiceWorker`. `template` selects the worker: `base` caches the app, and `firebase-fcm` also shows Firebase Cloud Messaging notifications.
526
+
527
+ ```ts
528
+ const ServiceWorkerConfigSchema: ZodDiscriminatedUnion<[ZodObject<{ outDir: ZodString; cacheNamePrefix: ZodString; includeToPrecache: ZodArray<ZodString>; excludeFilesFromPrecache: ZodArray<ZodString>; excludeMIMETypesFromCache: ZodArray<ZodString>; template: ZodLiteral<"base"> }, $strict>, ZodObject<{ outDir: ZodString; cacheNamePrefix: ZodString; includeToPrecache: ZodArray<ZodString>; excludeFilesFromPrecache: ZodArray<ZodString>; excludeMIMETypesFromCache: ZodArray<ZodString>; template: ZodLiteral<"firebase-fcm">; firebaseConfigProcessEnvKey: ZodString; firebaseSdkVersion: ZodString }, $strict>], "template">
529
+ ```
530
+
531
+ **Example**
532
+
533
+ ```ts
534
+ import { ServiceWorkerConfigSchema } from 'jg-sw/build';
535
+
536
+ const config = ServiceWorkerConfigSchema.parse({
537
+ outDir: 'dist',
538
+ cacheNamePrefix: 'my-app',
539
+ template: 'base',
540
+ includeToPrecache: ['/', '/index.html', '/assets'],
541
+ excludeFilesFromPrecache: [],
542
+ excludeMIMETypesFromCache: ['application/json'],
543
+ });
544
+ ```
545
+
546
+ ### <a id="api-build-types"></a>Types in `jg-sw/build`
547
+
548
+ #### <a id="api-build-IBuildServiceWorkerOptions"></a>`IBuildServiceWorkerOptions`
549
+
550
+ *Type*
551
+
552
+ the options buildServiceWorker and writeServiceWorker accept
553
+
554
+ ```ts
555
+ type IBuildServiceWorkerOptions = {
556
+ environment?: 'development' | 'staging' | 'production';
557
+ caching?: boolean;
558
+ cwd?: string;
559
+ };
560
+ ```
561
+
562
+ **Members**
563
+
564
+ - `environment: "development" | "staging" | "production"` (property): the environment whose file the firebase-fcm template reads its Firebase config from: .env, .env.staging, or .env.production. The firebase-fcm template requires it
565
+ - `caching: boolean` (property): false leaves out precaching, cache cleanup, and the fetch handler, for development. Defaults to true
566
+ - `cwd: string` (property): the directory outDir and the environment files are resolved from. Defaults to process.cwd()
567
+
568
+ #### <a id="api-build-IServiceWorkerConfig"></a>`IServiceWorkerConfig`
569
+
570
+ *Type*
571
+
572
+ a service worker config, validated by ServiceWorkerConfigSchema
573
+
574
+ ```ts
575
+ type IServiceWorkerConfig = z.infer<typeof ServiceWorkerConfigSchema>;
576
+ ```
package/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.1.0
4
+
5
+ A fresh start: breaking changes in a minor version, once, because nothing depended on 1.0.x.
6
+
7
+ ### Added
8
+
9
+ - `ERRORS.SW_REGISTRATION_FAILED`, the code of a registration the browser rejects, with the browser's error as `cause`.
10
+ - `API.md`, the full API reference, generated from the TSDoc of the version it ships with, so it can be read from `node_modules` without a browser.
11
+
12
+ ### Changed
13
+
14
+ - `registrationError` in the snapshot is an `Exception` with one of the `ERRORS` codes, instead of an encoded message.
15
+ - Errors are `Exception`s from `jg-exception` 1.1.0: the code, the data, and the cause each have their own field, nothing is encoded into the message, and `toJSON()` and `toString()` follow `jg-exception`.
16
+ - Error messages say what failed at that step and keep the error underneath as `cause`, instead of repeating its message, so `extractMessage` prints each layer of the chain once.
17
+
3
18
  ## 1.0.1
4
19
 
5
20
  ### Changed
@@ -8,35 +23,13 @@
8
23
 
9
24
  ## 1.0.0
10
25
 
11
- The first release of `jg-sw`, which replaces `sw-service` 1.2.0 and `sw-builder` 1.2.1.
26
+ The first release.
12
27
 
13
28
  ### Added
14
29
 
15
- - `registerServiceWorker`, `getServiceWorkerSnapshot`, `subscribeToServiceWorker`, `updateApp`, `canInstallApp`, and `installApp`, which replace the `SWService` object, with `ERRORS` and the `REGISTRATION_DURATION_SECONDS`, `UPDATE_INSTALL_TIMEOUT_SECONDS`, `DEFAULT_SERVICE_WORKER_PATH`, `DEFAULT_SERVICE_WORKER_SCOPE`, and `SKIP_WAITING_MESSAGE_TYPE` constants.
30
+ - `registerServiceWorker`, `getServiceWorkerSnapshot`, `subscribeToServiceWorker`, `updateApp`, `canInstallApp`, and `installApp`, with `ERRORS` and the `REGISTRATION_DURATION_SECONDS`, `UPDATE_INSTALL_TIMEOUT_SECONDS`, `DEFAULT_SERVICE_WORKER_PATH`, `DEFAULT_SERVICE_WORKER_SCOPE`, and `SKIP_WAITING_MESSAGE_TYPE` constants.
16
31
  - A snapshot whose identity changes only with the state, for `useSyncExternalStore`, with `hasWaitingWorker` and the installation state.
17
32
  - `jg-sw/build`, with `buildServiceWorker`, `writeServiceWorker`, `readServiceWorkerConfig`, `ServiceWorkerConfigSchema`, and `ERRORS`, the codes of every error it throws.
18
33
  - `serviceWorkerPlugin`, a Vite plugin that writes the worker after a build and serves it without caching in development.
19
34
  - The `jg-sw` CLI, with `--config`, `--environment`, `--no-caching`, and `--help`.
20
- - The worker activates when the page posts the skip-waiting message.
21
- - `caching: false` and `--no-caching`, which generate a worker without precaching, cache cleanup, or a fetch handler.
22
- - The `INVALID_FIREBASE_SDK_VERSION` code.
23
-
24
- ### Changed
25
-
26
- - `updateApp()` activates the waiting worker and reloads once it controls the page. Without one, it waits up to 30 seconds for a new worker to install, and reloads as it is when none installs.
27
- - `registerServiceWorker()` resolves once the registration settles, never rejects, and returns the first call's promise on later calls.
28
- - `installApp()` resolves to the prompt's outcome.
29
- - The install prompt is tracked from the first `registerServiceWorker()` call, before the registration settles.
30
- - Logs appear only with `debugMode`, and nothing touches `window` or `navigator` when the module loads.
31
- - The worker never caches responses marked `Cache-Control: no-store` or `private`.
32
- - The config file is named `jg-sw.config.json`, and the Firebase background hook `self.jgSwFcmBackgroundMessageHandler`.
33
- - `firebaseSdkVersion` must be a version such as `12.4.0`. An invalid template fails with `INVALID_TEMPLATE_NAME`, and an unsupported environment with `INVALID_ENVIRONMENT`, whatever the template.
34
- - Environment files load with Node.js's `process.loadEnvFile`.
35
- - Precached paths are URL paths from `outDir`, with `#`, `?`, and `%` percent-encoded and each directory's files sorted. A path outside `outDir` fails with `NOT_A_PATH_ELEMENT`.
36
- - Values written into the worker are serialized as JSON.
37
- - Errors are `Exception`s from `jg-exception`.
38
-
39
- ### Removed
40
-
41
- - The `SWService` object and its `ISWService`, `IAppInstallerService`, and `ISWRegistrationOptions` types.
42
- - Running the CLI on import, and the `dotenv` and `argv-utils` dependencies.
35
+ - A generated worker that activates when the page posts the skip-waiting message, never caches responses marked `Cache-Control: no-store` or `private`, and can be generated without caching for development.