├── .editorconfig ├── .env ├── .eslintrc.js ├── .firebaserc ├── .gitignore ├── README.md ├── assets ├── README.md └── css │ ├── app.styl │ └── main.css ├── components └── LoginForm.vue ├── firebase.json ├── layouts └── default.vue ├── middleware └── auth-check.js ├── nuxt-firebase-authentication.jpg ├── nuxt.config.js ├── package.json ├── pages ├── about.vue ├── admin.vue ├── admin │ ├── index.vue │ └── settings.vue ├── index.vue └── login.vue ├── plugins ├── auth-cookie.js ├── firebase-client-init.js ├── vuetify.js └── web-font-loader.js ├── serverMiddleware └── validateFirebaseIdToken.js ├── services └── firebase-admin-init.js ├── static ├── README.md ├── favicon.ico ├── google.svg ├── icon.png ├── sw.js ├── workbox-sw.prod.v1.3.0.js └── workbox-sw.prod.v1.3.0.js.map ├── store └── index.js └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_size = 2 6 | indent_style = space 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | API_KEY=AIzaSyAzdoAjlM9YlQ-gl8VRayCxtJbnrl9qDsw 2 | AUTH_DOMAIN=nuxt-firebase-auth.firebaseapp.com 3 | DB_URL=https://nuxt-firebase-auth.firebaseio.com 4 | PROJECT_ID=nuxt-firebase-auth 5 | STORAGE_BUCKET=nuxt-firebase-auth.appspot.com 6 | MESSAGING_SENDER_ID=316484287956 7 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parser: 'babel-eslint', 4 | env: { 5 | browser: true, 6 | node: true 7 | }, 8 | extends: 'standard', 9 | // required to lint *.vue files 10 | plugins: [ 11 | 'html' 12 | ], 13 | // add your custom rules here 14 | rules: {}, 15 | globals: {} 16 | } 17 | -------------------------------------------------------------------------------- /.firebaserc: -------------------------------------------------------------------------------- 1 | { 2 | "projects": { 3 | "default": "nuxt-firebase-auth" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | node_modules 3 | 4 | # logs 5 | npm-debug.log 6 | yarn-error.log 7 | 8 | # Nuxt build 9 | .nuxt 10 | 11 | # Nuxt generate 12 | dist 13 | 14 | plugins/firebase-auth.js 15 | middleware/check-auth.js 16 | backups 17 | nuxt-firebase-auth-key.json 18 | .env 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Nuxt.js Firebase Auth - SSR Version 2 | This is an example of using Nuxt.js in SSR mode with Firebase Authentication 3 | 4 | [Live Demo](https://nuxt-ssr-firebase-auth.now.sh) 5 | 6 | ## Files of Importance 7 | - `/plugins/auth-cookie.js` 8 | - `/plugins/firebase-client-init.js` 9 | - `/services/firebase-admin-init.js` 10 | - `/middleware/auth-check.js` 11 | - `/store/index.js` 12 | 13 | ## Notes 14 | For the Sign-In to work, you will need to add your production site url to the Firebase's Authorized Domains in the Authorization settings of the Firebase Console. 15 | 16 | ## Credits 17 | I got a lot of help understanding what needed to happen by looking at this so special thanks to the contributers of that project! 18 | Firebase Function - Handlebars Example 19 | -------------------------------------------------------------------------------- /assets/README.md: -------------------------------------------------------------------------------- 1 | # ASSETS 2 | 3 | This directory contains your un-compiled assets such as LESS, SASS, or JavaScript. 4 | 5 | More information about the usage of this directory in the documentation: 6 | https://nuxtjs.org/guide/assets#webpacked 7 | 8 | **This directory is not required, you can delete it if you don't want to use it.** 9 | -------------------------------------------------------------------------------- /assets/css/app.styl: -------------------------------------------------------------------------------- 1 | // Specify overrides (theme and/or base variables etc.) 2 | // See https://vuetifyjs.com/quick-start 3 | $theme := { 4 | primary: #3f51b5 5 | light: #757de8 6 | dark: #002984 7 | accent: #ce93d8 8 | secondary: #424242 9 | info: #0D47A1 10 | warning: #ffb300 11 | error: #B71C1C 12 | success: #2E7D32 13 | } 14 | 15 | 16 | // Import Vuetify styling 17 | @require '~vuetify/src/stylus/main.styl' 18 | -------------------------------------------------------------------------------- /assets/css/main.css: -------------------------------------------------------------------------------- 1 | .page-enter-active, .page-leave-active { 2 | transition: all .45s ease-in-out; 3 | transition: all .45s cubic-bezier(.55,0,.1,1); 4 | 5 | // transform: translate(30px, 0); 6 | } 7 | .page-enter, .page-leave-active { 8 | opacity: 0; 9 | transform: translate(10px, 0); 10 | } 11 | 12 | html, body { 13 | font-size: 102% !important; 14 | } 15 | 16 | .spinner { 17 | background-color: #3f51b5 !important; 18 | } 19 | 20 | a.adminNavRoute.nuxt-link-exact-active.nuxt-link-active { 21 | font-weight: 700; 22 | text-decoration: none; 23 | } 24 | -------------------------------------------------------------------------------- /components/LoginForm.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Loading... 7 | 8 | 9 | Sign In with Google 10 | Google Sign In 11 | 12 | 13 | 14 | 15 | 16 | 31 | -------------------------------------------------------------------------------- /firebase.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosting": { 3 | "public": "dist" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /layouts/default.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 36 | settings 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | {{user.name}} 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | Logout 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | © 2017 73 | 74 | Github Repo 75 | 76 | 77 | 78 | 79 | 109 | 110 | 140 | -------------------------------------------------------------------------------- /middleware/auth-check.js: -------------------------------------------------------------------------------- 1 | export default function ({ store, redirect, error }) { 2 | if (!store.getters.activeUser) { 3 | return redirect('/login') 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /nuxt-firebase-authentication.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidroyer/nuxt-ssr-firebase-auth/b35218c1e9bc50e72972ff44c6ada9131018eb61/nuxt-firebase-authentication.jpg -------------------------------------------------------------------------------- /nuxt.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | head: { 3 | title: 'Nuxt SSR - Firebase Auth', 4 | meta: [ 5 | { charset: 'utf-8' }, 6 | { name: 'viewport', content: 'width=device-width, initial-scale=1' }, 7 | { hid: 'description', name: 'description', content: 'Nuxt.js project' } 8 | ], 9 | link: [ 10 | { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }, 11 | // { rel: 'preload', as: 'style', href: 'https://fonts.googleapis.com/css?family=Roboto:300,400,500,700' }, 12 | // { rel: 'stylesheet', href: 'https://fonts.googleapis.com/css?family=Material+Icons' } 13 | ] 14 | }, 15 | loading: { color: '#3f51b5' }, 16 | build: { 17 | extend (config, ctx) { 18 | if (ctx.dev && ctx.isClient) { 19 | // config.module.rules.push({ 20 | // enforce: 'pre', 21 | // test: /\.(js|vue)$/, 22 | // loader: 'eslint-loader', 23 | // exclude: /(node_modules)/ 24 | // }) 25 | } 26 | }, 27 | vendor: [ 28 | 'vuetify', 29 | '~/plugins/firebase-client-init.js' 30 | ], 31 | extractCSS: true, 32 | }, 33 | modules: [ 34 | // '@nuxtjs/pwa' 35 | ], 36 | plugins: [ 37 | {src: '~/plugins/vuetify.js', ssr: true }, 38 | {src: '~/plugins/firebase-client-init.js', ssr: false }, 39 | {src: '~/plugins/auth-cookie.js', ssr: false }, 40 | {src: '~/plugins/web-font-loader.js', ssr: false } 41 | ], 42 | serverMiddleware: [ 43 | '~/serverMiddleware/validateFirebaseIdToken' 44 | ], 45 | css: [ 46 | { src: '~/assets/css/main.css', lang: 'css'}, 47 | { src: '~/assets/css/app.styl', lang: 'styl'} 48 | ] 49 | } 50 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nuxt-firebase-auth", 3 | "version": "0.0.31", 4 | "description": "Nuxt.js with Firebase Auth project", 5 | "author": "David Royer ", 6 | "private": true, 7 | "scripts": { 8 | "dev": "nuxt", 9 | "build": "nuxt build", 10 | "start": "nuxt start", 11 | "generate": "nuxt generate", 12 | "lint": "eslint --ext .js,.vue --ignore-path .gitignore .", 13 | "precommit": "npm run lint", 14 | "fdeploy": "yarn build && firebase deploy", 15 | "heroku-postbuild": "npm run build", 16 | "hdeploy": "git push heroku ssr:master" 17 | }, 18 | "dependencies": { 19 | "@nuxtjs/pwa": "^1.0.2", 20 | "cookie-parser": "^1.4.3", 21 | "firebase": "4.5.1", 22 | "firebase-admin": "^5.4.2", 23 | "nuxt": "^1.0.0-rc11", 24 | "vuetify": "^0.16.9", 25 | "webfontloader": "^1.6.28", 26 | "xmlhttprequest": "^1.8.0" 27 | }, 28 | "now": { 29 | "alias": "nuxt-ssr-firebase-auth" 30 | }, 31 | "devDependencies": { 32 | "babel-eslint": "^7.2.3", 33 | "eslint": "^4.3.0", 34 | "eslint-config-standard": "^10.2.1", 35 | "eslint-loader": "^1.9.0", 36 | "eslint-plugin-html": "^3.1.1", 37 | "eslint-plugin-import": "^2.7.0", 38 | "eslint-plugin-node": "^5.1.1", 39 | "eslint-plugin-promise": "^3.5.0", 40 | "eslint-plugin-standard": "^3.0.1", 41 | "stylus": "^0.54.5", 42 | "stylus-loader": "^3.0.1" 43 | }, 44 | "engines": { 45 | "node": "8.7.0", 46 | "yarn": "1.1.0" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /pages/about.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | About 5 | If you are not logged in then you should be redirected to login when clicking this link to the Admin page 6 | Test Me 7 | 8 | 9 | Admin: /admin 10 | 11 | 12 | Admin Subroute: /admin/settings 13 | 14 | 15 | 16 | 17 | 18 | 19 | 23 | -------------------------------------------------------------------------------- /pages/admin.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | Admin Dashboard 4 | The routes below are are using nuxt-child 5 | 6 | Dashboard Home 7 | Settings 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 23 | 24 | 39 | 40 | -------------------------------------------------------------------------------- /pages/admin/index.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | Admin Dashboard for Child Routes 4 | The index page inside the admin folder 5 | 6 | 7 | 8 | 12 | 13 | 21 | -------------------------------------------------------------------------------- /pages/admin/settings.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Settings Here 5 | Some content will go here about this project. 6 | < Admin Dashboard 7 | 8 | 9 | 10 | 11 | 15 | -------------------------------------------------------------------------------- /pages/index.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | You're logged in! 11 | 12 | thumb_up 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 30 | 31 | 58 | -------------------------------------------------------------------------------- /pages/login.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 16 | 17 | 19 | -------------------------------------------------------------------------------- /plugins/auth-cookie.js: -------------------------------------------------------------------------------- 1 | import {Auth} from '~/plugins/firebase-client-init.js' 2 | 3 | export default (context) => { 4 | Auth.addAuthTokenListener(function (idToken) { 5 | document.cookie = '__session=' + idToken + ';max-age=' + (idToken ? 3600 : 0); 6 | }); 7 | } 8 | -------------------------------------------------------------------------------- /plugins/firebase-client-init.js: -------------------------------------------------------------------------------- 1 | import * as firebase from 'firebase/app' 2 | import 'firebase/auth'; 3 | 4 | var config = { 5 | apiKey: "AIzaSyAzdoAjlM9YlQ-gl8VRayCxtJbnrl9qDsw", 6 | authDomain: "nuxt-firebase-auth.firebaseapp.com", 7 | databaseURL: "https://nuxt-firebase-auth.firebaseio.com", 8 | projectId: "nuxt-firebase-auth", 9 | storageBucket: "nuxt-firebase-auth.appspot.com", 10 | messagingSenderId: "316484287956" 11 | } 12 | 13 | export default !firebase.apps.length ? firebase.initializeApp(config) : firebase.app() 14 | export const Auth = firebase.auth() 15 | export const GoogleAuthProvider = new firebase.auth.GoogleAuthProvider() 16 | -------------------------------------------------------------------------------- /plugins/vuetify.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuetify from 'vuetify' 3 | 4 | Vue.use(Vuetify) 5 | -------------------------------------------------------------------------------- /plugins/web-font-loader.js: -------------------------------------------------------------------------------- 1 | import WebFont from 'webfontloader' 2 | 3 | WebFont.load({ 4 | google: { 5 | families: ['Roboto:300,400,500,700', 'Material+Icons'] 6 | } 7 | }) 8 | -------------------------------------------------------------------------------- /serverMiddleware/validateFirebaseIdToken.js: -------------------------------------------------------------------------------- 1 | const admin = require('../services/firebase-admin-init.js') 2 | const cookieParser = require('cookie-parser')(); 3 | global.XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest 4 | 5 | module.exports = function (req, res, next) { 6 | 7 | getIdTokenFromRequest(req, res).then(idToken => { 8 | if (idToken) { 9 | addDecodedIdTokenToRequest(idToken, req).then(() => { 10 | next(); 11 | }); 12 | } else { 13 | next(); 14 | } 15 | }); 16 | } 17 | 18 | function getIdTokenFromRequest(req, res) { 19 | if (req.headers.authorization && req.headers.authorization.startsWith('Bearer ')) { 20 | console.log('Found "Authorization" header'); 21 | // Read the ID Token from the Authorization header. 22 | return Promise.resolve(req.headers.authorization.split('Bearer ')[1]); 23 | } 24 | return new Promise(function(resolve) { 25 | cookieParser(req, res, () => { 26 | if (req.cookies && req.cookies.__session) { 27 | console.log('Found "__session" cookie'); 28 | // Read the ID Token from cookie. 29 | resolve(req.cookies.__session); 30 | } else { 31 | resolve(); 32 | } 33 | }); 34 | }); 35 | } 36 | 37 | /** 38 | * Returns a Promise with the Decoded ID Token and adds it to req.user. 39 | */ 40 | function addDecodedIdTokenToRequest(idToken, req) { 41 | return admin.auth().verifyIdToken(idToken).then(decodedIdToken => { 42 | console.log('ID Token correctly decoded', decodedIdToken); 43 | req.user = decodedIdToken; 44 | }).catch(error => { 45 | console.error('Error while verifying Firebase ID token:', error); 46 | }); 47 | } 48 | -------------------------------------------------------------------------------- /services/firebase-admin-init.js: -------------------------------------------------------------------------------- 1 | const admin = require('firebase-admin'); 2 | const key = require("../serviceAccountKey.json"); 3 | 4 | module.exports = admin.initializeApp({ 5 | credential: admin.credential.cert(key), 6 | databaseURL: `https://${key.project_id}.firebaseio.com` 7 | }); 8 | -------------------------------------------------------------------------------- /static/README.md: -------------------------------------------------------------------------------- 1 | # STATIC 2 | 3 | This directory contains your static files. 4 | Each file inside this directory is mapped to /. 5 | 6 | Example: /static/robots.txt is mapped as /robots.txt. 7 | 8 | More information about the usage of this directory in the documentation: 9 | https://nuxtjs.org/guide/assets#static 10 | 11 | **This directory is not required, you can delete it if you don't want to use it.** 12 | -------------------------------------------------------------------------------- /static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidroyer/nuxt-ssr-firebase-auth/b35218c1e9bc50e72972ff44c6ada9131018eb61/static/favicon.ico -------------------------------------------------------------------------------- /static/google.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /static/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidroyer/nuxt-ssr-firebase-auth/b35218c1e9bc50e72972ff44c6ada9131018eb61/static/icon.png -------------------------------------------------------------------------------- /static/sw.js: -------------------------------------------------------------------------------- 1 | importScripts('workbox-sw.prod.v1.3.0.js'); 2 | 3 | /** 4 | * DO NOT EDIT THE FILE MANIFEST ENTRY 5 | * 6 | * The method precache() does the following: 7 | * 1. Cache URLs in the manifest to a local cache. 8 | * 2. When a network request is made for any of these URLs the response 9 | * will ALWAYS comes from the cache, NEVER the network. 10 | * 3. When the service worker changes ONLY assets with a revision change are 11 | * updated, old cache entries are left as is. 12 | * 13 | * By changing the file manifest manually, your users may end up not receiving 14 | * new versions of files because the revision hasn't changed. 15 | * 16 | * Please use workbox-build or some other tool / approach to generate the file 17 | * manifest which accounts for changes to local files and update the revision 18 | * accordingly. 19 | */ 20 | const fileManifest = [ 21 | { 22 | "url": "/_nuxt/app.3e9566adee20c0fc7202.js", 23 | "revision": "6b2224d6414f4adb7aeaab3f0dcd803b" 24 | }, 25 | { 26 | "url": "/_nuxt/common.290d9a515277eef9a7ee.js", 27 | "revision": "43f478b236cdcdd60832f9f673d17e3d" 28 | }, 29 | { 30 | "url": "/_nuxt/common.872e737efc4cc1dcbd846ecda25d89a6.css", 31 | "revision": "f20a4f78856a99613c9a0192c2724a71" 32 | }, 33 | { 34 | "url": "/_nuxt/layouts/default.3861c047f60e19d8c482.js", 35 | "revision": "6bfe77792a9c6a7670ba144b1a46dd8f" 36 | }, 37 | { 38 | "url": "/_nuxt/manifest.87a4146cd5b368552d62.js", 39 | "revision": "5161fd95dce79a8f9ae8342297e38059" 40 | }, 41 | { 42 | "url": "/_nuxt/pages/about.9e3bd611e19ed7c59f10.js", 43 | "revision": "0c97495b3629a5164b7b19570d05b200" 44 | }, 45 | { 46 | "url": "/_nuxt/pages/admin.9daa5064a13fbe7b19f4.js", 47 | "revision": "5495c0e97390235ba751fc5894d1eb6c" 48 | }, 49 | { 50 | "url": "/_nuxt/pages/admin/index.005c8a263373928c5f48.js", 51 | "revision": "075a202b3bbf780d0c63533e367c93cf" 52 | }, 53 | { 54 | "url": "/_nuxt/pages/admin/settings.9383526583eb30493727.js", 55 | "revision": "cffa9fe194d9ae4961b6d2349435abd2" 56 | }, 57 | { 58 | "url": "/_nuxt/pages/index.aea0548410c626dd1529.js", 59 | "revision": "a7692d034144ee950172eadea659af0e" 60 | }, 61 | { 62 | "url": "/_nuxt/pages/login.09cb4d5f945555ab4567.js", 63 | "revision": "d3e3bc097f3a476ab9176a9dabd56711" 64 | } 65 | ]; 66 | 67 | const workboxSW = new self.WorkboxSW({ 68 | "cacheId": "nuxt-firebase-auth_0.0.2", 69 | "clientsClaim": true, 70 | "directoryIndex": "/" 71 | }); 72 | workboxSW.precache(fileManifest); 73 | workboxSW.router.registerRoute('/**', workboxSW.strategies.networkFirst({}), 'GET'); 74 | workboxSW.router.registerRoute('/_nuxt/**', workboxSW.strategies.cacheFirst({}), 'GET'); 75 | -------------------------------------------------------------------------------- /static/workbox-sw.prod.v1.3.0.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 Google Inc. All Rights Reserved. 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | */ 15 | 16 | var WorkboxSW = (function () { 17 | 'use strict'; 18 | 19 | class ErrorFactory$1{constructor(a){this._errors=a;}createError(a,b){if(!(a in this._errors))throw new Error(`Unable to generate error '${a}'.`);let c=this._errors[a].replace(/\s+/g,' '),d=null;b&&(c+=` [${b.message}]`,d=b.stack);const e=new Error;return e.name=a,e.message=c,e.stack=d,e}} 20 | 21 | const errors={"not-in-sw":'workbox-sw must be loaded in your service worker file.',"unsupported-route-type":'The first parameter to registerRoute() should be either an Express-style path string, a RegExp, or a function.',"empty-express-string":'The Express style route string must have some characters, an empty string is invalid.',"bad-revisioned-cache-list":`The 'precache()' method expects`+`an array of revisioned urls like so: ['/example/hello.1234.txt', `+`{path: 'hello.txt', revision: '1234'}]`,"navigation-route-url-string":`The registerNavigationRoute() method `+`expects a URL string as its first parameter.`,"bad-cache-id":`The 'cacheId' parameter must be a string with at least `+`one character`,"bad-skip-waiting":`The 'skipWaiting' parameter must be a boolean.`,"bad-clients-claim":`The 'clientsClaim' parameter must be a boolean.`,"bad-directory-index":`The 'directoryIndex' parameter must be a boolean.`};var ErrorFactory = new ErrorFactory$1(errors); 22 | 23 | const errors$1={"express-route-invalid-path":`When using ExpressRoute, you must 24 | provide a path that starts with a '/' character (to match same-origin 25 | requests) or that starts with 'http' (to match cross-origin requests)`};var ErrorFactory$3 = new ErrorFactory$1(errors$1); 26 | 27 | var ErrorStackParser = {parse:()=>[]}; 28 | 29 | function atLeastOne(a){const b=Object.keys(a);b.some((c)=>a[c]!==void 0)||throwError('Please set at least one of the following parameters: '+b.map((c)=>`'${c}'`).join(', '));}function hasMethod(a,b){const c=Object.keys(a).pop(),d=typeof a[c][b];'function'!=d&&throwError(`The '${c}' parameter must be an object that exposes a 30 | '${b}' method.`);}function isInstance(a,b){const c=Object.keys(a).pop();a[c]instanceof b||throwError(`The '${c}' parameter must be an instance of 31 | '${b.name}'`);}function isOneOf(a,b){const c=Object.keys(a).pop();b.includes(a[c])||throwError(`The '${c}' parameter must be set to one of the 32 | following: ${b}`);}function isType(a,b){const c=Object.keys(a).pop(),d=typeof a[c];d!==b&&throwError(`The '${c}' parameter has the wrong type. (Expected: 33 | ${b}, actual: ${d})`);}function isArrayOfType(a,b){const c=Object.keys(a).pop(),d=`The '${c}' parameter should be an array containing 34 | one or more '${b}' elements.`;Array.isArray(a[c])||throwError(d);for(let e of a[c])typeof e!==b&&throwError(d);}function isArrayOfClass(a,b){const c=Object.keys(a).pop(),d=`The '${c}' parameter should be an array containing 35 | one or more '${b.name}' instances.`;Array.isArray(a[c])||throwError(d);for(let e of a[c])e instanceof b||throwError(d);}function throwError(a){a=a.replace(/\s+/g,' ');const b=new Error(a);b.name='assertion-failed';const c=ErrorStackParser.parse(b);throw 3<=c.length&&(b.message=`Invalid call to ${c[2].functionName}() — `+a),b} 36 | 37 | function normalizeHandler(a){return'object'==typeof a?(hasMethod({handler:a},'handle'),a):(isType({handler:a},'function'),{handle:a})} 38 | 39 | const defaultMethod='GET';const validMethods=['DELETE','GET','HEAD','POST','PUT']; 40 | 41 | class Route{constructor({match:a,handler:b,method:c}={}){this.handler=normalizeHandler(b),isType({match:a},'function'),this.match=a,c?(isOneOf({method:c},validMethods),this.method=c):this.method=defaultMethod;}} 42 | 43 | var index$1=Array.isArray||function(a){return'[object Array]'==Object.prototype.toString.call(a)}; 44 | 45 | var index=pathToRegexp; var parse_1=parse; var compile_1=compile; var tokensToFunction_1=tokensToFunction; var tokensToRegExp_1=tokensToRegExp; var PATH_REGEXP=new RegExp(['(\\\\.)','([\\/.])?(?:(?:\\:(\\w+)(?:\\(((?:\\\\.|[^\\\\()])+)\\))?|\\(((?:\\\\.|[^\\\\()])+)\\))([+*?])?|(\\*))'].join('|'),'g');function parse(a,b){for(var k,d=[],e=0,f=0,g='',h=b&&b.delimiter||'/';null!=(k=PATH_REGEXP.exec(a));){var l=k[0],n=k[1],o=k.index;if(g+=a.slice(f,o),f=o+l.length,n){g+=n[1];continue}var p=a[f],q=k[2],r=k[3],s=k[4],t=k[5],u=k[6],v=k[7];g&&(d.push(g),g='');var z=k[2]||h,A=s||t;d.push({name:r||e++,prefix:q||'',delimiter:z,optional:'?'===u||'*'===u,repeat:'+'===u||'*'===u,partial:null!=q&&null!=p&&p!==q,asterisk:!!v,pattern:A?escapeGroup(A):v?'.*':'[^'+escapeString(z)+']+?'});}return f{if(a.startsWith('/')&&g.origin!==location.origin)return null;const h=a.startsWith('/')?g.pathname:g.href,i=h.match(e);if(!i)return null;const j={};return d.forEach((k,l)=>{j[k.name]=i[l+1];}),j},handler:b,method:c});}} 48 | 49 | class LogGroup{constructor(){this._logs=[],this._childGroups=[],this._isFallbackMode=!1;const a=/Firefox\/(\d*)\.\d*/.exec(navigator.userAgent);if(a)try{const b=parseInt(a[1],10);55>b&&(this._isFallbackMode=!0);}catch(b){this._isFallbackMode=!0;}/Edge\/\d*\.\d*/.exec(navigator.userAgent)&&(this._isFallbackMode=!0);}addPrimaryLog(a){this._primaryLog=a;}addLog(a){this._logs.push(a);}addChildGroup(a){0===a._logs.length||this._childGroups.push(a);}print(){return 0===this._logs.length&&0===this._childGroups.length?void this._printLogDetails(this._primaryLog):void(this._primaryLog&&(this._isFallbackMode?this._printLogDetails(this._primaryLog):console.groupCollapsed(...this._getLogContent(this._primaryLog))),this._logs.forEach((a)=>{this._printLogDetails(a);}),this._childGroups.forEach((a)=>{a.print();}),this._primaryLog&&!this._isFallbackMode&&console.groupEnd())}_printLogDetails(a){const b=a.logFunc?a.logFunc:console.log;b(...this._getLogContent(a));}_getLogContent(a){let b=a.message;this._isFallbackMode&&'string'==typeof b&&(b=b.replace(/%c/g,''));let c=[b];return!this._isFallbackMode&&a.colors&&(c=c.concat(a.colors)),a.args&&(c=c.concat(a.args)),c}} 50 | 51 | function isServiceWorkerGlobalScope(){return'ServiceWorkerGlobalScope'in self&&self instanceof ServiceWorkerGlobalScope}function isDevBuild(){return`dev`==`prod`}function isLocalhost(){return!!('localhost'===location.hostname||'[::1]'===location.hostname||location.hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/))} 52 | 53 | self.workbox=self.workbox||{},self.workbox.LOG_LEVEL=self.workbox.LOG_LEVEL||{none:-1,verbose:0,debug:1,warn:2,error:3};const LIGHT_GREY=`#bdc3c7`; const DARK_GREY=`#7f8c8d`; const LIGHT_GREEN=`#2ecc71`; const LIGHT_YELLOW=`#f1c40f`; const LIGHT_RED=`#e74c3c`; const LIGHT_BLUE=`#3498db`;class LogHelper{constructor(){this._defaultLogLevel=isDevBuild()?self.workbox.LOG_LEVEL.debug:self.workbox.LOG_LEVEL.warn;}log(a){this._printMessage(self.workbox.LOG_LEVEL.verbose,a);}debug(a){this._printMessage(self.workbox.LOG_LEVEL.debug,a);}warn(a){this._printMessage(self.workbox.LOG_LEVEL.warn,a);}error(a){this._printMessage(self.workbox.LOG_LEVEL.error,a);}_printMessage(a,b){if(this._shouldLogMessage(a,b)){const c=this._getAllLogGroups(a,b);c.print();}}_getAllLogGroups(a,b){const c=new LogGroup,d=this._getPrimaryMessageDetails(a,b);if(c.addPrimaryLog(d),b.error){const f={message:b.error,logFunc:console.error};c.addLog(f);}const e=new LogGroup;if(b.that&&b.that.constructor&&b.that.constructor.name){const f=b.that.constructor.name;e.addLog(this._getKeyValueDetails('class',f));}return b.data&&('object'!=typeof b.data||b.data instanceof Array?e.addLog(this._getKeyValueDetails('additionalData',b.data)):Object.keys(b.data).forEach((f)=>{e.addLog(this._getKeyValueDetails(f,b.data[f]));})),c.addChildGroup(e),c}_getKeyValueDetails(a,b){return{message:`%c${a}: `,colors:[`color: ${LIGHT_BLUE}`],args:b}}_getPrimaryMessageDetails(a,b){let c,d;a===self.workbox.LOG_LEVEL.verbose?(c='Info',d=LIGHT_GREY):a===self.workbox.LOG_LEVEL.debug?(c='Debug',d=LIGHT_GREEN):a===self.workbox.LOG_LEVEL.warn?(c='Warn',d=LIGHT_YELLOW):a===self.workbox.LOG_LEVEL.error?(c='Error',d=LIGHT_RED):void 0;let e=`%c🔧 %c[${c}]`;const f=[`color: ${LIGHT_GREY}`,`color: ${d}`];let g;return'string'==typeof b?g=b:b.message&&(g=b.message),g&&(g=g.replace(/\s+/g,' '),e+=`%c ${g}`,f.push(`color: ${DARK_GREY}; font-weight: normal`)),{message:e,colors:f}}_shouldLogMessage(a,b){if(!b)return!1;let c=this._defaultLogLevel;return self&&self.workbox&&'number'==typeof self.workbox.logLevel&&(c=self.workbox.logLevel),c===self.workbox.LOG_LEVEL.none||a{let g=!1,h;if('navigate'===e.request.mode){const i=f.pathname+f.search;a.some((j)=>j.test(i))?b.some((j)=>j.test(i))?h=`The navigation route is not being used, since the `+`request URL matches both the whitelist and blacklist.`:(h=`The navigation route is being used.`,g=!0):h=`The navigation route is not being used, since the `+`URL being navigated to doesn't match the whitelist.`,logHelper.debug({that:this,message:h,data:{"request-url":f.href,whitelist:a,blacklist:b,handler:c}});}return g},handler:c,method:'GET'});}} 56 | 57 | class RegExpRoute extends Route{constructor({regExp:a,handler:b,method:c}){isInstance({regExp:a},RegExp);super({match:({url:e})=>{const f=a.exec(e.href);return f?e.origin!==location.origin&&0!==f.index?(logHelper.debug({that:this,message:`Skipping route, because the RegExp match didn't occur `+`at the start of the URL.`,data:{url:e.href,regExp:a}}),null):f.slice(1):null},handler:b,method:c});}} 58 | 59 | class Router$2{constructor({handleFetch:a}={}){'undefined'==typeof a&&(a=!0),this._routes=new Map,a&&this._addFetchListener();}_addFetchListener(){self.addEventListener('fetch',(a)=>{const b=new URL(a.request.url);if(!b.protocol.startsWith('http'))return void logHelper.log({that:this,message:'URL does not start with HTTP and so not passing through the router.',data:{request:a.request}});let c,d;for(let e of this._routes.get(a.request.method)||[]){const f=e.match({url:b,event:a});if(f){d=e,logHelper.log({that:this,message:'The router found a matching route.',data:{route:d,request:a.request}});let g=f;Array.isArray(g)&&0===g.length?g=void 0:g.constructor===Object&&0===Object.keys(g).length&&(g=void 0),c=e.handler.handle({url:b,event:a,params:g});break}}!c&&this.defaultHandler&&(c=this.defaultHandler.handle({url:b,event:a})),c&&this.catchHandler&&(c=c.catch((e)=>{return this.catchHandler.handle({url:b,event:a,error:e})})),c&&a.respondWith(c.then((e)=>{return logHelper.debug({that:this,message:'The router is managing a route with a response.',data:{route:d,request:a.request,response:e}}),e}));});}setDefaultHandler({handler:a}={}){this.defaultHandler=normalizeHandler(a);}setCatchHandler({handler:a}={}){this.catchHandler=normalizeHandler(a);}registerRoutes({routes:a}={}){isArrayOfClass({routes:a},Route);for(let b of a)this._routes.has(b.method)||this._routes.set(b.method,[]),this._routes.get(b.method).unshift(b);}registerRoute({route:a}={}){isInstance({route:a},Route),this.registerRoutes({routes:[a]});}unregisterRoutes({routes:a}={}){isArrayOfClass({routes:a},Route);for(let b of a){this._routes.has(b.method)||logHelper.error({that:this,message:`Can't unregister route; there are no ${b.method} 60 | routes registered.`,data:{route:b}});const c=this._routes.get(b.method).indexOf(b);-1caches.match(a,{cacheName:c}),whitelist:b.whitelist||[/./],blacklist:b.blacklist||[]})});}} 64 | 65 | const errors$2={"multiple-cache-will-update-plugins":'You cannot register more than one plugin that implements cacheWillUpdate.',"multiple-cache-will-match-plugins":'You cannot register more than one plugin that implements cacheWillMatch.',"invalid-response-for-caching":'The fetched response could not be cached due to an invalid response code.',"no-response-received":'No response received; falling back to cache.',"bad-cache-id":`The 'cacheId' parameter must be a string with at least `+`one character.`};var ErrorFactory$4 = new ErrorFactory$1(errors$2); 66 | 67 | class CacheableResponse{constructor({statuses:a,headers:b}={}){atLeastOne({statuses:a,headers:b}),a!==void 0&&isArrayOfType({statuses:a},'number'),b!==void 0&&isType({headers:b},'object'),this.statuses=a,this.headers=b;}isResponseCacheable({request:a,response:b}={}){isInstance({response:b},Response);let c=!0;if(this.statuses&&(c=this.statuses.includes(b.status)),this.headers&&c&&(c=Object.keys(this.headers).some((d)=>{return b.headers.get(d)===this.headers[d]})),!c){const d={response:b};this.statuses&&(d['valid-status-codes']=JSON.stringify(this.statuses)),this.headers&&(d['valid-headers']=JSON.stringify(this.headers)),a&&(d.request=a),logHelper.debug({message:`The response does not meet the criteria for being added to the 68 | cache.`,data:d});}return c}} 69 | 70 | class CacheableResponsePlugin extends CacheableResponse{cacheWillUpdate({request:a,response:b}={}){return this.isResponseCacheable({request:a,response:b})}} 71 | 72 | const getDefaultCacheName=({cacheId:a}={})=>{let b=`workbox-runtime-caching`;return a&&(b=`${a}-${b}`),self&&self.registration&&(b+=`-${self.registration.scope}`),b}; 73 | const pluginCallbacks=['cacheDidUpdate','cacheWillMatch','cacheWillUpdate','fetchDidFail','requestWillFetch']; 74 | 75 | var cleanResponseCopy = (({response:a})=>{isInstance({response:a},Response);const b=a.clone(),c='body'in b?Promise.resolve(b.body):b.blob();return c.then((d)=>{return new Response(d,{headers:b.headers,status:b.status,statusText:b.statusText})})}); 76 | 77 | class RequestWrapper{constructor({cacheName:a,cacheId:b,plugins:c,fetchOptions:d,matchOptions:e}={}){if(b&&('string'!=typeof b||0===b.length))throw ErrorFactory$4.createError('bad-cache-id');a?(isType({cacheName:a},'string'),this.cacheName=a,b&&(this.cacheName=`${b}-${this.cacheName}`)):this.cacheName=getDefaultCacheName({cacheId:b}),d&&(isType({fetchOptions:d},'object'),this.fetchOptions=d),e&&(isType({matchOptions:e},'object'),this.matchOptions=e),this.plugins=new Map,c&&(isArrayOfType({plugins:c},'object'),c.forEach((f)=>{for(let g of pluginCallbacks)if('function'==typeof f[g]){if(!this.plugins.has(g))this.plugins.set(g,[]);else if('cacheWillUpdate'===g)throw ErrorFactory$4.createError('multiple-cache-will-update-plugins');else if('cacheWillMatch'===g)throw ErrorFactory$4.createError('multiple-cache-will-match-plugins');this.plugins.get(g).push(f);}})),this.plugins.has('cacheWillUpdate')&&(this._userSpecifiedCachableResponsePlugin=this.plugins.get('cacheWillUpdate')[0]);}getDefaultCacheableResponsePlugin(){return this._defaultCacheableResponsePlugin||(this._defaultCacheableResponsePlugin=new CacheableResponsePlugin({statuses:[200]})),this._defaultCacheableResponsePlugin}async getCache(){return this._cache||(this._cache=await caches.open(this.cacheName)),this._cache}async match({request:a}){atLeastOne({request:a});const b=await this.getCache();let c=await b.match(a,this.matchOptions);if(this.plugins.has('cacheWillMatch')){const d=this.plugins.get('cacheWillMatch')[0];c=d.cacheWillMatch({request:a,cache:b,cachedResponse:c,matchOptions:this.matchOptions,cacheName:this.cacheName});}return c}async fetch({request:a}){'string'==typeof a?a=new Request(a):isInstance({request:a},Request);const b=this.plugins.has('fetchDidFail')?a.clone():null;if(this.plugins.has('requestWillFetch'))for(let c of this.plugins.get('requestWillFetch')){const d=c.requestWillFetch({request:a});isInstance({returnedPromise:d},Promise);const e=await d;isInstance({returnedRequest:e},Request),a=e;}try{return await fetch(a,this.fetchOptions)}catch(c){if(this.plugins.has('fetchDidFail'))for(let d of this.plugins.get('fetchDidFail'))d.fetchDidFail({request:b.clone()});throw c}}async fetchAndCache({request:a,waitOnCache:b,cacheKey:c,cacheResponsePlugin:d,cleanRedirects:e}){atLeastOne({request:a});let f;const g=await this.fetch({request:a}),h=this._userSpecifiedCachableResponsePlugin||d||this.getDefaultCacheableResponsePlugin(),i=h.cacheWillUpdate({request:a,response:g});if(i){const j=e&&g.redirected?await cleanResponseCopy({response:g}):g.clone();f=this.getCache().then(async(k)=>{let l;const m=c||a;if('opaque'!==g.type&&this.plugins.has('cacheDidUpdate')&&(l=await this.match({request:m})),await k.put(m,j),this.plugins.has('cacheDidUpdate'))for(let n of this.plugins.get('cacheDidUpdate'))await n.cacheDidUpdate({cacheName:this.cacheName,oldResponse:l,newResponse:j,url:'url'in m?m.url:m});});}else if(!i&&b)throw ErrorFactory$4.createError('invalid-response-for-caching');return b&&f&&(await f),g}} 78 | 79 | class Handler{constructor({requestWrapper:a,waitOnCache:b}={}){this.requestWrapper=a?a:new RequestWrapper,this.waitOnCache=!!b;}handle({event:a,params:b}={}){throw Error('This abstract method must be implemented in a subclass.')}} 80 | 81 | class CacheFirst extends Handler{async handle({event:a}={}){isInstance({event:a},FetchEvent);const b=await this.requestWrapper.match({request:a.request});return b||(await this.requestWrapper.fetchAndCache({request:a.request,waitOnCache:this.waitOnCache}))}} 82 | 83 | class CacheOnly extends Handler{async handle({event:a}={}){return isInstance({event:a},FetchEvent),await this.requestWrapper.match({request:a.request})}} 84 | 85 | class NetworkFirst extends Handler{constructor(a={}){super(a),this._cacheablePlugin=new CacheableResponsePlugin({statuses:[0,200]});const{networkTimeoutSeconds:b}=a;b&&(isType({networkTimeoutSeconds:b},'number'),this.networkTimeoutSeconds=b);}async handle({event:a}={}){isInstance({event:a},FetchEvent);const b=[];let c;this.networkTimeoutSeconds&&b.push(new Promise((e)=>{c=setTimeout(()=>{e(this.requestWrapper.match({request:a.request}));},1e3*this.networkTimeoutSeconds);}));const d=this.requestWrapper.fetchAndCache({request:a.request,waitOnCache:this.waitOnCache,cacheResponsePlugin:this._cacheablePlugin}).then((e)=>{return c&&clearTimeout(c),e?e:Promise.reject(ErrorFactory$4.createError('no-response-received'))}).catch(()=>this.requestWrapper.match({request:a.request}));return b.push(d),Promise.race(b)}} 86 | 87 | class NetworkOnly extends Handler{async handle({event:a}={}){return isInstance({event:a},FetchEvent),await this.requestWrapper.fetch({request:a.request})}} 88 | 89 | class StaleWhileRevalidate extends Handler{constructor(a={}){super(a),this._cacheablePlugin=new CacheableResponsePlugin({statuses:[0,200]});}async handle({event:a}={}){isInstance({event:a},FetchEvent);const b=this.requestWrapper.fetchAndCache({request:a.request,waitOnCache:this.waitOnCache,cacheResponsePlugin:this._cacheablePlugin}).catch(()=>Response.error()),c=await this.requestWrapper.match({request:a.request});return c||(await b)}} 90 | 91 | let tmpIdbName=`workbox-cache-expiration`;self&&self.registration&&(tmpIdbName+=`-${self.registration.scope}`);const idbName=tmpIdbName;const idbVersion=1;const urlPropertyName='url';const timestampPropertyName='timestamp'; 92 | 93 | function createCommonjsModule(fn, module) { 94 | return module = { exports: {} }, fn(module, module.exports), module.exports; 95 | } 96 | 97 | var idb=createCommonjsModule(function(a){'use strict';(function(){function b(r){return Array.prototype.slice.call(r)}function c(r){return new Promise(function(s,t){r.onsuccess=function(){s(r.result);},r.onerror=function(){t(r.error);};})}function d(r,s,t){var u,v=new Promise(function(w,x){u=r[s].apply(r,t),c(u).then(w,x);});return v.request=u,v}function e(r,s,t){var u=d(r,s,t);return u.then(function(v){return v?new k(v,u.request):void 0})}function f(r,s,t){t.forEach(function(u){Object.defineProperty(r.prototype,u,{get:function(){return this[s][u]},set:function(v){this[s][u]=v;}});});}function g(r,s,t,u){u.forEach(function(v){v in t.prototype&&(r.prototype[v]=function(){return d(this[s],v,arguments)});});}function h(r,s,t,u){u.forEach(function(v){v in t.prototype&&(r.prototype[v]=function(){return this[s][v].apply(this[s],arguments)});});}function i(r,s,t,u){u.forEach(function(v){v in t.prototype&&(r.prototype[v]=function(){return e(this[s],v,arguments)});});}function j(r){this._index=r;}function k(r,s){this._cursor=r,this._request=s;}function l(r){this._store=r;}function m(r){this._tx=r,this.complete=new Promise(function(s,t){r.oncomplete=function(){s();},r.onerror=function(){t(r.error);},r.onabort=function(){t(r.error);};});}function n(r,s,t){this._db=r,this.oldVersion=s,this.transaction=new m(t);}function o(r){this._db=r;}f(j,'_index',['name','keyPath','multiEntry','unique']),g(j,'_index',IDBIndex,['get','getKey','getAll','getAllKeys','count']),i(j,'_index',IDBIndex,['openCursor','openKeyCursor']),f(k,'_cursor',['direction','key','primaryKey','value']),g(k,'_cursor',IDBCursor,['update','delete']),['advance','continue','continuePrimaryKey'].forEach(function(r){r in IDBCursor.prototype&&(k.prototype[r]=function(){var s=this,t=arguments;return Promise.resolve().then(function(){return s._cursor[r].apply(s._cursor,t),c(s._request).then(function(u){return u?new k(u,s._request):void 0})})});}),l.prototype.createIndex=function(){return new j(this._store.createIndex.apply(this._store,arguments))},l.prototype.index=function(){return new j(this._store.index.apply(this._store,arguments))},f(l,'_store',['name','keyPath','indexNames','autoIncrement']),g(l,'_store',IDBObjectStore,['put','add','delete','clear','get','getAll','getKey','getAllKeys','count']),i(l,'_store',IDBObjectStore,['openCursor','openKeyCursor']),h(l,'_store',IDBObjectStore,['deleteIndex']),m.prototype.objectStore=function(){return new l(this._tx.objectStore.apply(this._tx,arguments))},f(m,'_tx',['objectStoreNames','mode']),h(m,'_tx',IDBTransaction,['abort']),n.prototype.createObjectStore=function(){return new l(this._db.createObjectStore.apply(this._db,arguments))},f(n,'_db',['name','version','objectStoreNames']),h(n,'_db',IDBDatabase,['deleteObjectStore','close']),o.prototype.transaction=function(){return new m(this._db.transaction.apply(this._db,arguments))},f(o,'_db',['name','version','objectStoreNames']),h(o,'_db',IDBDatabase,['close']),['openCursor','openKeyCursor'].forEach(function(r){[l,j].forEach(function(s){s.prototype[r.replace('open','iterate')]=function(){var t=b(arguments),u=t[t.length-1],v=this._store||this._index,w=v[r].apply(v,t.slice(0,-1));w.onsuccess=function(){u(w.result);};};});}),[j,l].forEach(function(r){r.prototype.getAll||(r.prototype.getAll=function(s,t){var u=this,v=[];return new Promise(function(w){u.iterateCursor(s,function(x){return x?(v.push(x.value),void 0!==t&&v.length==t?void w(v):void x.continue()):void w(v)});})});});var q={open:function(r,s,t){var u=d(indexedDB,'open',[r,s]),v=u.request;return v.onupgradeneeded=function(w){t&&t(new n(v.result,w.oldVersion,v.transaction));},u.then(function(w){return new o(w)})},delete:function(r){return d(indexedDB,'deleteDatabase',[r])}};a.exports=q,a.exports.default=a.exports;})();}); 98 | 99 | const errors$3={"max-entries-or-age-required":`Either the maxEntries or maxAgeSeconds 100 | parameters (or both) are required when constructing Plugin.`,"max-entries-must-be-number":`The maxEntries parameter to the Plugin 101 | constructor must either be a number or undefined.`,"max-age-seconds-must-be-number":`The maxAgeSeconds parameter to the Plugin 102 | constructor must either be a number or undefined.`};var ErrorFactory$5 = new ErrorFactory$1(errors$3); 103 | 104 | class CacheExpiration{constructor({maxEntries:a,maxAgeSeconds:b}={}){if(!(a||b))throw ErrorFactory$5.createError('max-entries-or-age-required');if(a&&'number'!=typeof a)throw ErrorFactory$5.createError('max-entries-must-be-number');if(b&&'number'!=typeof b)throw ErrorFactory$5.createError('max-age-seconds-must-be-number');this.maxEntries=a,this.maxAgeSeconds=b,this._dbs=new Map,this._caches=new Map,this._expirationMutex=!1,this._timestampForNextRun=null;}async getDB({cacheName:a}={}){isType({cacheName:a},'string');const b=`${idbName}-${a}`;if(!this._dbs.has(b)){const c=await idb.open(b,idbVersion,(d)=>{const e=d.createObjectStore(a,{keyPath:urlPropertyName});e.createIndex(timestampPropertyName,timestampPropertyName,{unique:!1});});this._dbs.set(b,c);}return this._dbs.get(b)}async getCache({cacheName:a}={}){if(isType({cacheName:a},'string'),!this._caches.has(a)){const b=await caches.open(a);this._caches.set(a,b);}return this._caches.get(a)}isResponseFresh({cacheName:a,cachedResponse:b,now:c}={}){if(b&&this.maxAgeSeconds){isInstance({cachedResponse:b},Response);const d=b.headers.get('date');if(d){'undefined'==typeof c&&(c=Date.now());const e=new Date(d),f=e.getTime();return!!isNaN(f)||f+1e3*this.maxAgeSeconds>c}return this.expireEntries({cacheName:a,now:c}),!0}return!0}async updateTimestamp({cacheName:a,url:b,now:c}={}){isType({url:b},'string'),isType({cacheName:a},'string');const d=new URL(b,location);d.hash='','undefined'==typeof c&&(c=Date.now());const e=await this.getDB({cacheName:a}),f=e.transaction(a,'readwrite');f.objectStore(a).put({[timestampPropertyName]:c,[urlPropertyName]:d.href}),await f.complete;}async expireEntries({cacheName:a,now:b}={}){if(this._expirationMutex)return void(this._timestampForNextRun=b);this._expirationMutex=!0,isType({cacheName:a},'string'),'undefined'==typeof b&&(b=Date.now());const c=this.maxAgeSeconds?await this.findOldEntries({cacheName:a,now:b}):[],d=this.maxEntries?await this.findExtraEntries({cacheName:a}):[],e=[...new Set(c.concat(d))];if(await this.deleteFromCacheAndIDB({cacheName:a,urls:e}),0{i&&(i.value[timestampPropertyName]this.maxEntries&&(d=c.transaction(a,'readonly'),e=d.objectStore(a),f=e.index(timestampPropertyName),f.iterateCursor((h)=>{h&&(b.push(h.value[urlPropertyName]),g-b.length>this.maxEntries&&h.continue());})),await d.complete,b}async deleteFromCacheAndIDB({cacheName:a,urls:b}={}){if(isType({cacheName:a},'string'),isArrayOfType({urls:b},'string'),0{return a.headers.has(e)&&b.headers.has(e)});return d?c.every((e)=>{return a.headers.has(e)===b.headers.has(e)&&a.headers.get(e)===b.headers.get(e)}):(logHelper.log({message:`Unable to determine whether the response has been updated 119 | because none of the headers that would be checked are present.`,data:{"First Response":a,"Second Response":b,"Headers To Check":JSON.stringify(c)}}),!0)} 120 | 121 | class BroadcastCacheUpdate{constructor({channelName:a,headersToCheck:b,source:c}={}){if('string'!=typeof a||0===a.length)throw ErrorFactory$6.createError('channel-name-required');this.channelName=a,this.headersToCheck=b||defaultHeadersToCheck,this.source=c||defaultSource;}get channel(){return this._channel||(this._channel=new BroadcastChannel(this.channelName)),this._channel}notifyIfUpdated({first:a,second:b,cacheName:c,url:d}){isType({cacheName:c},'string'),responsesAreSame({first:a,second:b,headersToCheck:this.headersToCheck})||broadcastUpdate({cacheName:c,url:d,channel:this.channel,source:this.source});}} 122 | 123 | class BroadcastCacheUpdatePlugin extends BroadcastCacheUpdate{cacheDidUpdate({cacheName:a,oldResponse:b,newResponse:c,url:d}){isType({cacheName:a},'string'),isInstance({newResponse:c},Response),b&&this.notifyIfUpdated({cacheName:a,first:b,second:c,url:d});}} 124 | 125 | class Strategies{constructor({cacheId:a}={}){this._cacheId=a;}cacheFirst(a){return this._getCachingMechanism(CacheFirst,a)}cacheOnly(a){return this._getCachingMechanism(CacheOnly,a)}networkFirst(a){return this._getCachingMechanism(NetworkFirst,a)}networkOnly(a){return this._getCachingMechanism(NetworkOnly,a)}staleWhileRevalidate(a){return this._getCachingMechanism(StaleWhileRevalidate,a)}_getCachingMechanism(a,b={}){const c={cacheExpiration:CacheExpirationPlugin,broadcastCacheUpdate:BroadcastCacheUpdatePlugin,cacheableResponse:CacheableResponsePlugin},d={plugins:[]};b.excludeCacheId||(d.cacheId=this._cacheId),b.cacheName&&(d.cacheName=b.cacheName);const e=Object.keys(c);return e.forEach((f)=>{if(b[f]){const g=c[f],h=b[f];d.plugins.push(new g(h));}}),b.plugins&&b.plugins.forEach((f)=>{d.plugins.push(f);}),b.requestWrapper=new RequestWrapper(d),new a(b)}} 126 | 127 | const errorMessageFactory=(a,b)=>{let c=`An error was thrown by workbox with error code: `+`;'${a}'`;return b&&(c+=` with extras: '${JSON.stringify(b)}'`),c}; 128 | 129 | class WorkboxError extends Error{constructor(a,b){super(),this.name=a,this.message=errorMessageFactory(a,b),b&&(this.extras=b);}} 130 | 131 | class BaseCacheManager{constructor({cacheName:a,cacheId:b,plugins:c}={}){if(b&&('string'!=typeof b||0===b.length))throw new WorkboxError('bad-cache-id',{cacheId:b});this._entriesToCache=new Map,this._requestWrapper=new RequestWrapper({cacheName:a,cacheId:b,plugins:c,fetchOptions:{credentials:'same-origin'}});}_addEntries(a){this._parsedCacheUrls=null,a.forEach((b)=>{this._addEntryToInstallList(this._parseEntry(b));});}getCacheName(){return this._requestWrapper.cacheName}getCachedUrls(){return this._parsedCacheUrls||(this._parsedCacheUrls=Array.from(this._entriesToCache.keys()).map((a)=>new URL(a,location).href)),this._parsedCacheUrls}_addEntryToInstallList(a){const b=a.entryID,c=this._entriesToCache.get(a.entryID);return c?void this._onDuplicateInstallEntryFound(a,c):void this._entriesToCache.set(b,a)}async install(){if(0===this._entriesToCache.size)return[];const a=[];return this._entriesToCache.forEach((b)=>{a.push(this._cacheEntry(b));}),Promise.all(a)}async _cacheEntry(a){const b=await this._isAlreadyCached(a),c={url:a.request.url,revision:a.revision,wasUpdated:!b};if(b)return c;try{return await this._requestWrapper.fetchAndCache({request:a.getNetworkRequest(),waitOnCache:!0,cacheKey:a.request,cleanRedirects:!0}),await this._onEntryCached(a),c}catch(d){throw new WorkboxError('request-not-cached',{url:a.request.url,error:d})}}async cleanup(){if(!(await caches.has(this.getCacheName())))return;const a=[];this._entriesToCache.forEach((e)=>{a.push(e.request.url);});const b=await this._getCache(),c=await b.keys(),d=c.filter((e)=>!a.includes(e.url));return Promise.all(d.map(async(e)=>{await b.delete(e),await this._onEntryDeleted(e.url);}))}async _getCache(){return this._cache||(this._cache=await caches.open(this.getCacheName())),this._cache}_parseEntry(){throw new WorkboxError('requires-overriding')}_onDuplicateEntryFound(){throw new WorkboxError('requires-overriding')}_isAlreadyCached(){throw new WorkboxError('requires-overriding')}_onEntryCached(){throw new WorkboxError('requires-overriding')}_onEntryDeleted(){throw new WorkboxError('requires-overriding')}} 132 | 133 | class IDBHelper{constructor(a,b,c){if(a==void 0||b==void 0||c==void 0)throw Error('name, version, storeName must be passed to the constructor.');this._name=a,this._version=b,this._storeName=c;}_getDb(){return this._dbPromise?this._dbPromise:(this._dbPromise=idb.open(this._name,this._version,(a)=>{a.createObjectStore(this._storeName);}).then((a)=>{return a}),this._dbPromise)}close(){return this._dbPromise?this._dbPromise.then((a)=>{a.close(),this._dbPromise=null;}):void 0}put(a,b){return this._getDb().then((c)=>{const d=c.transaction(this._storeName,'readwrite'),e=d.objectStore(this._storeName);return e.put(b,a),d.complete})}delete(a){return this._getDb().then((b)=>{const c=b.transaction(this._storeName,'readwrite'),d=c.objectStore(this._storeName);return d.delete(a),c.complete})}get(a){return this._getDb().then((b)=>{return b.transaction(this._storeName).objectStore(this._storeName).get(a)})}getAllValues(){return this._getDb().then((a)=>{return a.transaction(this._storeName).objectStore(this._storeName).getAll()})}getAllKeys(){return this._getDb().then((a)=>{return a.transaction(this._storeName).objectStore(this._storeName).getAllKeys()})}} 134 | 135 | const cacheBustParamName='_workbox-precaching';const version='v1';const dbName='workbox-precaching';const dbVersion='1';const dbStorename='asset-revisions';let tmpRevisionedCacheName=`workbox-precaching-revisioned-${version}`;self&&self.registration&&(tmpRevisionedCacheName+=`-${self.registration.scope}`);const defaultRevisionedCacheName=tmpRevisionedCacheName; 136 | 137 | class RevisionDetailsModel{constructor(){this._idbHelper=new IDBHelper(dbName,dbVersion,dbStorename);}get(a){return this._idbHelper.get(a)}put(a,b){return this._idbHelper.put(a,b)}delete(a){return this._idbHelper.delete(a)}_close(){this._idbHelper.close();}} 138 | 139 | class BaseCacheEntry{constructor({entryID:a,revision:b,request:c,cacheBust:d}){this.entryID=a,this.revision=b,this.request=c,this.cacheBust=d;}getNetworkRequest(){if(!0!==this.cacheBust)return this.request;let a=this.request.url;const b={};if(!0===this.cacheBust)if('cache'in Request.prototype)b.cache='reload';else{const c=new URL(a,location);c.search+=(c.search?'&':'')+encodeURIComponent(cacheBustParamName)+'='+encodeURIComponent(this.revision),a=c.toString();}return new Request(a,b)}} 140 | 141 | class StringCacheEntry extends BaseCacheEntry{constructor(a){if(isType({url:a},'string'),0===a.length)throw new WorkboxError('invalid-string-entry',{url:a});super({entryID:a,revision:a,request:new Request(a),cacheBust:!1});}} 142 | 143 | class ObjectCacheEntry extends BaseCacheEntry{constructor({entryID:a,revision:b,url:c,cacheBust:d}){if('undefined'!=typeof b&&(isType({revision:b},'string'),0===b.length))throw new WorkboxError('invalid-object-entry',{problemParam:'revision',problemValue:b});if('undefined'==typeof d&&(d=!!b),isType({cacheBust:d},'boolean'),isType({url:c},'string'),0===c.length)throw new WorkboxError('invalid-object-entry',{problemParam:'url',problemValue:c});if('undefined'==typeof a)a=new URL(c,location).toString();else if(0===a.length)throw new WorkboxError('invalid-object-entry',{problemParam:'entryID',problemValue:a});super({entryID:a,revision:b||c,request:new Request(c),cacheBust:d});}} 144 | 145 | class RevisionedCacheManager extends BaseCacheManager{constructor(a={}){a.cacheName=a.cacheName||defaultRevisionedCacheName,super(a),this._revisionDetailsModel=new RevisionDetailsModel;}addToCacheList({revisionedFiles:a}={}){isInstance({revisionedFiles:a},Array),super._addEntries(a);const b=a.filter((c)=>'string'==typeof c||!c.revision);0{return this._close()})}_createLogFriendlyString(a){let b=`\n`;return a.forEach((c)=>{b+=` URL: '${c.url}' Revision: `+`'${c.revision}'\n`;}),b}install(){return super.install().then((a)=>{const b=[],c=[];a.forEach((e)=>{e.wasUpdated?b.push({url:e.url,revision:e.revision}):c.push({url:e.url,revision:e.revision});});const d={};return 0{const d=this._revisionedCacheManager.getCachedUrls();0{if(a)return self.skipWaiting()}));}),self.addEventListener('activate',(c)=>{c.waitUntil(this._revisionedCacheManager.cleanup().then(()=>{if(b)return self.clients.claim()}));});}_registerDefaultRoutes(a,b){const c=[];(a||b)&&c.push(this._getCacheMatchPlugin(a,b));const d=this.strategies.cacheFirst({cacheName:this._revisionedCacheManager.getCacheName(),plugins:c,excludeCacheId:!0});this.router.registerRoute(({url:f})=>{f.hash='';const g=this._revisionedCacheManager.getCachedUrls();if(-1!==g.indexOf(f.href))return!0;let h=this._removeIgnoreUrlParams(f.href,a);return-1!==g.indexOf(h.href)||b&&h.pathname.endsWith('/')&&(f.pathname+=b,-1!==g.indexOf(f.href))},d);}_getCacheMatchPlugin(a,b){return{cacheWillMatch:async({request:d,cache:e,cachedResponse:f,matchOptions:g})=>{if(f)return f;let h=this._removeIgnoreUrlParams(d.url,a);return e.match(h.toString(),g).then((i)=>{return!i&&h.pathname.endsWith('/')?(h.pathname+=b,e.match(h.toString(),g)):i})}}}_removeIgnoreUrlParams(a,b){const c=new URL(a),d=c.search.slice(1),e=d.split('&'),f=e.map((i)=>{return i.split('=')}),g=f.filter((i)=>{return b.every((j)=>{return!j.test(i[0])})}),h=g.map((i)=>{return i.join('=')});return c.search=h.join('&'),c}} 154 | 155 | return WorkboxSW$1; 156 | 157 | }()); 158 | //# sourceMappingURL=workbox-sw.prod.v1.3.0.js.map 159 | -------------------------------------------------------------------------------- /store/index.js: -------------------------------------------------------------------------------- 1 | import Vuex from 'vuex' 2 | import {Auth, GoogleAuthProvider} from '~/plugins/firebase-client-init.js' 3 | 4 | function buildUserObject (authData) { 5 | let { email, displayName, uid, photoURL } = authData.user 6 | let user = {} 7 | user['email'] = email 8 | user['name'] = displayName 9 | user['uid'] = uid 10 | user['picture'] = photoURL 11 | return user 12 | } 13 | 14 | const createStore = () => { 15 | return new Vuex.Store({ 16 | state: { 17 | user: null, 18 | loading: false 19 | }, 20 | 21 | getters: { 22 | activeUser: (state, getters) => { 23 | return state.user 24 | }, 25 | isLoading: (state, getters) => { 26 | return state.loading 27 | } 28 | }, 29 | 30 | mutations: { 31 | setUser (state, payload) { 32 | state.user = payload 33 | }, 34 | setLoading (state, payload) { 35 | state.loading = payload 36 | } 37 | }, 38 | 39 | actions: { 40 | nuxtServerInit ({ commit }, { req }) { 41 | if (req.user) { 42 | commit('setUser', req.user) 43 | } 44 | }, 45 | 46 | async signInWithGooglePopup ({commit}) { 47 | commit('setLoading', true) 48 | let authData = await Auth.signInWithPopup(GoogleAuthProvider) 49 | commit('setUser', buildUserObject(authData)) 50 | commit('setLoading', false) 51 | }, 52 | 53 | async signOut ({commit}) { 54 | await Auth.signOut() 55 | commit('setUser', null) 56 | } 57 | } 58 | }) 59 | } 60 | 61 | export default createStore 62 | --------------------------------------------------------------------------------
If you are not logged in then you should be redirected to login when clicking this link to the Admin page
Test Me
nuxt-child
Some content will go here about this project.