jg-sw 1.1.1 → 1.2.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/API.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # <a id="api-reference"></a>jg-sw API reference
2
2
 
3
- Every export of `jg-sw@1.1.1`, 26 in all, with its signature, its errors, and an example. It's generated from the package's TSDoc.
3
+ Every export of `jg-sw@1.2.0`, 26 in all, with its signature, its errors, and an example. It's generated from the package's TSDoc.
4
4
 
5
5
  ## <a id="api-entry-main"></a>`jg-sw`
6
6
 
@@ -358,14 +358,14 @@ Generates the service worker's source from a config, without writing it. Every g
358
358
 
359
359
  ```ts
360
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 },
361
+ config: { outDir: string; cacheNamePrefix: string; includeToPrecache: string[]; excludeFilesFromPrecache: string[]; excludeMIMETypesFromCache: string[]; navigationFallback?: string; template: "base" } | { outDir: string; cacheNamePrefix: string; includeToPrecache: string[]; excludeFilesFromPrecache: string[]; excludeMIMETypesFromCache: string[]; navigationFallback?: string; template: "firebase-fcm"; firebaseConfigProcessEnvKey: string; firebaseSdkVersion: string },
362
362
  options?: IBuildServiceWorkerOptions,
363
363
  ): Promise<string>
364
364
  ```
365
365
 
366
366
  **Parameters**
367
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.
368
+ - `config` `{ outDir: string; cacheNamePrefix: string; includeToPrecache: string[]; excludeFilesFromPrecache: string[]; excludeMIMETypesFromCache: string[]; navigationFallback?: string; template: "base" } | { outDir: string; cacheNamePrefix: string; includeToPrecache: string[]; excludeFilesFromPrecache: string[]; excludeMIMETypesFromCache: string[]; navigationFallback?: string; template: "firebase-fcm"; firebaseConfigProcessEnvKey: string; firebaseSdkVersion: string }`: The service worker config, as read by readServiceWorkerConfig or written inline.
369
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
370
 
371
371
  **Returns** `Promise<string>`: A promise that resolves to the worker's source.
@@ -377,6 +377,7 @@ buildServiceWorker(
377
377
  - `INVALID_FIREBASE_SDK_VERSION`: The Firebase SDK version must be a version such as 12.4.0.
378
378
  - `INVALID_ENVIRONMENT`: The environment isn't supported, or the firebase-fcm template needs one.
379
379
  - `NOT_A_PATH_ELEMENT`: An asset to precache is outside outDir, or isn't a file or a directory.
380
+ - `NAVIGATION_FALLBACK_NOT_PRECACHED`: The navigation fallback isn't one of the precached paths.
380
381
  - `FAILED_TO_BUILD_CONFIG`: Unable to load the environment file.
381
382
  - `INVALID_FIREBASE_CONFIG`: The environment variable that holds the Firebase config is missing or
382
383
  - empty.
@@ -410,14 +411,14 @@ Reads and validates a service worker config file, such as `jg-sw.config.json`.
410
411
  ```ts
411
412
  readServiceWorkerConfig(
412
413
  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
+ ): Promise<{ outDir: string; cacheNamePrefix: string; includeToPrecache: string[]; excludeFilesFromPrecache: string[]; excludeMIMETypesFromCache: string[]; navigationFallback?: string; template: "base" } | { outDir: string; cacheNamePrefix: string; includeToPrecache: string[]; excludeFilesFromPrecache: string[]; excludeMIMETypesFromCache: string[]; navigationFallback?: string; template: "firebase-fcm"; firebaseConfigProcessEnvKey: string; firebaseSdkVersion: string }>
414
415
  ```
415
416
 
416
417
  **Parameters**
417
418
 
418
419
  - `path` `string`: The config file's path.
419
420
 
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
+ **Returns** `Promise<{ outDir: string; cacheNamePrefix: string; includeToPrecache: string[]; excludeFilesFromPrecache: string[]; excludeMIMETypesFromCache: string[]; navigationFallback?: string; template: "base" } | { outDir: string; cacheNamePrefix: string; includeToPrecache: string[]; excludeFilesFromPrecache: string[]; excludeMIMETypesFromCache: string[]; navigationFallback?: string; template: "firebase-fcm"; firebaseConfigProcessEnvKey: string; firebaseSdkVersion: string }>`: A promise that resolves to the config.
421
422
 
422
423
  **Throws**
423
424
 
@@ -442,13 +443,13 @@ Creates a Vite plugin that generates the service worker. After a production buil
442
443
 
443
444
  ```ts
444
445
  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
+ options: { config: string | { outDir: string; cacheNamePrefix: string; includeToPrecache: string[]; excludeFilesFromPrecache: string[]; excludeMIMETypesFromCache: string[]; navigationFallback?: string; template: "base" } | { outDir: string; cacheNamePrefix: string; includeToPrecache: string[]; excludeFilesFromPrecache: string[]; excludeMIMETypesFromCache: string[]; navigationFallback?: string; template: "firebase-fcm"; firebaseConfigProcessEnvKey: string; firebaseSdkVersion: string } } & IBuildServiceWorkerOptions,
446
447
  ): Plugin
447
448
  ```
448
449
 
449
450
  **Parameters**
450
451
 
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
+ - `options` `{ config: string | { outDir: string; cacheNamePrefix: string; includeToPrecache: string[]; excludeFilesFromPrecache: string[]; excludeMIMETypesFromCache: string[]; navigationFallback?: string; template: "base" } | { outDir: string; cacheNamePrefix: string; includeToPrecache: string[]; excludeFilesFromPrecache: string[]; excludeMIMETypesFromCache: string[]; navigationFallback?: 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
 
453
454
  **Returns** `Plugin`: The Vite plugin.
454
455
 
@@ -472,14 +473,14 @@ Generates the service worker and writes it to `sw.js` in the config's outDir, af
472
473
 
473
474
  ```ts
474
475
  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
+ config: { outDir: string; cacheNamePrefix: string; includeToPrecache: string[]; excludeFilesFromPrecache: string[]; excludeMIMETypesFromCache: string[]; navigationFallback?: string; template: "base" } | { outDir: string; cacheNamePrefix: string; includeToPrecache: string[]; excludeFilesFromPrecache: string[]; excludeMIMETypesFromCache: string[]; navigationFallback?: string; template: "firebase-fcm"; firebaseConfigProcessEnvKey: string; firebaseSdkVersion: string },
476
477
  options?: IBuildServiceWorkerOptions,
477
478
  ): Promise<string>
478
479
  ```
479
480
 
480
481
  **Parameters**
481
482
 
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
+ - `config` `{ outDir: string; cacheNamePrefix: string; includeToPrecache: string[]; excludeFilesFromPrecache: string[]; excludeMIMETypesFromCache: string[]; navigationFallback?: string; template: "base" } | { outDir: string; cacheNamePrefix: string; includeToPrecache: string[]; excludeFilesFromPrecache: string[]; excludeMIMETypesFromCache: string[]; navigationFallback?: string; template: "firebase-fcm"; firebaseConfigProcessEnvKey: string; firebaseSdkVersion: string }`: The service worker config, as read by readServiceWorkerConfig or written inline.
483
484
  - `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
 
485
486
  **Returns** `Promise<string>`: A promise that resolves to the written file's path.
@@ -491,6 +492,7 @@ writeServiceWorker(
491
492
  - `INVALID_FIREBASE_SDK_VERSION`: The Firebase SDK version must be a version such as 12.4.0.
492
493
  - `INVALID_ENVIRONMENT`: The environment isn't supported, or the firebase-fcm template needs one.
493
494
  - `NOT_A_PATH_ELEMENT`: An asset to precache is outside outDir, or isn't a file or a directory.
495
+ - `NAVIGATION_FALLBACK_NOT_PRECACHED`: The navigation fallback isn't one of the precached paths.
494
496
  - `FAILED_TO_BUILD_CONFIG`: Unable to load the environment file.
495
497
  - `INVALID_FIREBASE_CONFIG`: The environment variable that holds the Firebase config is missing or
496
498
  - empty.
@@ -515,7 +517,7 @@ await writeServiceWorker(config, { environment: 'production' }); // '/app/dist/s
515
517
  the codes for every error thrown by the build side
516
518
 
517
519
  ```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" }
520
+ 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"; NAVIGATION_FALLBACK_NOT_PRECACHED: "NAVIGATION_FALLBACK_NOT_PRECACHED" }
519
521
  ```
520
522
 
521
523
  #### <a id="api-build-ServiceWorkerConfigSchema"></a>`ServiceWorkerConfigSchema`
@@ -525,7 +527,7 @@ const ERRORS: { INVALID_TEMPLATE_NAME: "INVALID_TEMPLATE_NAME"; NOT_A_PATH_ELEME
525
527
  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
528
 
527
529
  ```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">
530
+ const ServiceWorkerConfigSchema: ZodDiscriminatedUnion<[ZodObject<{ outDir: ZodString; cacheNamePrefix: ZodString; includeToPrecache: ZodArray<ZodString>; excludeFilesFromPrecache: ZodArray<ZodString>; excludeMIMETypesFromCache: ZodArray<ZodString>; navigationFallback: ZodOptional<ZodString>; template: ZodLiteral<"base"> }, $strict>, ZodObject<{ outDir: ZodString; cacheNamePrefix: ZodString; includeToPrecache: ZodArray<ZodString>; excludeFilesFromPrecache: ZodArray<ZodString>; excludeMIMETypesFromCache: ZodArray<ZodString>; navigationFallback: ZodOptional<ZodString>; template: ZodLiteral<"firebase-fcm">; firebaseConfigProcessEnvKey: ZodString; firebaseSdkVersion: ZodString }, $strict>], "template">
529
531
  ```
530
532
 
531
533
  **Example**
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.2.0
4
+
5
+ ### Added
6
+
7
+ - `navigationFallback` in the config: a precached page, such as `/index.html`, that answers a navigation the worker can neither find in its cache nor fetch, so a single-page app's routes, such as `/settings`, open offline. Without it, the worker behaves as before.
8
+ - `ERRORS.NAVIGATION_FALLBACK_NOT_PRECACHED`, which the build throws when the fallback isn't one of the precached paths.
9
+
3
10
  ## 1.1.1
4
11
 
5
12
  ### Added
package/README.md CHANGED
@@ -68,14 +68,15 @@ Create `jg-sw.config.json` next to your app's `package.json`:
68
68
  }
69
69
  ```
70
70
 
71
- | Field | What it does |
72
- | --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
73
- | `outDir` | The directory the app's build is written to. The worker is written there as `sw.js`. |
74
- | `template` | `base` caches the app. `firebase-fcm` also shows Firebase Cloud Messaging notifications. |
75
- | `cacheNamePrefix` | The namespace of the worker's caches, in lowercase words separated by hyphens, such as `my-app`. Each build caches under `<cacheNamePrefix>--<10 characters>`. |
76
- | `includeToPrecache` | The files and directories, relative to `outDir`, cached when the worker installs. Directories are read recursively, and `/` stands for the app's root page. |
77
- | `excludeFilesFromPrecache` | File names left out of precaching, wherever they are. |
78
- | `excludeMIMETypesFromCache` | MIME types whose responses are never cached. |
71
+ | Field | What it does |
72
+ | --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
73
+ | `outDir` | The directory the app's build is written to. The worker is written there as `sw.js`. |
74
+ | `template` | `base` caches the app. `firebase-fcm` also shows Firebase Cloud Messaging notifications. |
75
+ | `cacheNamePrefix` | The namespace of the worker's caches, in lowercase words separated by hyphens, such as `my-app`. Each build caches under `<cacheNamePrefix>--<10 characters>`. |
76
+ | `includeToPrecache` | The files and directories, relative to `outDir`, cached when the worker installs. Directories are read recursively, and `/` stands for the app's root page. |
77
+ | `excludeFilesFromPrecache` | File names left out of precaching, wherever they are. |
78
+ | `excludeMIMETypesFromCache` | MIME types whose responses are never cached. |
79
+ | `navigationFallback` | Optional. A precached page, such as `/index.html`, that answers a navigation the worker can't serve offline, so a single-page app's routes still open. The build fails with `NAVIGATION_FALLBACK_NOT_PRECACHED` when it isn't precached. |
79
80
 
80
81
  This config precaches the hashed files under `dist/assets`, but not `/` or `index.html`. Nothing is precached unless it's listed: add `/` and `/index.html` to precache the app's shell.
81
82
 
@@ -134,6 +135,7 @@ await writeServiceWorker(config, { environment: 'production' }); // '/app/dist/s
134
135
  - The page activates a waiting worker by posting `{ type: 'JG_SW_SKIP_WAITING' }` to it.
135
136
  - `activate` deletes the older caches in its own namespace, keeps every other cache, then claims its clients.
136
137
  - It answers same-origin `GET` requests from the current version's cache, then from the network, and caches successful responses unless they're opaque, partial, marked `Vary: *`, marked `Cache-Control: no-store` or `private`, or of an excluded MIME type. A failed cache write never replaces a valid response.
138
+ - When a navigation is in neither its cache nor the network's reach, such as offline, it answers with the `navigationFallback` page when one is set, and otherwise with a `408` network error. A `4xx` or `5xx` from the server is passed through as it is.
137
139
 
138
140
  ### Firebase Cloud Messaging
139
141
 
package/RECIPES.md CHANGED
@@ -28,6 +28,9 @@ export default defineConfig({
28
28
 
29
29
  // the worker answers from its cache first, so keep API data and other pages out of it
30
30
  excludeMIMETypesFromCache: ['application/json', 'text/html'],
31
+
32
+ // offline, a route the app handles, such as /settings, opens through the page
33
+ navigationFallback: '/index.html',
31
34
  },
32
35
  }),
33
36
  ],
@@ -61,7 +64,7 @@ window.addEventListener(
61
64
  );
62
65
  ```
63
66
 
64
- During `vite`, the dev server answers `/sw.js` with a worker that caches nothing, so registration works and every request still reaches the dev server. To try the offline app, run `vite build` and `vite preview`, load the page once, then turn the network off in the browser's devtools. The worker matches each request exactly and has no fallback page, so offline, open the app at `/`: reloading a client-side route, such as `/settings`, gets the worker's network error instead of the app.
67
+ During `vite`, the dev server answers `/sw.js` with a worker that caches nothing, so registration works and every request still reaches the dev server. To try the offline app, run `vite build` and `vite preview`, load the page once, then turn the network off in the browser's devtools. The worker matches each request exactly, so a client-side route, such as `/settings`, is never in its cache. Offline, `navigationFallback` answers the navigation with the precached page, at the route's URL, so the app's router shows it; without it, reloading the route gets the worker's network error. The fallback must be one of the precached paths, or the build fails with `NAVIGATION_FALLBACK_NOT_PRECACHED`.
65
68
 
66
69
  ## Telling people an update is ready
67
70
 
@@ -22,6 +22,7 @@ export declare const ServiceWorkerConfigSchema: z.ZodDiscriminatedUnion<[z.ZodOb
22
22
  includeToPrecache: z.ZodArray<z.ZodString>;
23
23
  excludeFilesFromPrecache: z.ZodArray<z.ZodString>;
24
24
  excludeMIMETypesFromCache: z.ZodArray<z.ZodString>;
25
+ navigationFallback: z.ZodOptional<z.ZodString>;
25
26
  }, z.core.$strict>, z.ZodObject<{
26
27
  template: z.ZodLiteral<"firebase-fcm">;
27
28
  firebaseConfigProcessEnvKey: z.ZodString;
@@ -31,6 +32,7 @@ export declare const ServiceWorkerConfigSchema: z.ZodDiscriminatedUnion<[z.ZodOb
31
32
  includeToPrecache: z.ZodArray<z.ZodString>;
32
33
  excludeFilesFromPrecache: z.ZodArray<z.ZodString>;
33
34
  excludeMIMETypesFromCache: z.ZodArray<z.ZodString>;
35
+ navigationFallback: z.ZodOptional<z.ZodString>;
34
36
  }, z.core.$strict>], "template">;
35
37
  export type IServiceWorkerConfig = z.infer<typeof ServiceWorkerConfigSchema>;
36
38
  export declare const FirebaseOptionsSchema: z.ZodObject<{
@@ -14,6 +14,13 @@ const BaseConfigShape = {
14
14
  excludeFilesFromPrecache: z.array(z.string()),
15
15
  // the MIME types whose responses the worker never caches, such as 'application/json'
16
16
  excludeMIMETypesFromCache: z.array(z.string()),
17
+ // the precached page that answers a navigation the worker can neither find in its cache nor
18
+ // fetch, such as '/index.html' for a single-page app offline. Without it, such a navigation gets
19
+ // the worker's network error
20
+ navigationFallback: z
21
+ .string()
22
+ .startsWith('/', 'The navigation fallback must be a path such as /index.html.')
23
+ .optional(),
17
24
  };
18
25
  // the config of a worker that caches the app
19
26
  const BaseConfigSchema = z.strictObject({
@@ -1 +1 @@
1
- {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../src/build/config/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,yBAAyB,EAAE,4BAA4B,EAAE,MAAM,oBAAoB,CAAC;AAE7F,mCAAmC;AACnC,MAAM,eAAe,GAAG;IACtB,+EAA+E;IAC/E,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAEzB,qFAAqF;IACrF,eAAe,EAAE,CAAC;SACf,MAAM,EAAE;SACR,KAAK,CACJ,yBAAyB,EACzB,wFAAwF,CACzF;IAEH,oFAAoF;IACpF,iBAAiB,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IAEtC,2DAA2D;IAC3D,wBAAwB,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IAE7C,qFAAqF;IACrF,yBAAyB,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;CAC/C,CAAC;AAEF,6CAA6C;AAC7C,MAAM,gBAAgB,GAAG,CAAC,CAAC,YAAY,CAAC;IACtC,GAAG,eAAe;IAClB,QAAQ,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC;CAC5B,CAAC,CAAC;AAEH,8FAA8F;AAC9F,MAAM,uBAAuB,GAAG,CAAC,CAAC,YAAY,CAAC;IAC7C,GAAG,eAAe;IAClB,QAAQ,EAAE,CAAC,CAAC,OAAO,CAAC,cAAc,CAAC;IAEnC,sFAAsF;IACtF,2BAA2B,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAE9C,yEAAyE;IACzE,kBAAkB,EAAE,CAAC;SAClB,MAAM,EAAE;SACR,KAAK,CACJ,4BAA4B,EAC5B,4DAA4D,CAC7D;CACJ,CAAC,CAAC;AAEH;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,CAAC,kBAAkB,CAAC,UAAU,EAAE;IACxE,gBAAgB;IAChB,uBAAuB;CACxB,CAAC,CAAC;AAKH,8DAA8D;AAC9D,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC,YAAY,CAAC;IAClD,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACzB,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC7B,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC5B,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAChC,iBAAiB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACpC,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;CACzB,CAAC,CAAC;AAIH,qFAAqF;AACrF,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC3C,OAAO,EAAE,qBAAqB;CAC/B,CAAC,CAAC","sourcesContent":["import { z } from 'zod';\n\nimport { CACHE_NAME_PREFIX_PATTERN, FIREBASE_SDK_VERSION_PATTERN } from '../shared/index.js';\n\n// the fields every template shares\nconst BaseConfigShape = {\n // the directory the app's build is written to, where the worker is written too\n outDir: z.string().min(1),\n\n // the application-owned namespace the worker's caches are named in, such as 'my-app'\n cacheNamePrefix: z\n .string()\n .regex(\n CACHE_NAME_PREFIX_PATTERN,\n 'The cache name prefix must contain lowercase alphanumeric tokens separated by hyphens.',\n ),\n\n // the files and directories, relative to outDir, the worker caches when it installs\n includeToPrecache: z.array(z.string()),\n\n // the file names left out of precaching, wherever they are\n excludeFilesFromPrecache: z.array(z.string()),\n\n // the MIME types whose responses the worker never caches, such as 'application/json'\n excludeMIMETypesFromCache: z.array(z.string()),\n};\n\n// the config of a worker that caches the app\nconst BaseConfigSchema = z.strictObject({\n ...BaseConfigShape,\n template: z.literal('base'),\n});\n\n// the config of a worker that caches the app and shows Firebase Cloud Messaging notifications\nconst FirebaseFcmConfigSchema = z.strictObject({\n ...BaseConfigShape,\n template: z.literal('firebase-fcm'),\n\n // the environment variable holding the Firebase config, as JSON: { \"options\": { … } }\n firebaseConfigProcessEnvKey: z.string().min(1),\n\n // the Firebase JavaScript SDK version the worker loads, such as '12.4.0'\n firebaseSdkVersion: z\n .string()\n .regex(\n FIREBASE_SDK_VERSION_PATTERN,\n 'The Firebase SDK version must be a version such as 12.4.0.',\n ),\n});\n\n/**\n * The schema of a service worker config: the `jg-sw.config.json` file the CLI reads, or the\n * object passed to `buildServiceWorker`. `template` selects the worker: `base` caches the app,\n * and `firebase-fcm` also shows Firebase Cloud Messaging notifications.\n * @example\n * import { ServiceWorkerConfigSchema } from 'jg-sw/build';\n *\n * const config = ServiceWorkerConfigSchema.parse({\n * outDir: 'dist',\n * cacheNamePrefix: 'my-app',\n * template: 'base',\n * includeToPrecache: ['/', '/index.html', '/assets'],\n * excludeFilesFromPrecache: [],\n * excludeMIMETypesFromCache: ['application/json'],\n * });\n */\nexport const ServiceWorkerConfigSchema = z.discriminatedUnion('template', [\n BaseConfigSchema,\n FirebaseFcmConfigSchema,\n]);\n\n// a service worker config, validated by ServiceWorkerConfigSchema\nexport type IServiceWorkerConfig = z.infer<typeof ServiceWorkerConfigSchema>;\n\n// the options Firebase's initializeApp receives in the worker\nexport const FirebaseOptionsSchema = z.strictObject({\n apiKey: z.string().min(1),\n authDomain: z.string().min(1),\n projectId: z.string().min(1),\n storageBucket: z.string().min(1),\n messagingSenderId: z.string().min(1),\n appId: z.string().min(1),\n});\n\nexport type IFirebaseOptions = z.infer<typeof FirebaseOptionsSchema>;\n\n// the JSON value the environment variable named by firebaseConfigProcessEnvKey holds\nexport const FirebaseConfigSchema = z.object({\n options: FirebaseOptionsSchema,\n});\n"]}
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../src/build/config/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,yBAAyB,EAAE,4BAA4B,EAAE,MAAM,oBAAoB,CAAC;AAE7F,mCAAmC;AACnC,MAAM,eAAe,GAAG;IACtB,+EAA+E;IAC/E,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAEzB,qFAAqF;IACrF,eAAe,EAAE,CAAC;SACf,MAAM,EAAE;SACR,KAAK,CACJ,yBAAyB,EACzB,wFAAwF,CACzF;IAEH,oFAAoF;IACpF,iBAAiB,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IAEtC,2DAA2D;IAC3D,wBAAwB,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IAE7C,qFAAqF;IACrF,yBAAyB,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IAE9C,4FAA4F;IAC5F,iGAAiG;IACjG,6BAA6B;IAC7B,kBAAkB,EAAE,CAAC;SAClB,MAAM,EAAE;SACR,UAAU,CAAC,GAAG,EAAE,6DAA6D,CAAC;SAC9E,QAAQ,EAAE;CACd,CAAC;AAEF,6CAA6C;AAC7C,MAAM,gBAAgB,GAAG,CAAC,CAAC,YAAY,CAAC;IACtC,GAAG,eAAe;IAClB,QAAQ,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC;CAC5B,CAAC,CAAC;AAEH,8FAA8F;AAC9F,MAAM,uBAAuB,GAAG,CAAC,CAAC,YAAY,CAAC;IAC7C,GAAG,eAAe;IAClB,QAAQ,EAAE,CAAC,CAAC,OAAO,CAAC,cAAc,CAAC;IAEnC,sFAAsF;IACtF,2BAA2B,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAE9C,yEAAyE;IACzE,kBAAkB,EAAE,CAAC;SAClB,MAAM,EAAE;SACR,KAAK,CACJ,4BAA4B,EAC5B,4DAA4D,CAC7D;CACJ,CAAC,CAAC;AAEH;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,CAAC,kBAAkB,CAAC,UAAU,EAAE;IACxE,gBAAgB;IAChB,uBAAuB;CACxB,CAAC,CAAC;AAKH,8DAA8D;AAC9D,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC,YAAY,CAAC;IAClD,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACzB,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC7B,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC5B,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAChC,iBAAiB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACpC,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;CACzB,CAAC,CAAC;AAIH,qFAAqF;AACrF,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC3C,OAAO,EAAE,qBAAqB;CAC/B,CAAC,CAAC","sourcesContent":["import { z } from 'zod';\n\nimport { CACHE_NAME_PREFIX_PATTERN, FIREBASE_SDK_VERSION_PATTERN } from '../shared/index.js';\n\n// the fields every template shares\nconst BaseConfigShape = {\n // the directory the app's build is written to, where the worker is written too\n outDir: z.string().min(1),\n\n // the application-owned namespace the worker's caches are named in, such as 'my-app'\n cacheNamePrefix: z\n .string()\n .regex(\n CACHE_NAME_PREFIX_PATTERN,\n 'The cache name prefix must contain lowercase alphanumeric tokens separated by hyphens.',\n ),\n\n // the files and directories, relative to outDir, the worker caches when it installs\n includeToPrecache: z.array(z.string()),\n\n // the file names left out of precaching, wherever they are\n excludeFilesFromPrecache: z.array(z.string()),\n\n // the MIME types whose responses the worker never caches, such as 'application/json'\n excludeMIMETypesFromCache: z.array(z.string()),\n\n // the precached page that answers a navigation the worker can neither find in its cache nor\n // fetch, such as '/index.html' for a single-page app offline. Without it, such a navigation gets\n // the worker's network error\n navigationFallback: z\n .string()\n .startsWith('/', 'The navigation fallback must be a path such as /index.html.')\n .optional(),\n};\n\n// the config of a worker that caches the app\nconst BaseConfigSchema = z.strictObject({\n ...BaseConfigShape,\n template: z.literal('base'),\n});\n\n// the config of a worker that caches the app and shows Firebase Cloud Messaging notifications\nconst FirebaseFcmConfigSchema = z.strictObject({\n ...BaseConfigShape,\n template: z.literal('firebase-fcm'),\n\n // the environment variable holding the Firebase config, as JSON: { \"options\": { … } }\n firebaseConfigProcessEnvKey: z.string().min(1),\n\n // the Firebase JavaScript SDK version the worker loads, such as '12.4.0'\n firebaseSdkVersion: z\n .string()\n .regex(\n FIREBASE_SDK_VERSION_PATTERN,\n 'The Firebase SDK version must be a version such as 12.4.0.',\n ),\n});\n\n/**\n * The schema of a service worker config: the `jg-sw.config.json` file the CLI reads, or the\n * object passed to `buildServiceWorker`. `template` selects the worker: `base` caches the app,\n * and `firebase-fcm` also shows Firebase Cloud Messaging notifications.\n * @example\n * import { ServiceWorkerConfigSchema } from 'jg-sw/build';\n *\n * const config = ServiceWorkerConfigSchema.parse({\n * outDir: 'dist',\n * cacheNamePrefix: 'my-app',\n * template: 'base',\n * includeToPrecache: ['/', '/index.html', '/assets'],\n * excludeFilesFromPrecache: [],\n * excludeMIMETypesFromCache: ['application/json'],\n * });\n */\nexport const ServiceWorkerConfigSchema = z.discriminatedUnion('template', [\n BaseConfigSchema,\n FirebaseFcmConfigSchema,\n]);\n\n// a service worker config, validated by ServiceWorkerConfigSchema\nexport type IServiceWorkerConfig = z.infer<typeof ServiceWorkerConfigSchema>;\n\n// the options Firebase's initializeApp receives in the worker\nexport const FirebaseOptionsSchema = z.strictObject({\n apiKey: z.string().min(1),\n authDomain: z.string().min(1),\n projectId: z.string().min(1),\n storageBucket: z.string().min(1),\n messagingSenderId: z.string().min(1),\n appId: z.string().min(1),\n});\n\nexport type IFirebaseOptions = z.infer<typeof FirebaseOptionsSchema>;\n\n// the JSON value the environment variable named by firebaseConfigProcessEnvKey holds\nexport const FirebaseConfigSchema = z.object({\n options: FirebaseOptionsSchema,\n});\n"]}
@@ -28,6 +28,7 @@ import { type IBuildServiceWorkerOptions } from '../shared/index.js';
28
28
  * - INVALID_FIREBASE_SDK_VERSION: The Firebase SDK version must be a version such as 12.4.0.
29
29
  * - INVALID_ENVIRONMENT: The environment isn't supported, or the firebase-fcm template needs one.
30
30
  * - NOT_A_PATH_ELEMENT: An asset to precache is outside outDir, or isn't a file or a directory.
31
+ * - NAVIGATION_FALLBACK_NOT_PRECACHED: The navigation fallback isn't one of the precached paths.
31
32
  * - FAILED_TO_BUILD_CONFIG: Unable to load the environment file.
32
33
  * - INVALID_FIREBASE_CONFIG: The environment variable that holds the Firebase config is missing or
33
34
  * empty.
@@ -53,6 +54,7 @@ export declare const buildServiceWorker: (config: IServiceWorkerConfig, options?
53
54
  * - INVALID_FIREBASE_SDK_VERSION: The Firebase SDK version must be a version such as 12.4.0.
54
55
  * - INVALID_ENVIRONMENT: The environment isn't supported, or the firebase-fcm template needs one.
55
56
  * - NOT_A_PATH_ELEMENT: An asset to precache is outside outDir, or isn't a file or a directory.
57
+ * - NAVIGATION_FALLBACK_NOT_PRECACHED: The navigation fallback isn't one of the precached paths.
56
58
  * - FAILED_TO_BUILD_CONFIG: Unable to load the environment file.
57
59
  * - INVALID_FIREBASE_CONFIG: The environment variable that holds the Firebase config is missing or
58
60
  * empty.
@@ -1,10 +1,11 @@
1
1
  import { join, resolve } from 'node:path';
2
+ import { Exception } from 'jg-exception';
2
3
  import { writeTextFile } from 'jg-fs';
3
4
  import { generateRandomString } from 'jg-utilities';
4
5
  import { SERVICE_WORKER_FILE_NAME } from '../../shared/index.js';
5
6
  import { parseEnvironment, parseServiceWorkerConfig, readFirebaseOptions, } from '../config/index.js';
6
7
  import { buildPrecacheAssetPaths } from '../precache/index.js';
7
- import { CACHE_NAME_CHARACTERS, CACHE_NAME_LENGTH, CACHE_NAME_SEPARATOR, } from '../shared/index.js';
8
+ import { CACHE_NAME_CHARACTERS, CACHE_NAME_LENGTH, CACHE_NAME_SEPARATOR, ERRORS, } from '../shared/index.js';
8
9
  import { renderServiceWorker } from '../worker/index.js';
9
10
  /**
10
11
  * Generates the worker and works out the directory it belongs in.
@@ -18,15 +19,22 @@ const generateServiceWorker = async (config, options) => {
18
19
  const cwd = options.cwd ?? process.cwd();
19
20
  const caching = options.caching ?? true;
20
21
  const outDir = resolve(cwd, validConfig.outDir);
22
+ const precacheAssets = caching
23
+ ? await buildPrecacheAssetPaths(outDir, validConfig.includeToPrecache, validConfig.excludeFilesFromPrecache)
24
+ : [];
25
+ const navigationFallback = validConfig.navigationFallback ?? null;
26
+ // offline, only a precached page can answer
27
+ if (caching && navigationFallback !== null && !precacheAssets.includes(navigationFallback)) {
28
+ throw new Exception(`The navigation fallback '${navigationFallback}' isn't precached. Add it to includeToPrecache.`, { code: ERRORS.NAVIGATION_FALLBACK_NOT_PRECACHED, data: { navigationFallback } });
29
+ }
21
30
  const source = await renderServiceWorker({
22
31
  template: validConfig.template,
23
32
  cacheNamePrefix: validConfig.cacheNamePrefix,
24
33
  cacheName: `${validConfig.cacheNamePrefix}${CACHE_NAME_SEPARATOR}${generateRandomString(CACHE_NAME_LENGTH, CACHE_NAME_CHARACTERS)}`,
25
- precacheAssets: caching
26
- ? await buildPrecacheAssetPaths(outDir, validConfig.includeToPrecache, validConfig.excludeFilesFromPrecache)
27
- : [],
34
+ precacheAssets,
28
35
  excludeMIMETypes: validConfig.excludeMIMETypesFromCache,
29
36
  caching,
37
+ navigationFallback,
30
38
  ...(validConfig.template === 'firebase-fcm'
31
39
  ? {
32
40
  firebase: {
@@ -66,6 +74,7 @@ const generateServiceWorker = async (config, options) => {
66
74
  * - INVALID_FIREBASE_SDK_VERSION: The Firebase SDK version must be a version such as 12.4.0.
67
75
  * - INVALID_ENVIRONMENT: The environment isn't supported, or the firebase-fcm template needs one.
68
76
  * - NOT_A_PATH_ELEMENT: An asset to precache is outside outDir, or isn't a file or a directory.
77
+ * - NAVIGATION_FALLBACK_NOT_PRECACHED: The navigation fallback isn't one of the precached paths.
69
78
  * - FAILED_TO_BUILD_CONFIG: Unable to load the environment file.
70
79
  * - INVALID_FIREBASE_CONFIG: The environment variable that holds the Firebase config is missing or
71
80
  * empty.
@@ -91,6 +100,7 @@ export const buildServiceWorker = async (config, options = {}) => (await generat
91
100
  * - INVALID_FIREBASE_SDK_VERSION: The Firebase SDK version must be a version such as 12.4.0.
92
101
  * - INVALID_ENVIRONMENT: The environment isn't supported, or the firebase-fcm template needs one.
93
102
  * - NOT_A_PATH_ELEMENT: An asset to precache is outside outDir, or isn't a file or a directory.
103
+ * - NAVIGATION_FALLBACK_NOT_PRECACHED: The navigation fallback isn't one of the precached paths.
94
104
  * - FAILED_TO_BUILD_CONFIG: Unable to load the environment file.
95
105
  * - INVALID_FIREBASE_CONFIG: The environment variable that holds the Firebase config is missing or
96
106
  * empty.
@@ -1 +1 @@
1
- {"version":3,"file":"service-worker.js","sourceRoot":"","sources":["../../../src/build/service-worker/service-worker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AACtC,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAEpD,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AACjE,OAAO,EACL,gBAAgB,EAChB,wBAAwB,EACxB,mBAAmB,GAEpB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,EACL,qBAAqB,EACrB,iBAAiB,EACjB,oBAAoB,GAErB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAEzD;;;;;GAKG;AACH,MAAM,qBAAqB,GAAG,KAAK,EACjC,MAA4B,EAC5B,OAAmC,EACU,EAAE;IAC/C,MAAM,WAAW,GAAG,wBAAwB,CAAC,MAAM,CAAC,CAAC;IACrD,MAAM,WAAW,GAAG,gBAAgB,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;IAC1D,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IACzC,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,IAAI,CAAC;IACxC,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAChD,MAAM,MAAM,GAAG,MAAM,mBAAmB,CAAC;QACvC,QAAQ,EAAE,WAAW,CAAC,QAAQ;QAC9B,eAAe,EAAE,WAAW,CAAC,eAAe;QAC5C,SAAS,EAAE,GAAG,WAAW,CAAC,eAAe,GAAG,oBAAoB,GAAG,oBAAoB,CAAC,iBAAiB,EAAE,qBAAqB,CAAC,EAAE;QACnI,cAAc,EAAE,OAAO;YACrB,CAAC,CAAC,MAAM,uBAAuB,CAC3B,MAAM,EACN,WAAW,CAAC,iBAAiB,EAC7B,WAAW,CAAC,wBAAwB,CACrC;YACH,CAAC,CAAC,EAAE;QACN,gBAAgB,EAAE,WAAW,CAAC,yBAAyB;QACvD,OAAO;QACP,GAAG,CAAC,WAAW,CAAC,QAAQ,KAAK,cAAc;YACzC,CAAC,CAAC;gBACE,QAAQ,EAAE;oBACR,UAAU,EAAE,WAAW,CAAC,kBAAkB;oBAC1C,OAAO,EAAE,MAAM,mBAAmB,CAChC,WAAW,CAAC,2BAA2B,EACvC,WAAW,EACX,GAAG,CACJ;iBACF;aACF;YACH,CAAC,CAAC,EAAE,CAAC;KACR,CAAC,CAAC;IACH,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AAC5B,CAAC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,KAAK,EACrC,MAA4B,EAC5B,UAAsC,EAAE,EACvB,EAAE,CAAC,CAAC,MAAM,qBAAqB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC;AAE5E;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,KAAK,EACrC,MAA4B,EAC5B,UAAsC,EAAE,EACvB,EAAE;IACnB,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,qBAAqB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACxE,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,EAAE,wBAAwB,CAAC,CAAC;IACpD,MAAM,aAAa,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAClC,OAAO,IAAI,CAAC;AACd,CAAC,CAAC","sourcesContent":["import { join, resolve } from 'node:path';\nimport { writeTextFile } from 'jg-fs';\nimport { generateRandomString } from 'jg-utilities';\n\nimport { SERVICE_WORKER_FILE_NAME } from '../../shared/index.js';\nimport {\n parseEnvironment,\n parseServiceWorkerConfig,\n readFirebaseOptions,\n type IServiceWorkerConfig,\n} from '../config/index.js';\nimport { buildPrecacheAssetPaths } from '../precache/index.js';\nimport {\n CACHE_NAME_CHARACTERS,\n CACHE_NAME_LENGTH,\n CACHE_NAME_SEPARATOR,\n type IBuildServiceWorkerOptions,\n} from '../shared/index.js';\nimport { renderServiceWorker } from '../worker/index.js';\n\n/**\n * Generates the worker and works out the directory it belongs in.\n * @param config The service worker config.\n * @param options The environment, caching, and working directory.\n * @returns A promise that resolves to the worker's source and its output directory.\n */\nconst generateServiceWorker = async (\n config: IServiceWorkerConfig,\n options: IBuildServiceWorkerOptions,\n): Promise<{ source: string; outDir: string }> => {\n const validConfig = parseServiceWorkerConfig(config);\n const environment = parseEnvironment(options.environment);\n const cwd = options.cwd ?? process.cwd();\n const caching = options.caching ?? true;\n const outDir = resolve(cwd, validConfig.outDir);\n const source = await renderServiceWorker({\n template: validConfig.template,\n cacheNamePrefix: validConfig.cacheNamePrefix,\n cacheName: `${validConfig.cacheNamePrefix}${CACHE_NAME_SEPARATOR}${generateRandomString(CACHE_NAME_LENGTH, CACHE_NAME_CHARACTERS)}`,\n precacheAssets: caching\n ? await buildPrecacheAssetPaths(\n outDir,\n validConfig.includeToPrecache,\n validConfig.excludeFilesFromPrecache,\n )\n : [],\n excludeMIMETypes: validConfig.excludeMIMETypesFromCache,\n caching,\n ...(validConfig.template === 'firebase-fcm'\n ? {\n firebase: {\n sdkVersion: validConfig.firebaseSdkVersion,\n options: await readFirebaseOptions(\n validConfig.firebaseConfigProcessEnvKey,\n environment,\n cwd,\n ),\n },\n }\n : {}),\n });\n return { source, outDir };\n};\n\n/**\n * Generates the service worker's source from a config, without writing it. Every generated worker\n * gets a new cache name, `<cacheNamePrefix>--<10 random characters>`, so a new deployment\n * replaces the previous one's cache when it activates.\n * @param config The service worker config, as read by readServiceWorkerConfig or written inline.\n * @param options The environment the firebase-fcm template reads its Firebase config from,\n * whether the worker caches (false for development), and the directory paths are resolved from.\n * @returns A promise that resolves to the worker's source.\n * @example\n * import { buildServiceWorker } from 'jg-sw/build';\n *\n * const source = await buildServiceWorker(\n * {\n * outDir: 'dist',\n * cacheNamePrefix: 'my-app',\n * template: 'base',\n * includeToPrecache: ['/', '/assets'],\n * excludeFilesFromPrecache: [],\n * excludeMIMETypesFromCache: ['application/json'],\n * },\n * { caching: true },\n * );\n * @throws\n * - FAILED_TO_READ_BASE_CONFIG: The service worker config is invalid.\n * - INVALID_TEMPLATE_NAME: The template must be 'base' or 'firebase-fcm'.\n * - INVALID_FIREBASE_SDK_VERSION: The Firebase SDK version must be a version such as 12.4.0.\n * - INVALID_ENVIRONMENT: The environment isn't supported, or the firebase-fcm template needs one.\n * - NOT_A_PATH_ELEMENT: An asset to precache is outside outDir, or isn't a file or a directory.\n * - FAILED_TO_BUILD_CONFIG: Unable to load the environment file.\n * - INVALID_FIREBASE_CONFIG: The environment variable that holds the Firebase config is missing or\n * empty.\n * - FAILED_TO_READ_FIREBASE_CONFIG: Unable to read the Firebase config from the environment\n * variable.\n */\nexport const buildServiceWorker = async (\n config: IServiceWorkerConfig,\n options: IBuildServiceWorkerOptions = {},\n): Promise<string> => (await generateServiceWorker(config, options)).source;\n\n/**\n * Generates the service worker and writes it to `sw.js` in the config's outDir, after the app's\n * build has written its files there.\n * @param config The service worker config, as read by readServiceWorkerConfig or written inline.\n * @param options The environment the firebase-fcm template reads its Firebase config from,\n * whether the worker caches (false for development), and the directory paths are resolved from.\n * @returns A promise that resolves to the written file's path.\n * @example\n * import { readServiceWorkerConfig, writeServiceWorker } from 'jg-sw/build';\n *\n * const config = await readServiceWorkerConfig('jg-sw.config.json');\n * await writeServiceWorker(config, { environment: 'production' }); // '/app/dist/sw.js'\n * @throws\n * - FAILED_TO_READ_BASE_CONFIG: The service worker config is invalid.\n * - INVALID_TEMPLATE_NAME: The template must be 'base' or 'firebase-fcm'.\n * - INVALID_FIREBASE_SDK_VERSION: The Firebase SDK version must be a version such as 12.4.0.\n * - INVALID_ENVIRONMENT: The environment isn't supported, or the firebase-fcm template needs one.\n * - NOT_A_PATH_ELEMENT: An asset to precache is outside outDir, or isn't a file or a directory.\n * - FAILED_TO_BUILD_CONFIG: Unable to load the environment file.\n * - INVALID_FIREBASE_CONFIG: The environment variable that holds the Firebase config is missing or\n * empty.\n * - FAILED_TO_READ_FIREBASE_CONFIG: Unable to read the Firebase config from the environment\n * variable.\n */\nexport const writeServiceWorker = async (\n config: IServiceWorkerConfig,\n options: IBuildServiceWorkerOptions = {},\n): Promise<string> => {\n const { source, outDir } = await generateServiceWorker(config, options);\n const path = join(outDir, SERVICE_WORKER_FILE_NAME);\n await writeTextFile(path, source);\n return path;\n};\n"]}
1
+ {"version":3,"file":"service-worker.js","sourceRoot":"","sources":["../../../src/build/service-worker/service-worker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AACtC,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAEpD,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AACjE,OAAO,EACL,gBAAgB,EAChB,wBAAwB,EACxB,mBAAmB,GAEpB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,EACL,qBAAqB,EACrB,iBAAiB,EACjB,oBAAoB,EACpB,MAAM,GAEP,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAEzD;;;;;GAKG;AACH,MAAM,qBAAqB,GAAG,KAAK,EACjC,MAA4B,EAC5B,OAAmC,EACU,EAAE;IAC/C,MAAM,WAAW,GAAG,wBAAwB,CAAC,MAAM,CAAC,CAAC;IACrD,MAAM,WAAW,GAAG,gBAAgB,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;IAC1D,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IACzC,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,IAAI,CAAC;IACxC,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAChD,MAAM,cAAc,GAAG,OAAO;QAC5B,CAAC,CAAC,MAAM,uBAAuB,CAC3B,MAAM,EACN,WAAW,CAAC,iBAAiB,EAC7B,WAAW,CAAC,wBAAwB,CACrC;QACH,CAAC,CAAC,EAAE,CAAC;IACP,MAAM,kBAAkB,GAAG,WAAW,CAAC,kBAAkB,IAAI,IAAI,CAAC;IAClE,4CAA4C;IAC5C,IAAI,OAAO,IAAI,kBAAkB,KAAK,IAAI,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,kBAAkB,CAAC,EAAE,CAAC;QAC3F,MAAM,IAAI,SAAS,CACjB,4BAA4B,kBAAkB,iDAAiD,EAC/F,EAAE,IAAI,EAAE,MAAM,CAAC,iCAAiC,EAAE,IAAI,EAAE,EAAE,kBAAkB,EAAE,EAAE,CACjF,CAAC;IACJ,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,mBAAmB,CAAC;QACvC,QAAQ,EAAE,WAAW,CAAC,QAAQ;QAC9B,eAAe,EAAE,WAAW,CAAC,eAAe;QAC5C,SAAS,EAAE,GAAG,WAAW,CAAC,eAAe,GAAG,oBAAoB,GAAG,oBAAoB,CAAC,iBAAiB,EAAE,qBAAqB,CAAC,EAAE;QACnI,cAAc;QACd,gBAAgB,EAAE,WAAW,CAAC,yBAAyB;QACvD,OAAO;QACP,kBAAkB;QAClB,GAAG,CAAC,WAAW,CAAC,QAAQ,KAAK,cAAc;YACzC,CAAC,CAAC;gBACE,QAAQ,EAAE;oBACR,UAAU,EAAE,WAAW,CAAC,kBAAkB;oBAC1C,OAAO,EAAE,MAAM,mBAAmB,CAChC,WAAW,CAAC,2BAA2B,EACvC,WAAW,EACX,GAAG,CACJ;iBACF;aACF;YACH,CAAC,CAAC,EAAE,CAAC;KACR,CAAC,CAAC;IACH,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AAC5B,CAAC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,KAAK,EACrC,MAA4B,EAC5B,UAAsC,EAAE,EACvB,EAAE,CAAC,CAAC,MAAM,qBAAqB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC;AAE5E;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,KAAK,EACrC,MAA4B,EAC5B,UAAsC,EAAE,EACvB,EAAE;IACnB,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,qBAAqB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACxE,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,EAAE,wBAAwB,CAAC,CAAC;IACpD,MAAM,aAAa,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAClC,OAAO,IAAI,CAAC;AACd,CAAC,CAAC","sourcesContent":["import { join, resolve } from 'node:path';\nimport { Exception } from 'jg-exception';\nimport { writeTextFile } from 'jg-fs';\nimport { generateRandomString } from 'jg-utilities';\n\nimport { SERVICE_WORKER_FILE_NAME } from '../../shared/index.js';\nimport {\n parseEnvironment,\n parseServiceWorkerConfig,\n readFirebaseOptions,\n type IServiceWorkerConfig,\n} from '../config/index.js';\nimport { buildPrecacheAssetPaths } from '../precache/index.js';\nimport {\n CACHE_NAME_CHARACTERS,\n CACHE_NAME_LENGTH,\n CACHE_NAME_SEPARATOR,\n ERRORS,\n type IBuildServiceWorkerOptions,\n} from '../shared/index.js';\nimport { renderServiceWorker } from '../worker/index.js';\n\n/**\n * Generates the worker and works out the directory it belongs in.\n * @param config The service worker config.\n * @param options The environment, caching, and working directory.\n * @returns A promise that resolves to the worker's source and its output directory.\n */\nconst generateServiceWorker = async (\n config: IServiceWorkerConfig,\n options: IBuildServiceWorkerOptions,\n): Promise<{ source: string; outDir: string }> => {\n const validConfig = parseServiceWorkerConfig(config);\n const environment = parseEnvironment(options.environment);\n const cwd = options.cwd ?? process.cwd();\n const caching = options.caching ?? true;\n const outDir = resolve(cwd, validConfig.outDir);\n const precacheAssets = caching\n ? await buildPrecacheAssetPaths(\n outDir,\n validConfig.includeToPrecache,\n validConfig.excludeFilesFromPrecache,\n )\n : [];\n const navigationFallback = validConfig.navigationFallback ?? null;\n // offline, only a precached page can answer\n if (caching && navigationFallback !== null && !precacheAssets.includes(navigationFallback)) {\n throw new Exception(\n `The navigation fallback '${navigationFallback}' isn't precached. Add it to includeToPrecache.`,\n { code: ERRORS.NAVIGATION_FALLBACK_NOT_PRECACHED, data: { navigationFallback } },\n );\n }\n const source = await renderServiceWorker({\n template: validConfig.template,\n cacheNamePrefix: validConfig.cacheNamePrefix,\n cacheName: `${validConfig.cacheNamePrefix}${CACHE_NAME_SEPARATOR}${generateRandomString(CACHE_NAME_LENGTH, CACHE_NAME_CHARACTERS)}`,\n precacheAssets,\n excludeMIMETypes: validConfig.excludeMIMETypesFromCache,\n caching,\n navigationFallback,\n ...(validConfig.template === 'firebase-fcm'\n ? {\n firebase: {\n sdkVersion: validConfig.firebaseSdkVersion,\n options: await readFirebaseOptions(\n validConfig.firebaseConfigProcessEnvKey,\n environment,\n cwd,\n ),\n },\n }\n : {}),\n });\n return { source, outDir };\n};\n\n/**\n * Generates the service worker's source from a config, without writing it. Every generated worker\n * gets a new cache name, `<cacheNamePrefix>--<10 random characters>`, so a new deployment\n * replaces the previous one's cache when it activates.\n * @param config The service worker config, as read by readServiceWorkerConfig or written inline.\n * @param options The environment the firebase-fcm template reads its Firebase config from,\n * whether the worker caches (false for development), and the directory paths are resolved from.\n * @returns A promise that resolves to the worker's source.\n * @example\n * import { buildServiceWorker } from 'jg-sw/build';\n *\n * const source = await buildServiceWorker(\n * {\n * outDir: 'dist',\n * cacheNamePrefix: 'my-app',\n * template: 'base',\n * includeToPrecache: ['/', '/assets'],\n * excludeFilesFromPrecache: [],\n * excludeMIMETypesFromCache: ['application/json'],\n * },\n * { caching: true },\n * );\n * @throws\n * - FAILED_TO_READ_BASE_CONFIG: The service worker config is invalid.\n * - INVALID_TEMPLATE_NAME: The template must be 'base' or 'firebase-fcm'.\n * - INVALID_FIREBASE_SDK_VERSION: The Firebase SDK version must be a version such as 12.4.0.\n * - INVALID_ENVIRONMENT: The environment isn't supported, or the firebase-fcm template needs one.\n * - NOT_A_PATH_ELEMENT: An asset to precache is outside outDir, or isn't a file or a directory.\n * - NAVIGATION_FALLBACK_NOT_PRECACHED: The navigation fallback isn't one of the precached paths.\n * - FAILED_TO_BUILD_CONFIG: Unable to load the environment file.\n * - INVALID_FIREBASE_CONFIG: The environment variable that holds the Firebase config is missing or\n * empty.\n * - FAILED_TO_READ_FIREBASE_CONFIG: Unable to read the Firebase config from the environment\n * variable.\n */\nexport const buildServiceWorker = async (\n config: IServiceWorkerConfig,\n options: IBuildServiceWorkerOptions = {},\n): Promise<string> => (await generateServiceWorker(config, options)).source;\n\n/**\n * Generates the service worker and writes it to `sw.js` in the config's outDir, after the app's\n * build has written its files there.\n * @param config The service worker config, as read by readServiceWorkerConfig or written inline.\n * @param options The environment the firebase-fcm template reads its Firebase config from,\n * whether the worker caches (false for development), and the directory paths are resolved from.\n * @returns A promise that resolves to the written file's path.\n * @example\n * import { readServiceWorkerConfig, writeServiceWorker } from 'jg-sw/build';\n *\n * const config = await readServiceWorkerConfig('jg-sw.config.json');\n * await writeServiceWorker(config, { environment: 'production' }); // '/app/dist/sw.js'\n * @throws\n * - FAILED_TO_READ_BASE_CONFIG: The service worker config is invalid.\n * - INVALID_TEMPLATE_NAME: The template must be 'base' or 'firebase-fcm'.\n * - INVALID_FIREBASE_SDK_VERSION: The Firebase SDK version must be a version such as 12.4.0.\n * - INVALID_ENVIRONMENT: The environment isn't supported, or the firebase-fcm template needs one.\n * - NOT_A_PATH_ELEMENT: An asset to precache is outside outDir, or isn't a file or a directory.\n * - NAVIGATION_FALLBACK_NOT_PRECACHED: The navigation fallback isn't one of the precached paths.\n * - FAILED_TO_BUILD_CONFIG: Unable to load the environment file.\n * - INVALID_FIREBASE_CONFIG: The environment variable that holds the Firebase config is missing or\n * empty.\n * - FAILED_TO_READ_FIREBASE_CONFIG: Unable to read the Firebase config from the environment\n * variable.\n */\nexport const writeServiceWorker = async (\n config: IServiceWorkerConfig,\n options: IBuildServiceWorkerOptions = {},\n): Promise<string> => {\n const { source, outDir } = await generateServiceWorker(config, options);\n const path = join(outDir, SERVICE_WORKER_FILE_NAME);\n await writeTextFile(path, source);\n return path;\n};\n"]}
@@ -7,4 +7,5 @@ export declare const ERRORS: {
7
7
  readonly FAILED_TO_BUILD_CONFIG: "FAILED_TO_BUILD_CONFIG";
8
8
  readonly FAILED_TO_READ_FIREBASE_CONFIG: "FAILED_TO_READ_FIREBASE_CONFIG";
9
9
  readonly INVALID_FIREBASE_SDK_VERSION: "INVALID_FIREBASE_SDK_VERSION";
10
+ readonly NAVIGATION_FALLBACK_NOT_PRECACHED: "NAVIGATION_FALLBACK_NOT_PRECACHED";
10
11
  };
@@ -8,5 +8,6 @@ export const ERRORS = {
8
8
  FAILED_TO_BUILD_CONFIG: 'FAILED_TO_BUILD_CONFIG',
9
9
  FAILED_TO_READ_FIREBASE_CONFIG: 'FAILED_TO_READ_FIREBASE_CONFIG',
10
10
  INVALID_FIREBASE_SDK_VERSION: 'INVALID_FIREBASE_SDK_VERSION',
11
+ NAVIGATION_FALLBACK_NOT_PRECACHED: 'NAVIGATION_FALLBACK_NOT_PRECACHED',
11
12
  };
12
13
  //# sourceMappingURL=errors.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../src/build/shared/errors.ts"],"names":[],"mappings":"AAAA,qDAAqD;AACrD,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,qBAAqB,EAAE,uBAAuB;IAC9C,kBAAkB,EAAE,oBAAoB;IACxC,0BAA0B,EAAE,4BAA4B;IACxD,uBAAuB,EAAE,yBAAyB;IAClD,mBAAmB,EAAE,qBAAqB;IAC1C,sBAAsB,EAAE,wBAAwB;IAChD,8BAA8B,EAAE,gCAAgC;IAChE,4BAA4B,EAAE,8BAA8B;CACpD,CAAC","sourcesContent":["// the codes for every error thrown by the build side\nexport const ERRORS = {\n INVALID_TEMPLATE_NAME: 'INVALID_TEMPLATE_NAME',\n NOT_A_PATH_ELEMENT: 'NOT_A_PATH_ELEMENT',\n FAILED_TO_READ_BASE_CONFIG: 'FAILED_TO_READ_BASE_CONFIG',\n INVALID_FIREBASE_CONFIG: 'INVALID_FIREBASE_CONFIG',\n INVALID_ENVIRONMENT: 'INVALID_ENVIRONMENT',\n FAILED_TO_BUILD_CONFIG: 'FAILED_TO_BUILD_CONFIG',\n FAILED_TO_READ_FIREBASE_CONFIG: 'FAILED_TO_READ_FIREBASE_CONFIG',\n INVALID_FIREBASE_SDK_VERSION: 'INVALID_FIREBASE_SDK_VERSION',\n} as const;\n"]}
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../src/build/shared/errors.ts"],"names":[],"mappings":"AAAA,qDAAqD;AACrD,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,qBAAqB,EAAE,uBAAuB;IAC9C,kBAAkB,EAAE,oBAAoB;IACxC,0BAA0B,EAAE,4BAA4B;IACxD,uBAAuB,EAAE,yBAAyB;IAClD,mBAAmB,EAAE,qBAAqB;IAC1C,sBAAsB,EAAE,wBAAwB;IAChD,8BAA8B,EAAE,gCAAgC;IAChE,4BAA4B,EAAE,8BAA8B;IAC5D,iCAAiC,EAAE,mCAAmC;CAC9D,CAAC","sourcesContent":["// the codes for every error thrown by the build side\nexport const ERRORS = {\n INVALID_TEMPLATE_NAME: 'INVALID_TEMPLATE_NAME',\n NOT_A_PATH_ELEMENT: 'NOT_A_PATH_ELEMENT',\n FAILED_TO_READ_BASE_CONFIG: 'FAILED_TO_READ_BASE_CONFIG',\n INVALID_FIREBASE_CONFIG: 'INVALID_FIREBASE_CONFIG',\n INVALID_ENVIRONMENT: 'INVALID_ENVIRONMENT',\n FAILED_TO_BUILD_CONFIG: 'FAILED_TO_BUILD_CONFIG',\n FAILED_TO_READ_FIREBASE_CONFIG: 'FAILED_TO_READ_FIREBASE_CONFIG',\n INVALID_FIREBASE_SDK_VERSION: 'INVALID_FIREBASE_SDK_VERSION',\n NAVIGATION_FALLBACK_NOT_PRECACHED: 'NAVIGATION_FALLBACK_NOT_PRECACHED',\n} as const;\n"]}
@@ -21,6 +21,9 @@ const PRECACHE_ASSETS = [];
21
21
  // the list of MIME Types that won't be cached when the app sends HTTP GET requests
22
22
  const EXCLUDE_MIME_TYPES = [];
23
23
 
24
+ // the precached page that answers a navigation the network can't serve, or null for none
25
+ const NAVIGATION_FALLBACK = null;
26
+
24
27
  // the Cache-Control directives that keep a response out of the cache
25
28
  const UNCACHEABLE_CACHE_CONTROL_DIRECTIVES = ['no-store', 'private'];
26
29
 
@@ -157,6 +160,13 @@ const cacheFirst = async (request, event) => {
157
160
  try {
158
161
  responseFromNetwork = await fetch(request);
159
162
  } catch {
163
+ // a page the app routes on the client, such as /settings, opens through the fallback page
164
+ if (NAVIGATION_FALLBACK !== null && request.mode === 'navigate') {
165
+ const fallback = await cache.match(NAVIGATION_FALLBACK);
166
+ if (fallback) {
167
+ return fallback;
168
+ }
169
+ }
160
170
  return new Response('Network error happened', {
161
171
  status: 408,
162
172
  headers: { 'Content-Type': 'text/plain' },
@@ -6,6 +6,7 @@ export type IRenderServiceWorkerInput = {
6
6
  precacheAssets: string[];
7
7
  excludeMIMETypes: string[];
8
8
  caching: boolean;
9
+ navigationFallback: string | null;
9
10
  firebase?: {
10
11
  sdkVersion: string;
11
12
  options: IFirebaseOptions;
@@ -38,6 +38,7 @@ export const renderServiceWorker = async (input) => {
38
38
  EXCLUDE_MIME_TYPES: input.excludeMIMETypes,
39
39
  SKIP_WAITING_MESSAGE_TYPE,
40
40
  CACHING: input.caching,
41
+ NAVIGATION_FALLBACK: input.navigationFallback,
41
42
  });
42
43
  if (input.template === 'base' || input.firebase === undefined) {
43
44
  return `${GENERATED_HEADER}\n${base}`;
@@ -1 +1 @@
1
- {"version":3,"file":"worker.js","sourceRoot":"","sources":["../../../src/build/worker/worker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAErC,OAAO,EAAE,yBAAyB,EAAE,MAAM,uBAAuB,CAAC;AAGlE,+DAA+D;AAC/D,MAAM,aAAa,GAAG;IACpB,IAAI,EAAE,IAAI,GAAG,CAAC,yBAAyB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;IACzD,cAAc,EAAE,IAAI,GAAG,CAAC,iCAAiC,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;CACnE,CAAC;AAEX,2CAA2C;AAC3C,MAAM,gBAAgB,GACpB,4FAA4F,CAAC;AAe/F;;;;;;GAMG;AACH,MAAM,YAAY,GAAG,CAAC,QAAgB,EAAE,MAA+B,EAAU,EAAE,CACjF,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,CAC3B,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,EAAE,CACxB,MAAM,CAAC,OAAO,CACZ,IAAI,MAAM,CAAC,UAAU,IAAI,SAAS,EAAE,GAAG,CAAC;AACxC,mFAAmF;AACnF,GAAG,EAAE,CAAC,SAAS,IAAI,MAAM,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,CAC3D,EACH,QAAQ,CACT,CAAC;AAEJ;;;;GAIG;AACH,MAAM,YAAY,GAAG,CAAC,IAAgC,EAAmB,EAAE,CACzE,YAAY,CAAC,aAAa,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAEnD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,KAAK,EAAE,KAAgC,EAAmB,EAAE;IAC7F,MAAM,IAAI,GAAG,YAAY,CAAC,MAAM,YAAY,CAAC,MAAM,CAAC,EAAE;QACpD,iBAAiB,EAAE,KAAK,CAAC,eAAe;QACxC,UAAU,EAAE,KAAK,CAAC,SAAS;QAC3B,eAAe,EAAE,KAAK,CAAC,cAAc;QACrC,kBAAkB,EAAE,KAAK,CAAC,gBAAgB;QAC1C,yBAAyB;QACzB,OAAO,EAAE,KAAK,CAAC,OAAO;KACvB,CAAC,CAAC;IACH,IAAI,KAAK,CAAC,QAAQ,KAAK,MAAM,IAAI,KAAK,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC9D,OAAO,GAAG,gBAAgB,KAAK,IAAI,EAAE,CAAC;IACxC,CAAC;IACD,MAAM,WAAW,GAAG,YAAY,CAAC,MAAM,YAAY,CAAC,cAAc,CAAC,EAAE;QACnE,oBAAoB,EAAE,KAAK,CAAC,QAAQ,CAAC,UAAU;QAC/C,gBAAgB,EAAE,KAAK,CAAC,QAAQ,CAAC,OAAO;KACzC,CAAC,CAAC;IACH,OAAO,GAAG,gBAAgB,KAAK,IAAI,CAAC,OAAO,EAAE,OAAO,WAAW,EAAE,CAAC;AACpE,CAAC,CAAC","sourcesContent":["import { fileURLToPath } from 'node:url';\nimport { readTextFile } from 'jg-fs';\n\nimport { SKIP_WAITING_MESSAGE_TYPE } from '../../shared/index.js';\nimport type { IFirebaseOptions, IServiceWorkerConfig } from '../config/index.js';\n\n// the templates, copied next to the compiled code by the build\nconst TEMPLATE_URLS = {\n base: new URL('../templates/base.sw.js', import.meta.url),\n 'firebase-fcm': new URL('../templates/firebase-fcm.sw.js', import.meta.url),\n} as const;\n\n// the first line of every generated worker\nconst GENERATED_HEADER =\n '// Generated by jg-sw. Changes to this file are overwritten the next time it is generated.';\n\n// what the worker is generated from\nexport type IRenderServiceWorkerInput = {\n template: IServiceWorkerConfig['template'];\n cacheNamePrefix: string;\n cacheName: string;\n precacheAssets: string[];\n excludeMIMETypes: string[];\n caching: boolean;\n\n // the firebase-fcm template's SDK version and app options\n firebase?: { sdkVersion: string; options: IFirebaseOptions };\n};\n\n/**\n * Sets the values of a template's constants, each declared on its own line as\n * `const NAME = <placeholder>;`. Values are serialized as JSON, so strings and paths are escaped.\n * @param template The template's source.\n * @param values The value of each constant, by name.\n * @returns The source with the constants set.\n */\nconst setConstants = (template: string, values: Record<string, unknown>): string =>\n Object.entries(values).reduce(\n (source, [name, value]) =>\n source.replace(\n new RegExp(`^const ${name} = .*;$`, 'm'),\n // a function, so `$` sequences in the value are never read as replacement patterns\n () => `const ${name} = ${JSON.stringify(value, null, 2)};`,\n ),\n template,\n );\n\n/**\n * Reads one of the worker templates.\n * @param name The template's name.\n * @returns A promise that resolves to the template's source.\n */\nconst readTemplate = (name: keyof typeof TEMPLATE_URLS): Promise<string> =>\n readTextFile(fileURLToPath(TEMPLATE_URLS[name]));\n\n/**\n * Generates the worker's source from its templates: the base template, followed by the\n * firebase-fcm template when the config selects it.\n * @param input What the worker is generated from.\n * @returns A promise that resolves to the worker's source.\n */\nexport const renderServiceWorker = async (input: IRenderServiceWorkerInput): Promise<string> => {\n const base = setConstants(await readTemplate('base'), {\n CACHE_NAME_PREFIX: input.cacheNamePrefix,\n CACHE_NAME: input.cacheName,\n PRECACHE_ASSETS: input.precacheAssets,\n EXCLUDE_MIME_TYPES: input.excludeMIMETypes,\n SKIP_WAITING_MESSAGE_TYPE,\n CACHING: input.caching,\n });\n if (input.template === 'base' || input.firebase === undefined) {\n return `${GENERATED_HEADER}\\n${base}`;\n }\n const firebaseFcm = setConstants(await readTemplate('firebase-fcm'), {\n FIREBASE_SDK_VERSION: input.firebase.sdkVersion,\n FIREBASE_OPTIONS: input.firebase.options,\n });\n return `${GENERATED_HEADER}\\n${base.trimEnd()}\\n\\n${firebaseFcm}`;\n};\n"]}
1
+ {"version":3,"file":"worker.js","sourceRoot":"","sources":["../../../src/build/worker/worker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAErC,OAAO,EAAE,yBAAyB,EAAE,MAAM,uBAAuB,CAAC;AAGlE,+DAA+D;AAC/D,MAAM,aAAa,GAAG;IACpB,IAAI,EAAE,IAAI,GAAG,CAAC,yBAAyB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;IACzD,cAAc,EAAE,IAAI,GAAG,CAAC,iCAAiC,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;CACnE,CAAC;AAEX,2CAA2C;AAC3C,MAAM,gBAAgB,GACpB,4FAA4F,CAAC;AAkB/F;;;;;;GAMG;AACH,MAAM,YAAY,GAAG,CAAC,QAAgB,EAAE,MAA+B,EAAU,EAAE,CACjF,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,CAC3B,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,EAAE,CACxB,MAAM,CAAC,OAAO,CACZ,IAAI,MAAM,CAAC,UAAU,IAAI,SAAS,EAAE,GAAG,CAAC;AACxC,mFAAmF;AACnF,GAAG,EAAE,CAAC,SAAS,IAAI,MAAM,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,CAC3D,EACH,QAAQ,CACT,CAAC;AAEJ;;;;GAIG;AACH,MAAM,YAAY,GAAG,CAAC,IAAgC,EAAmB,EAAE,CACzE,YAAY,CAAC,aAAa,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAEnD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,KAAK,EAAE,KAAgC,EAAmB,EAAE;IAC7F,MAAM,IAAI,GAAG,YAAY,CAAC,MAAM,YAAY,CAAC,MAAM,CAAC,EAAE;QACpD,iBAAiB,EAAE,KAAK,CAAC,eAAe;QACxC,UAAU,EAAE,KAAK,CAAC,SAAS;QAC3B,eAAe,EAAE,KAAK,CAAC,cAAc;QACrC,kBAAkB,EAAE,KAAK,CAAC,gBAAgB;QAC1C,yBAAyB;QACzB,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,mBAAmB,EAAE,KAAK,CAAC,kBAAkB;KAC9C,CAAC,CAAC;IACH,IAAI,KAAK,CAAC,QAAQ,KAAK,MAAM,IAAI,KAAK,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC9D,OAAO,GAAG,gBAAgB,KAAK,IAAI,EAAE,CAAC;IACxC,CAAC;IACD,MAAM,WAAW,GAAG,YAAY,CAAC,MAAM,YAAY,CAAC,cAAc,CAAC,EAAE;QACnE,oBAAoB,EAAE,KAAK,CAAC,QAAQ,CAAC,UAAU;QAC/C,gBAAgB,EAAE,KAAK,CAAC,QAAQ,CAAC,OAAO;KACzC,CAAC,CAAC;IACH,OAAO,GAAG,gBAAgB,KAAK,IAAI,CAAC,OAAO,EAAE,OAAO,WAAW,EAAE,CAAC;AACpE,CAAC,CAAC","sourcesContent":["import { fileURLToPath } from 'node:url';\nimport { readTextFile } from 'jg-fs';\n\nimport { SKIP_WAITING_MESSAGE_TYPE } from '../../shared/index.js';\nimport type { IFirebaseOptions, IServiceWorkerConfig } from '../config/index.js';\n\n// the templates, copied next to the compiled code by the build\nconst TEMPLATE_URLS = {\n base: new URL('../templates/base.sw.js', import.meta.url),\n 'firebase-fcm': new URL('../templates/firebase-fcm.sw.js', import.meta.url),\n} as const;\n\n// the first line of every generated worker\nconst GENERATED_HEADER =\n '// Generated by jg-sw. Changes to this file are overwritten the next time it is generated.';\n\n// what the worker is generated from\nexport type IRenderServiceWorkerInput = {\n template: IServiceWorkerConfig['template'];\n cacheNamePrefix: string;\n cacheName: string;\n precacheAssets: string[];\n excludeMIMETypes: string[];\n caching: boolean;\n\n // the precached page that answers a navigation the network can't serve, or null for none\n navigationFallback: string | null;\n\n // the firebase-fcm template's SDK version and app options\n firebase?: { sdkVersion: string; options: IFirebaseOptions };\n};\n\n/**\n * Sets the values of a template's constants, each declared on its own line as\n * `const NAME = <placeholder>;`. Values are serialized as JSON, so strings and paths are escaped.\n * @param template The template's source.\n * @param values The value of each constant, by name.\n * @returns The source with the constants set.\n */\nconst setConstants = (template: string, values: Record<string, unknown>): string =>\n Object.entries(values).reduce(\n (source, [name, value]) =>\n source.replace(\n new RegExp(`^const ${name} = .*;$`, 'm'),\n // a function, so `$` sequences in the value are never read as replacement patterns\n () => `const ${name} = ${JSON.stringify(value, null, 2)};`,\n ),\n template,\n );\n\n/**\n * Reads one of the worker templates.\n * @param name The template's name.\n * @returns A promise that resolves to the template's source.\n */\nconst readTemplate = (name: keyof typeof TEMPLATE_URLS): Promise<string> =>\n readTextFile(fileURLToPath(TEMPLATE_URLS[name]));\n\n/**\n * Generates the worker's source from its templates: the base template, followed by the\n * firebase-fcm template when the config selects it.\n * @param input What the worker is generated from.\n * @returns A promise that resolves to the worker's source.\n */\nexport const renderServiceWorker = async (input: IRenderServiceWorkerInput): Promise<string> => {\n const base = setConstants(await readTemplate('base'), {\n CACHE_NAME_PREFIX: input.cacheNamePrefix,\n CACHE_NAME: input.cacheName,\n PRECACHE_ASSETS: input.precacheAssets,\n EXCLUDE_MIME_TYPES: input.excludeMIMETypes,\n SKIP_WAITING_MESSAGE_TYPE,\n CACHING: input.caching,\n NAVIGATION_FALLBACK: input.navigationFallback,\n });\n if (input.template === 'base' || input.firebase === undefined) {\n return `${GENERATED_HEADER}\\n${base}`;\n }\n const firebaseFcm = setConstants(await readTemplate('firebase-fcm'), {\n FIREBASE_SDK_VERSION: input.firebase.sdkVersion,\n FIREBASE_OPTIONS: input.firebase.options,\n });\n return `${GENERATED_HEADER}\\n${base.trimEnd()}\\n\\n${firebaseFcm}`;\n};\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jg-sw",
3
- "version": "1.1.1",
3
+ "version": "1.2.0",
4
4
  "description": "Service workers for web apps: a generated caching worker with a Vite plugin and CLI, plus registration, updates, and installation.",
5
5
  "author": "Jesus Graterol",
6
6
  "license": "MIT",
@@ -52,7 +52,7 @@
52
52
  ],
53
53
  "dependencies": {
54
54
  "jg-exception": "^1.2.0",
55
- "jg-fs": "^1.1.1",
55
+ "jg-fs": "^1.2.0",
56
56
  "jg-utilities": "^1.1.1",
57
57
  "zod": "^4.6.7"
58
58
  },