5. PWA básica

Versión para imprimir.

A. Introduccion

B. Hazlo funcionar (con videos)

  1. Prueba e instala, de preferencia con Chrome, el sitio https://pwabasw.web.app/.

  2. Descarga el archivo /src/pwab.zip y descompáctalo.

  3. Crea una cuenta de email de Google por ejemplo, juanito@google.com

  4. En https://console.firebase.google.com/ selecciona la cuenta de email que acabas de crear, por ejemplo, juanito@google.com

  5. Crea un proyecto de Firebase nuevo. Por ejemplo, pwabase. No uses IA ni Amalytics.

  6. Crea una cuenta de GitHub usando el email anterior y selecciona el nombre de usuario unsando la parte inicial del correo electrónico, por ejemplo juanito.

  7. Crea un repositorio nuevo. En el nombre del repositorio, pon el nombre del proyecto de firebase, por ejemplo pwabase

  8. Importa el proyecto de GitHub a Visual Studio Code

  9. En Visual Studio Code selecciona el menú Terminal > New Terminal y al abrirse esta, teclea cmd y luego ENTER

  10. En la consola de firebase, en el costado izquierdo, en la opción Hosting y sin servidores selecciona Hosting. Añade hosting a ti sitio (Sin usar IA ni Amalytics) y sigue las instrucciones que se indican para para la consola, que básicamente son

    1. firebase login
      Si no has iniciado sesión, se abre el navegador y tienes que entrar a sesión con tu cuenta de Firebase.
    2. firebase init
      Selecciona el proyecto que ya está creado en Firebase, solo ponle hosting y emulador de hosting (selecciona que lo descargue.) Selecciona que no sea una Single page app.
  11. Edita los archivos que desees.

  12. El archivo sw.js tiene una lista de los archivos que se instalan. El archivo instruccionesListadoSw.txt te indica como generarla usando Visual Studio Code.

  13. Crea los íconos del proyecto con https://www.photopea.com/.

  14. Crea los íconos enmascarables con https://maskable.app/ a partir del archivo «icono2048.png».

  15. Coloca el archivo favicon.ico en la carpeta public del proyecto.

  16. Coloca los otros íconos en la carpeta public/img y asegúrate de que estén declarados en el archivo site.webmanifest.

  17. El archivo public/sw.js tiene una lista de los archivos que se instalan. El archivo instruccionesListadoSw.txt te indica como generarla usando Visual Studio Code.

  18. Para depurar localmente tu aplicación, teclea en la terminal
    firebase emulators:start
    Lo puedes ver en la dirección http://localhost:5000. Para detener el servidor local, en la terminal debes presionar Ctrl+C En el siguiente enlace se muestra como probar la aplicación. La forma de levantar el servidor es diferente, pero la forma de usar el navegador es la misma. Prueba tu PWA.

  19. Cada vez que vayas a publicar cambios, debes modificar el valor de VERSION en el archivo sw.js para poder ver los cambios en el navegador.

  20. Cuando desarrolles, es incómodo modificar la versión cada que realizas cambios; en vez de ello desinstala la app:

    1. Abre las herramientas de depuración haciendo clic derecho en la página y selecciona Inspeccionar (o Inspect si aparece en inglés).

    2. En la Pestaña Aplicación (o Application en inglés) selecciona Almacenamoento (o Storage en inglés). Cliquea Borrar datos del sitio.

    3. Recarga la app, de preferencia haciendo clic derecho en el ícono de volver a cargar la página Ïmagen del ícono de recarga y seleccionando vaciar caché y volver a cargar de manera forzada (o algo parecido). Si no aparece un menú emergente, simplemente cliquea volver a cargar la página Ïmagen del ícono de recarga. Revisa que no aparezca ningún error ni en la pestañas Consola, ni en Red.

    4. Tanbién puedes usar la combinación de teclas Ctrl+Mayúsculas+r para forzar que se actualice temporalmente el navegador en caso de que no se vean los cambios.

    5. En la Pestaña Aplicación (o Application en inglés) selecciona Archivo de manifiesto (o Manifest file en inglés). Esta herramienta analiza la estructura del archivo de manifiesto y te indica si hay un error.

    6. En la Pestaña Aplicación (o Application en inglés) selecciona Almacenamiento en caché (o Cache storage en inglés). Aquí puedes revisar si el caché de la aplicación se llenó correctamente. En caso de que esté vacío, es que hubo algún error durante la carga y la app se ejecuta más lenta.

    7. El archivo .htaccess Solo se utiliza en servidores compatibles con apache, como Ngnx o infinity free. Sirve para quitar el cache de http y declarar el mime type de los archivos .manifest

    8. Para desplegar tu aplicación, teclea en la terminal
      firebase deploy
      Te va a indicar la dirección para ver tu sitio en internet.

    9. Si quieres cerrar tu sesión de deasarrollo, teclea en la terminal: firebase logout
    10. Instala y usa tu PWA en Windows. Aunque en este video se recomienda usar Edge, al momento de actualizar el contenido, la opción más recomendada es Chrome para que te muestre las descripciones y las capturas de pantalla.

    11. Instala y usa tu PWA en Android. Al momento de actualizar las notas, tal vez no te aparezca el botón para instalar y tengas que seleccionar la acción de agregar a la pantalla principal que aparece en el menú de extensión de Chrome.

    12. Instala y usa tu PWA en iOS (iPhone y iPad).

C. Archivos

Haz clic en los triángulos para expandir las carpetas

D. Carpeta « public »

Versión para imprimir.

A. public / .htaccess

  • Este archivo configura las respuestas del servidor..

  • Lo utilizan principalmente servidores como Apache o Nginx.

  • Configura el mime type para el archivo de manifiesto y sedhabilita el uso de la cache general de http..

1
AddType application/manifest+json .webmanifest
2
3
ExpiresActive On
4
5
Header set Cache-Control "max-age=1, must-revalidate"
6

B. public / 404.html

1
<!DOCTYPE html>
2
<html>
3
  <head>
4
    <meta charset="utf-8">
5
    <meta name="viewport" content="width=device-width, initial-scale=1">
6
    <title>Page Not Found</title>
7
8
    <style media="screen">
9
      body { background: #ECEFF1; color: rgba(0,0,0,0.87); font-family: Roboto, Helvetica, Arial, sans-serif; margin: 0; padding: 0; }
10
      #message { background: white; max-width: 360px; margin: 100px auto 16px; padding: 32px 24px 16px; border-radius: 3px; }
11
      #message h3 { color: #888; font-weight: normal; font-size: 16px; margin: 16px 0 12px; }
12
      #message h2 { color: #ffa100; font-weight: bold; font-size: 16px; margin: 0 0 8px; }
13
      #message h1 { font-size: 22px; font-weight: 300; color: rgba(0,0,0,0.6); margin: 0 0 16px;}
14
      #message p { line-height: 140%; margin: 16px 0 24px; font-size: 14px; }
15
      #message a { display: block; text-align: center; background: #039be5; text-transform: uppercase; text-decoration: none; color: white; padding: 16px; border-radius: 4px; }
16
      #message, #message a { box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24); }
17
      #load { color: rgba(0,0,0,0.4); text-align: center; font-size: 13px; }
18
      @media (max-width: 600px) {
19
        body, #message { margin-top: 0; background: white; box-shadow: none; }
20
        body { border-top: 16px solid #ffa100; }
21
      }
22
    </style>
23
  </head>
24
  <body>
25
    <div id="message">
26
      <h2>404</h2>
27
      <h1>Page Not Found</h1>
28
      <p>The specified file was not found on this website. Please check the URL for mistakes and try again.</p>
29
      <h3>Why am I seeing this?</h3>
30
      <p>This page was generated by the Firebase Command-Line Interface. To modify it, edit the <code>404.html</code> file in your project's configured <code>public</code> directory.</p>
31
    </div>
32
  </body>
33
</html>

C. public / favicon.ico

favicon.ico

D. public / index.html

1
<!DOCTYPE html>
2
<html lang="es">
3
4
<head>
5
6
 <meta charset="UTF-8">
7
8
 <title>PWA Básica</title>
9
10
 <!-- Resumen para los motores de búsqueda. -->
11
 <meta name="description" content="Ejemplo de PWA">
12
13
 <script type="module" src="js/registraServiceWorker.js"></script>
14
15
 <meta name="viewport" content="width=device-width">
16
17
 <!-- Color de la barra de navegación de Chrome en dispositivos móviles. -->
18
 <meta name="theme-color" content="#cbc693">
19
20
 <!-- Ícono para la página web, que normalmente se pone en la raíz del sitio.
21
  Puede ser diferente para cada página. -->
22
 <link rel="icon" sizes="32x32" href="favicon.ico">
23
24
 <!-- Configuración de la PWA para Chrome, Edge y Safari.
25
  Debe ponerse en todas las páginas. -->
26
 <link rel="manifest" href="site.webmanifest">
27
28
 <link rel="stylesheet" href="css/estilos.css">
29
30
</head>
31
32
<body>
33
34
 <h1>PWA Básica</h1>
35
36
 <p>Hola mundo.</p>
37
38
</body>
39
40
</html>

E. public / site.webmanifest

  • Este archivo sirve para configurar los instaladores de la aplicación.

Explicación de las propiedades

short_name

Nombre corto. Normalmente se despliega en dispositivos móviles. Máximo 20 caracteres.

name

Nombre largo. Normalmente se despliega en computadoras de escritorio. Máximo 30 caracteres.

id

Identificador del archivo de instalación. Normalmente es la ruta del archivo inicial de la app.

start_url

Ruta del archivo inicial de la app.

display

Forma de mostrar la app. El término standalone significa que no se muestra la barra de navegación del navegador web.

theme_color

Color de la barra de estado (en dispositivos móviles) o de título (en computadoras de escritorio) de la app.

background_color

Color de fondo de la pantalla desplah en dispositivos móviles.

description

Describe el propósito de la aplicación. Aparece en el cuadro de diálogo que muestra el navegador al instalar la app.

screenshots

Listado de máximo 8 capturas de pantalla. Aparecen en el cuadro de diálogo que muestra el navegador al instalar la app. Debes incluir al menos una con "form_factor": "wide" y otra con "form_factor": "narrow".

icons

Listado de íconos en distintas resoluciones para los instaladores de la app. Se selecciona el que se vea mejor según las característocas del dispositivo.

src

Url de la imagen dentro de la app.

sizes

Dimensiones en pixeles de la imagen, anchoxalto.

type

Tipo mime de la imagen.

purpose

Forma en que se usa la imagen.

maskable

La imagen puede recortarse de forma segura para tomar distintas formas, como círculos, gotas, cuadrados con esquinas redondeadas, etc. Normalmente se usa para dispositivos móviles.

any

No se puede asegurar nada sobre la imagen. Normalmente se usa para dispositivos de escritorio.

Normalmente debe proporcionarse un juego de íconos con purpose any y otro juego de íconos con purpose maskable.

form_factor

Orientación de una screenshot.

wide

La screenshot tiene una orientación horizontal. Normalmente la creenshot se usa para dispositivos de escritorio.

narrow

La screenshot tiene una orientación vertical. Normalmente la creenshot se usa para dispositivos móviles.

Debes incluir al menos una screenshot con "form_factor": "wide" y otra con "form_factor": "narrow".

label

Descripción de una screenshot. Aparece en el cuadro de diálogo que muestra el navegador al instalar la app.

1
{
2
 "short_name": "PWA",
3
 "name": "Ejemplo de PWA",
4
 "id": "/index.html",
5
 "start_url": "/index.html",
6
 "display": "standalone",
7
 "theme_color": "#cbc693",
8
 "background_color": "#ffffff",
9
 "description": "Ejemplos básico de PWA.",
10
 "screenshots": [
11
  {
12
   "src": "/img/screenshot_horizontal.png",
13
   "sizes": "1507x777",
14
   "type": "image/png",
15
   "form_factor": "wide",
16
   "label": "PWA Básica"
17
  },
18
  {
19
   "src": "/img/screenshot_vertical.png",
20
   "sizes": "591x980",
21
   "type": "image/png",
22
   "form_factor": "narrow",
23
   "label": "PWA Básica (2)"
24
  }
25
 ],
26
 "icons": [
27
  {
28
   "src": "/img/maskable_icon_x48.png",
29
   "sizes": "48x48",
30
   "type": "image/png",
31
   "purpose": "any"
32
  },
33
  {
34
   "src": "/img/maskable_icon_x72.png",
35
   "sizes": "72x72",
36
   "type": "image/png",
37
   "purpose": "any"
38
  },
39
  {
40
   "src": "/img/maskable_icon_x96.png",
41
   "sizes": "96x96",
42
   "type": "image/png",
43
   "purpose": "any"
44
  },
45
  {
46
   "src": "/img/maskable_icon_x128.png",
47
   "sizes": "128x128",
48
   "type": "image/png",
49
   "purpose": "any"
50
  },
51
  {
52
   "src": "/img/maskable_icon_x192.png",
53
   "sizes": "192x192",
54
   "type": "image/png",
55
   "purpose": "any"
56
  },
57
  {
58
   "src": "/img/maskable_icon_x384.png",
59
   "sizes": "384x384",
60
   "type": "image/png",
61
   "purpose": "any"
62
  },
63
  {
64
   "src": "/img/maskable_icon_x512.png",
65
   "sizes": "512x512",
66
   "type": "image/png",
67
   "purpose": "any"
68
  },
69
  {
70
   "src": "/img/maskable_icon.png",
71
   "sizes": "2730x2730",
72
   "type": "image/png",
73
   "purpose": "any"
74
  },
75
  {
76
   "src": "/img/icono2048.png",
77
   "sizes": "2048x2048",
78
   "type": "image/png",
79
   "purpose": "any"
80
  },
81
  {
82
   "src": "/img/maskable_icon_x48.png",
83
   "sizes": "48x48",
84
   "type": "image/png",
85
   "purpose": "maskable"
86
  },
87
  {
88
   "src": "/img/maskable_icon_x72.png",
89
   "sizes": "72x72",
90
   "type": "image/png",
91
   "purpose": "maskable"
92
  },
93
  {
94
   "src": "/img/maskable_icon_x96.png",
95
   "sizes": "96x96",
96
   "type": "image/png",
97
   "purpose": "maskable"
98
  },
99
  {
100
   "src": "/img/maskable_icon_x128.png",
101
   "sizes": "128x128",
102
   "type": "image/png",
103
   "purpose": "maskable"
104
  },
105
  {
106
   "src": "/img/maskable_icon_x192.png",
107
   "sizes": "192x192",
108
   "type": "image/png",
109
   "purpose": "maskable"
110
  },
111
  {
112
   "src": "/img/maskable_icon_x384.png",
113
   "sizes": "384x384",
114
   "type": "image/png",
115
   "purpose": "maskable"
116
  },
117
  {
118
   "src": "/img/maskable_icon_x512.png",
119
   "sizes": "512x512",
120
   "type": "image/png",
121
   "purpose": "maskable"
122
  },
123
  {
124
   "src": "/img/maskable_icon.png",
125
   "sizes": "2730x2730",
126
   "type": "image/png",
127
   "purpose": "maskable"
128
  }
129
 ]
130
}

F. public / sw.js

1
/* Este archivo debe estar colocado en la carpeta raíz del sitio.
2
 * 
3
 * Cualquier cambio en el contenido de este archivo hace que el service
4
 * worker se reinstale. */
5
6
/**
7
 * Cambia el número de la versión cuando cambia el contenido de los
8
 * archivos.
9
 * 
10
 * El número a la izquierda del punto (.), en este caso <q>1</q>, se
11
 * conoce como número mayor y se cambia cuando se realizan
12
 * modificaciones grandes o importantes.
13
 * 
14
 * El número a la derecha del punto (.), en este caso <q>00</q>, se
15
 * conoce como número menor y se cambia cuando se realizan
16
 * modificaciones menores.
17
 */
18
const VERSION = "1.00"
19
20
/** Nombre del archivo de cache. */
21
const CACHE = "ejemploPWA"
22
23
/**
24
 * Archivos requeridos para que la aplicación funcione fuera de
25
 * línea.
26
 */
27
const ARCHIVOS = [
28
 "favicon.ico",
29
 "index.html",
30
 "site.webmanifest",
31
 "css/estilos.css",
32
 "img/icono2048.png",
33
 "img/maskable_icon.png",
34
 "img/maskable_icon_x128.png",
35
 "img/maskable_icon_x192.png",
36
 "img/maskable_icon_x384.png",
37
 "img/maskable_icon_x48.png",
38
 "img/maskable_icon_x512.png",
39
 "img/maskable_icon_x72.png",
40
 "img/maskable_icon_x96.png",
41
 "img/screenshot_horizontal.png",
42
 "img/screenshot_vertical.png",
43
 "js/registraServiceWorker.js",
44
 "/"
45
]
46
47
// Verifica si el código corre dentro de un service worker.
48
if (self instanceof ServiceWorkerGlobalScope) {
49
 // Evento al empezar a instalar el servide worker,
50
 self.addEventListener("install",
51
  (/** @type {ExtendableEvent} */ evt) => {
52
   console.log("El service worker se está instalando.")
53
   evt.waitUntil(llenaElCache())
54
  })
55
56
 // Evento al solicitar información a la red.
57
 self.addEventListener("fetch", (/** @type {FetchEvent} */ evt) => {
58
  if (evt.request.method === "GET") {
59
   evt.respondWith(buscaLaRespuestaEnElCache(evt))
60
  }
61
 })
62
63
 // Evento cuando el service worker se vuelve activo.
64
 self.addEventListener("activate",
65
  () => console.log("El service worker está activo."))
66
}
67
68
async function llenaElCache() {
69
 console.log("Intentando cargar caché:", CACHE)
70
 // Borra todos los cachés.
71
 const keys = await caches.keys()
72
 for (const key of keys) {
73
  await caches.delete(key)
74
 }
75
 // Abre el caché de este service worker.
76
 const cache = await caches.open(CACHE)
77
 // Carga el listado de ARCHIVOS.
78
 await cache.addAll(ARCHIVOS)
79
 console.log("Cache cargado:", CACHE)
80
 console.log("Versión:", VERSION)
81
}
82
83
/** @param {FetchEvent} evt */
84
async function buscaLaRespuestaEnElCache(evt) {
85
 // Abre el caché.
86
 const cache = await caches.open(CACHE)
87
 const request = evt.request
88
 /* Busca la respuesta a la solicitud en el contenido del caché, sin
89
  * tomar en cuenta la parte después del símbolo "?" en la URL. */
90
 const response = await cache.match(request, { ignoreSearch: true })
91
 if (response === undefined) {
92
  /* Si no la encuentra, empieza a descargar de la red y devuelve
93
   * la promesa. */
94
  return fetch(request)
95
 } else {
96
  // Si la encuentra, devuelve la respuesta encontrada en el caché.
97
  return response
98
 }
99
}

G. Carpeta « public / css »

1. public / css / estilos.css

1
html {
2
 color-scheme: light dark;
3
 font-family: sans-serif;
4
}

H. Carpeta « img »

A. img / icono2048.png

icono2048.png

B. img / maskable_icon.png

maskable_icon.png

C. img / maskable_icon_x128.png

maskable_icon_x128.png

D. img / maskable_icon_x192.png

maskable_icon_x192.png

E. img / maskable_icon_x384.png

maskable_icon_x384.png

F. img / maskable_icon_x48.png

maskable_icon_x48.png

G. img / maskable_icon_x512.png

maskable_icon_x512.png

H. img / maskable_icon_x72.png

maskable_icon_x72.png

I. img / maskable_icon_x96.png

maskable_icon_x96.png

J. img / screenshot_horizontal.png

screenshot_horizontal.png

K. img / screenshot_vertical.png

screenshot_vertical.png

I. Carpeta « public / js »

1. public / js / registraServiceWorker.js

1
const nombreDeServiceWorker = "sw.js"
2
3
try {
4
 navigator.serviceWorker.register(nombreDeServiceWorker)
5
  .then(registro => {
6
   console.log(nombreDeServiceWorker, "registrado.")
7
   console.log(registro)
8
  })
9
  .catch(error => console.log(error))
10
} catch (error) {
11
 console.log(error)
12
}

E. .firebaserc

1
{
2
  "projects": {
3
    "default": "pwabasw"
4
  }
5
}
6

F. firebase.json

1
{
2
  "hosting": {
3
    "public": "public",
4
    "headers": [
5
      {
6
        "source": "**",
7
        "headers": [
8
          {
9
            "key": "Cache-Control",
10
            "value": "no-cache, no-store, must-revalidate"
11
          },
12
          {
13
            "key": "Pragma",
14
            "value": "no-cache"
15
          },
16
          {
17
            "key": "Expires",
18
            "value": "0"
19
          }
20
        ]
21
      }
22
    ],
23
    "ignore": [
24
      "firebase.json",
25
      "**/.*",
26
      "**/node_modules/**"
27
    ]
28
  },
29
  "emulators": {
30
    "hosting": {
31
      "port": 5000
32
    },
33
    "ui": {
34
      "enabled": true
35
    },
36
    "singleProjectMode": true
37
  }
38
}
39

G. instruccionesListadoSw.txt

1
Generar el listado de archivos del sw.js desde Visual Studio Code.
2
1. Abrir una terminal desde el menú 
3
    Terminal > New Terminal
4
5
2. Desde la terminal introducir la orden:
6
    Get-ChildItem -path public -Recurse | Select Directory,Name | Out-File archivos.txt
7
8
3. Abrir el archivo generado, que se llama
9
    archivos.txt
10
   y sobre este, realizar los pasos que siguen:
11
12
4. Quita del archivo archivos.txt:
13
    * el encabezado,
14
    * todas las carpetas,
15
    * todos los archivos dentro de .vscode como:
16
       * el archivo .vscode/settings.json,
17
       * el archivo .vscode/launch.json,
18
    * el archivo .htaccess,
19
    * el archivo archivos.txt,
20
    * este archivo (instruccionesListadoSw.txt),
21
    * el archivo jsconfig.json,
22
    * el archivo sw.js,
23
    * el archivo de la base de datos, que termina en ".db" y
24
      está en la carpeta php,
25
    * todos los archivos de php y
26
    * las líneas en blanco del final
27
28
5. Cambia los \ por / desde Visual Studio Code con las siguientes
29
   combinaciones de teclas:
30
31
    Ctrl+H En el diálogo que aparece introduce lo siguiente:
32
    Find:\
33
    Replace:/
34
35
    Clic en el icono Reemplaza todo o Replace All y luego teclea ESC
36
 
37
6. Coloca las comillas y coma del final de cada línea desde Visual
38
   Studio Code con las siguientes combinaciones de teclas:
39
40
    Ctrl+H En el diálogo que aparece, selecciona el botón
41
            ".*"
42
           e introduce lo siguiente:
43
    Find:\s*$
44
    Replace:",
45
46
    Clic en el icono Reemplaza todo o Replace All y luego teclea ESC
47
48
7. Marca la carpeta inicial, presiona la combinación de teclas:
49
50
    Shift+Ctrl+L
51
52
    borra la selección, teclea " y luego ESC
53
54
8. Cambia las secuencias de espacios por / con las siguientes
55
   combinaciones de teclas:
56
57
    Ctrl+H En el diálogo que aparece, selecciona el botón
58
            ".*"
59
           e introduce lo siguiente:
60
    Find:\s+
61
    Replace:/
62
63
    Clic en el icono Reemplaza todo o Replace All y luego teclea ESC
64
65
9. Cambia las "/ por " con las siguientes combinaciones de teclas:
66
67
    Ctrl+H En el diálogo que aparece, quita la selección del botón
68
            ".*"
69
           e introduce lo siguiente:
70
    Find:"/
71
    Replace:"
72
73
    Clic en el icono Reemplaza todo o Replace All y luego teclea ESC
74
75
10. Copia el texto al archivo
76
     public/sw.js
77
    en el contenido del arreglo llamado ARCHIVOS, pero recuerda
78
    mantener el último elemento, que dice:
79
     "/"

H. archivos.txt

1
"favicon.ico",
2
"index.html",
3
"site.webmanifest",
4
"css/estilos.css",
5
"img/icono2048.png",
6
"img/maskable_icon.png",
7
"img/maskable_icon_x128.png",
8
"img/maskable_icon_x192.png",
9
"img/maskable_icon_x384.png",
10
"img/maskable_icon_x48.png",
11
"img/maskable_icon_x512.png",
12
"img/maskable_icon_x72.png",
13
"img/maskable_icon_x96.png",
14
"img/screenshot_horizontal.png",
15
"img/screenshot_vertical.png",
16
"js/registraServiceWorker.js",

I. .gitignore

1
# Logs
2
logs
3
*.log
4
npm-debug.log*
5
yarn-debug.log*
6
yarn-error.log*
7
firebase-debug.log*
8
firebase-debug.*.log*
9
10
# Firebase cache
11
.firebase/
12
13
# Firebase config
14
15
# Uncomment this if you'd like others to create their own Firebase project.
16
# For a team working on the same Firebase project(s), it is recommended to leave
17
# it commented so all members can deploy to the same project(s) in .firebaserc.
18
# .firebaserc
19
20
# Runtime data
21
pids
22
*.pid
23
*.seed
24
*.pid.lock
25
26
# Directory for instrumented libs generated by jscoverage/JSCover
27
lib-cov
28
29
# Coverage directory used by tools like istanbul
30
coverage
31
32
# nyc test coverage
33
.nyc_output
34
35
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
36
.grunt
37
38
# Bower dependency directory (https://bower.io/)
39
bower_components
40
41
# node-waf configuration
42
.lock-wscript
43
44
# Compiled binary addons (http://nodejs.org/api/addons.html)
45
build/Release
46
47
# Dependency directories
48
node_modules/
49
50
# Optional npm cache directory
51
.npm
52
53
# Optional eslint cache
54
.eslintcache
55
56
# Optional REPL history
57
.node_repl_history
58
59
# Output of 'npm pack'
60
*.tgz
61
62
# Yarn Integrity file
63
.yarn-integrity
64
65
# dotenv environment variables file
66
.env
67
68
# dataconnect generated files
69
.dataconnect
70

J. jsconfig.json

1
{
2
 "compilerOptions": {
3
  "checkJs": true,
4
  "strictNullChecks": true,
5
  "target": "ES6",
6
  "module": "Node16",
7
  "moduleResolution": "Node16",
8
  "lib": [
9
   "ES2017",
10
   "WebWorker",
11
   "DOM"
12
  ]
18
}

K. LICENSE

1
Creative Commons Legal Code
2
3
CC0 1.0 Universal
4
5
    CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
6
    LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
7
    ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
8
    INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
9
    REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
10
    PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
11
    THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
12
    HEREUNDER.
13
14
Statement of Purpose
15
16
The laws of most jurisdictions throughout the world automatically confer
17
exclusive Copyright and Related Rights (defined below) upon the creator
18
and subsequent owner(s) (each and all, an "owner") of an original work of
19
authorship and/or a database (each, a "Work").
20
21
Certain owners wish to permanently relinquish those rights to a Work for
22
the purpose of contributing to a commons of creative, cultural and
23
scientific works ("Commons") that the public can reliably and without fear
24
of later claims of infringement build upon, modify, incorporate in other
25
works, reuse and redistribute as freely as possible in any form whatsoever
26
and for any purposes, including without limitation commercial purposes.
27
These owners may contribute to the Commons to promote the ideal of a free
28
culture and the further production of creative, cultural and scientific
29
works, or to gain reputation or greater distribution for their Work in
30
part through the use and efforts of others.
31
32
For these and/or other purposes and motivations, and without any
33
expectation of additional consideration or compensation, the person
34
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
35
is an owner of Copyright and Related Rights in the Work, voluntarily
36
elects to apply CC0 to the Work and publicly distribute the Work under its
37
terms, with knowledge of his or her Copyright and Related Rights in the
38
Work and the meaning and intended legal effect of CC0 on those rights.
39
40
1. Copyright and Related Rights. A Work made available under CC0 may be
41
protected by copyright and related or neighboring rights ("Copyright and
42
Related Rights"). Copyright and Related Rights include, but are not
43
limited to, the following:
44
45
  i. the right to reproduce, adapt, distribute, perform, display,
46
     communicate, and translate a Work;
47
 ii. moral rights retained by the original author(s) and/or performer(s);
48
iii. publicity and privacy rights pertaining to a person's image or
49
     likeness depicted in a Work;
50
 iv. rights protecting against unfair competition in regards to a Work,
51
     subject to the limitations in paragraph 4(a), below;
52
  v. rights protecting the extraction, dissemination, use and reuse of data
53
     in a Work;
54
 vi. database rights (such as those arising under Directive 96/9/EC of the
55
     European Parliament and of the Council of 11 March 1996 on the legal
56
     protection of databases, and under any national implementation
57
     thereof, including any amended or successor version of such
58
     directive); and
59
vii. other similar, equivalent or corresponding rights throughout the
60
     world based on applicable law or treaty, and any national
61
     implementations thereof.
62
63
2. Waiver. To the greatest extent permitted by, but not in contravention
64
of, applicable law, Affirmer hereby overtly, fully, permanently,
65
irrevocably and unconditionally waives, abandons, and surrenders all of
66
Affirmer's Copyright and Related Rights and associated claims and causes
67
of action, whether now known or unknown (including existing as well as
68
future claims and causes of action), in the Work (i) in all territories
69
worldwide, (ii) for the maximum duration provided by applicable law or
70
treaty (including future time extensions), (iii) in any current or future
71
medium and for any number of copies, and (iv) for any purpose whatsoever,
72
including without limitation commercial, advertising or promotional
73
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
74
member of the public at large and to the detriment of Affirmer's heirs and
75
successors, fully intending that such Waiver shall not be subject to
76
revocation, rescission, cancellation, termination, or any other legal or
77
equitable action to disrupt the quiet enjoyment of the Work by the public
78
as contemplated by Affirmer's express Statement of Purpose.
79
80
3. Public License Fallback. Should any part of the Waiver for any reason
81
be judged legally invalid or ineffective under applicable law, then the
82
Waiver shall be preserved to the maximum extent permitted taking into
83
account Affirmer's express Statement of Purpose. In addition, to the
84
extent the Waiver is so judged Affirmer hereby grants to each affected
85
person a royalty-free, non transferable, non sublicensable, non exclusive,
86
irrevocable and unconditional license to exercise Affirmer's Copyright and
87
Related Rights in the Work (i) in all territories worldwide, (ii) for the
88
maximum duration provided by applicable law or treaty (including future
89
time extensions), (iii) in any current or future medium and for any number
90
of copies, and (iv) for any purpose whatsoever, including without
91
limitation commercial, advertising or promotional purposes (the
92
"License"). The License shall be deemed effective as of the date CC0 was
93
applied by Affirmer to the Work. Should any part of the License for any
94
reason be judged legally invalid or ineffective under applicable law, such
95
partial invalidity or ineffectiveness shall not invalidate the remainder
96
of the License, and in such case Affirmer hereby affirms that he or she
97
will not (i) exercise any of his or her remaining Copyright and Related
98
Rights in the Work or (ii) assert any associated claims and causes of
99
action with respect to the Work, in either case contrary to Affirmer's
100
express Statement of Purpose.
101
102
4. Limitations and Disclaimers.
103
104
 a. No trademark or patent rights held by Affirmer are waived, abandoned,
105
    surrendered, licensed or otherwise affected by this document.
106
 b. Affirmer offers the Work as-is and makes no representations or
107
    warranties of any kind concerning the Work, express, implied,
108
    statutory or otherwise, including without limitation warranties of
109
    title, merchantability, fitness for a particular purpose, non
110
    infringement, or the absence of latent or other defects, accuracy, or
111
    the present or absence of errors, whether or not discoverable, all to
112
    the greatest extent permissible under applicable law.
113
 c. Affirmer disclaims responsibility for clearing rights of other persons
114
    that may apply to the Work or any use thereof, including without
115
    limitation any person's Copyright and Related Rights in the Work.
116
    Further, Affirmer disclaims responsibility for obtaining any necessary
117
    consents, permissions or other rights required for any use of the
118
    Work.
119
 d. Affirmer understands and acknowledges that Creative Commons is not a
120
    party to this document and has no duty or obligation with respect to
121
    this CC0 or use of the Work.
122

L. README.md

1
# pwabasw
2
Ejwmplo de pwa
3

M. Resumen