Skip to content

Commit

Permalink
feat(service-worker): add service worker utilities and logging support (
Browse files Browse the repository at this point in the history
#18)

* feat(service-worker): add service worker utilities and logging support

* feat(service-worker): refactor imports and add version-checker utility

* refactor(service-worker): enhance PWA installation handling with type definitions and improved comments

* feat(service-worker): add beforeinstallprompt option and integrate snackbar package

* refactor(snackbar): optimize logger and signal instantiation with pure annotations

* feat(snackbar): allow 'infinite' duration option for snackbar display

* doc(snackbar): update documentation to clarify 'infinite' duration option

* refactor(service-worker): update snackbar duration to 'infinite' for service worker notifications

* refactor(service-worker): improve AlwatrSignal instantiation with pure annotations

* refactor(service-worker): update version and repository directory in package.json

* feat(service-worker): add an empty test file for initial setup

* refactor(snackbar): simplify default duration assignment in showSnackbar function

* doc(service-worker): remove unnecessary comment in main.ts

* refactor(service-worker): remove install-pwa.ts file and related functionality

* refactor(logger): simplify logger initialization by removing package tracer and package details

* refactor(service-worker): remove serviceWorkerNotifyHandler and related functionality

* refactor(version-checker): remove isVersionLarger function and related logger usage

* refactor(service-worker): remove unused exports from main.ts

* refactor(service-worker): clean up package.json and yarn.lock by removing unused dependencies

* doc(service-worker): write installation

* chore: set correct owner homepage

* refactor(service-worker): review and enhance

Co-authored-by: arashagp <arash.qardashpoor@gmail.com>

* lint: make happy

---------

Co-authored-by: S. Amir Mohammad Najafi <njfamirm@gmail.com>
  • Loading branch information
arashagp and njfamirm authored Dec 17, 2024
1 parent 2f8d49f commit d428086
Show file tree
Hide file tree
Showing 17 changed files with 945 additions and 15 deletions.
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
"argb",
"backorder",
"backordering",
"beforeinstallprompt",
"colspan",
"cssnano",
"endmacro",
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"description": "Necessary library for all ECMAScript (JavaScript/TypeScript) projects.",
"repository": "https://github.com/the-nexim/nanolib",
"license": "AGPL-3.0-only",
"author": "S. Amir Mohammad Najafi <njfamirm@gmail.com> (www.njfamirm.ir)",
"author": "S. Amir Mohammad Najafi <njfamirm@gmail.com> (https://www.njfamirm.ir)",
"contributors": [
"Arash Ghardashpoor <arash.qardashpoor@gmail.com> (https://www.agpagp.ir)"
],
Expand Down
2 changes: 1 addition & 1 deletion packages/alpine/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"directory": "packages/alpine"
},
"license": "AGPL-3.0-only",
"author": "S. Amir Mohammad Najafi <njfamirm@gmail.com> (www.njfamirm.ir)",
"author": "S. Amir Mohammad Najafi <njfamirm@gmail.com> (https://www.njfamirm.ir)",
"contributors": [
"Arash Ghardashpoor <arash.qardashpoor@gmail.com> (https://www.agpagp.ir)"
],
Expand Down
2 changes: 1 addition & 1 deletion packages/element/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"directory": "packages/element"
},
"license": "AGPL-3.0-only",
"author": "S. Amir Mohammad Najafi <njfamirm@gmail.com> (www.njfamirm.ir)",
"author": "S. Amir Mohammad Najafi <njfamirm@gmail.com> (https://www.njfamirm.ir)",
"contributors": [
"Arash Ghardashpoor <arash.qardashpoor@gmail.com> (https://www.agpagp.ir)"
],
Expand Down
2 changes: 1 addition & 1 deletion packages/eslint-config/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
"directory": "packages/eslint-config"
},
"license": "AGPL-3.0-only",
"author": "S. Amir Mohammad Najafi <njfamirm@gmail.com> (www.njfamirm.ir)",
"author": "S. Amir Mohammad Najafi <njfamirm@gmail.com> (https://www.njfamirm.ir)",
"contributors": [
"Arash Ghardashpoor <arash.qardashpoor@gmail.com> (https://www.agpagp.ir)"
],
Expand Down
2 changes: 1 addition & 1 deletion packages/prettier-config/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"directory": "packages/prettier-config"
},
"license": "AGPL-3.0-only",
"author": "S. Amir Mohammad Najafi <njfamirm@gmail.com> (www.njfamirm.ir)",
"author": "S. Amir Mohammad Najafi <njfamirm@gmail.com> (https://www.njfamirm.ir)",
"contributors": [
"Arash Ghardashpoor <arash.qardashpoor@gmail.com> (https://www.agpagp.ir)"
],
Expand Down
661 changes: 661 additions & 0 deletions packages/service-worker/LICENSE

Large diffs are not rendered by default.

51 changes: 51 additions & 0 deletions packages/service-worker/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# @nexim/service-worker

![NPM Version](https://img.shields.io/npm/v/%40nexim%2Fservice-worker)
![npm bundle size](https://img.shields.io/bundlephobia/min/%40nexim%2Fservice-worker)
![Build & Lint & Test](https://github.com/the-nexim/nanolib/actions/workflows/build-lint-test.yaml/badge.svg)
![NPM Downloads](https://img.shields.io/npm/dm/%40nexim%2Fservice-worker)
![NPM License](https://img.shields.io/npm/l/%40nexim%2Fservice-worker)

## Overview

Utilities to simplify the usage of service workers in your web applications.

## Installation

Install the package using npm or yarn:

```sh
npm install @nexim/service-worker

# Or using yarn
yarn add @nexim/service-worker
```

## Api

### registerServiceWorker

Register the service worker and handle updates.

```ts
import {registerServiceWorker} from '@nexim/service-worker';

const serviceWorkerPath = '/service-worker.js';
registerServiceWorker(serviceWorkerPath);
```

### serviceWorkerSignal

Signal for service worker events.

```ts
import {serviceWorkerSignal} from '@nexim/service-worker';

serviceWorkerSignal.subscribe(({event}) => {
console.log('Service worker event:', event);
});
```

### Type ServiceWorkerEvent

The events that can be emitted by the service worker.
73 changes: 73 additions & 0 deletions packages/service-worker/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
{
"name": "@nexim/service-worker",
"version": "0.0.0",
"description": "Utilities to simplify the usage of service workers in your web applications.",
"keywords": [
"worker",
"service-worker",
"typescript",
"nexim"
],
"homepage": "https://github.com/the-nexim/nanolib/tree/next/packages/service-worker#readme",
"bugs": {
"url": "https://github.com/the-nexim/nanolib/issues"
},
"repository": {
"type": "git",
"url": "https://github.com/the-nexim/nanolib",
"directory": "packages/service-worker"
},
"license": "AGPL-3.0-only",
"author": "S. Amir Mohammad Najafi <njfamirm@gmail.com> (https://www.njfamirm.ir)",
"contributors": [
"Arash Ghardashpoor <arash.qardashpoor@gmail.com> (https://www.agpagp.ir)"
],
"type": "module",
"exports": {
".": {
"types": "./dist/main.d.ts",
"import": "./dist/main.mjs",
"require": "./dist/main.cjs"
}
},
"main": "./dist/main.cjs",
"module": "./dist/main.mjs",
"types": "./dist/main.d.ts",
"files": [
"**/*.{js,mjs,cjs,map,d.ts,html,md,LEGAL.txt}",
"LICENSE",
"!**/*.test.js",
"!demo/**/*"
],
"scripts": {
"b": "yarn run build",
"build": "yarn run build:ts && yarn run build:es",
"build:es": "nano-build --preset=module",
"build:ts": "tsc --build",
"c": "yarn run clean",
"cb": "yarn run clean && yarn run build",
"clean": "rm -rfv dist *.tsbuildinfo",
"d": "yarn run build:es && yarn node --enable-source-maps --trace-warnings",
"t": "yarn run test",
"test": "NODE_OPTIONS=\"$NODE_OPTIONS --enable-source-maps --experimental-vm-modules\" ava",
"w": "yarn run watch",
"watch": "yarn run watch:ts & yarn run watch:es",
"watch:es": "yarn run build:es --watch",
"watch:ts": "yarn run build:ts --watch --preserveWatchOutput"
},
"dependencies": {
"@alwatr/flux": "^4.0.2",
"@alwatr/logger": "^5.0.0",
"@alwatr/package-tracer": "^5.0.0"
},
"devDependencies": {
"@alwatr/nano-build": "^5.0.0",
"@alwatr/type-helper": "^5.0.0",
"@nexim/typescript-config": "workspace:^",
"ava": "^6.2.0",
"typescript": "^5.6.3"
},
"publishConfig": {
"access": "public"
}
}
6 changes: 6 additions & 0 deletions packages/service-worker/src/main.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import test from 'ava';

// empty test
test('empty test', (test) => {
test.pass();
});
106 changes: 106 additions & 0 deletions packages/service-worker/src/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import {AlwatrSignal} from '@alwatr/flux';
import {createLogger} from '@alwatr/logger';
import {packageTracer} from '@alwatr/package-tracer';

/**
* The events that can be emitted by the service worker.
*/
export type ServiceWorkerEvent =
| 'service_worker_registered'
| 'service_worker_register_failed'
| 'service_worker_first_install'
| 'service_worker_updated'
| 'service_worker_installed'
| 'service_worker_update_found';

__dev_mode__: packageTracer.add(__package_name__, __package_version__);

const logger = /* @__PURE__ */ createLogger(__package_name__);

/**
* Signal for service worker events.
*
* @example
* import {serviceWorkerSignal} from '@nexim/service-worker';
*
* serviceWorkerSignal.subscribe(({event}) => {
* console.log('Service worker event:', event);
* });
*/
export const serviceWorkerSignal = /* @__PURE__ */ new AlwatrSignal<{event: ServiceWorkerEvent}>({
name: 'serviceWorker',
});

/**
* Register the service worker and handle updates.
*
* @param serviceWorkerPath The path to the service worker.
*
* @example
* import {registerServiceWorker} from '@nexim/service-worker';
*
* const serviceWorkerPath = '/service-worker.js';
* registerServiceWorker(serviceWorkerPath);
*/
export async function registerServiceWorker(serviceWorkerPath: string): Promise<void> {
logger.logMethodArgs?.('registerServiceWorker', {serviceWorkerPath});

try {
const swRegistration = await navigator.serviceWorker.register(serviceWorkerPath);
serviceWorkerSignal.notify({event: 'service_worker_registered'});
swRegistration.addEventListener('updatefound', () => serviceWorkerUpdateFoundHandler(swRegistration.installing));
logger.logOther?.('Service worker registered.');
}
catch (error) {
logger.error('registerServiceWorker', 'registration_failed ', {error});
serviceWorkerSignal.notify({event: 'service_worker_register_failed'});
}
}

/**
* Handle the 'updatefound' event
*
* @param serviceWorker The service worker
*/
function serviceWorkerUpdateFoundHandler(serviceWorker: ServiceWorker | null): void {
if (serviceWorker == null) return;
logger.logMethod?.('swUpdateFound');

// Only notify update found if there's an existing controller
if (navigator.serviceWorker.controller) {
serviceWorkerSignal.notify({event: 'service_worker_update_found'});
}
else {
serviceWorkerSignal.notify({event: 'service_worker_first_install'});
}

serviceWorker.addEventListener('statechange', () => serviceWorkerStateChangeHandler(serviceWorker));
}

/**
* Handle the 'statechange' event.
*
* If the service worker state is 'installed' then it is either a new install or an update.
* If the service worker state is 'redundant' then the service worker is redundant.
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorker/state
*
* @param serviceWorker The service worker.
*/
function serviceWorkerStateChangeHandler(serviceWorker: ServiceWorker): void {
logger.logMethodArgs?.('serviceWorkerStateChangeHandler', {state: serviceWorker.state});

if (serviceWorker.state === 'installed') {
// if old controller available then its update else its new install
if (navigator.serviceWorker.controller) {
serviceWorkerSignal.notify({event: 'service_worker_updated'});
}
else {
serviceWorkerSignal.notify({event: 'service_worker_installed'});
}
}
else if (serviceWorker.state === 'redundant') {
logger.accident('serviceWorkerStateChangeHandler', 'sw_redundant', 'Service worker redundant');
serviceWorkerSignal.notify({event: 'service_worker_installed'});
}
}
12 changes: 12 additions & 0 deletions packages/service-worker/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"extends": "@nexim/typescript-config/tsconfig.json",
"compilerOptions": {
"rootDir": "src",
"outDir": "dist",
"emitDeclarationOnly": true,
"composite": true,
"types": ["@alwatr/nano-build", "@alwatr/type-helper"]
},
"include": ["src/**/*.ts"],
"references": []
}
2 changes: 1 addition & 1 deletion packages/snackbar/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"directory": "packages/snackbar"
},
"license": "AGPL-3.0-only",
"author": "S. Amir Mohammad Najafi <njfamirm@gmail.com> (www.njfamirm.ir)",
"author": "S. Amir Mohammad Najafi <njfamirm@gmail.com> (https://www.njfamirm.ir)",
"contributors": [
"Arash Ghardashpoor <arash.qardashpoor@gmail.com> (https://www.agpagp.ir)"
],
Expand Down
15 changes: 7 additions & 8 deletions packages/snackbar/src/lib/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@ import {waitForTimeout} from '@alwatr/wait';

import type {SnackbarComponent} from './element.js';

const logger = createLogger(`${__package_name__}/handler`);
const logger = /* @__PURE__ */ createLogger(`${__package_name__}/handler`);

/**
* @property content - Content to be displayed in the snackbar.
* @property {action} - The action button configuration.
* @property action.label - The label for the action button.
* @property action.handler - The handler function for the action button.
* @property duration - Duration for which the snackbar is displayed. `-1` for infinite duration.
* @property duration - Duration for which the snackbar is displayed. `infinite` for infinite duration.
* Duration for which the snackbar is displayed.
* `-1` for infinite duration.
* `infinite` for infinite duration.
* @property addCloseButton - Whether to add a close button to the snackbar.
*/
export type SnackbarOptions = {
Expand All @@ -23,7 +23,7 @@ export type SnackbarOptions = {
label: string;
handler: () => void;
};
duration?: Duration;
duration?: Duration | 'infinite';
addCloseButton?: boolean;
};

Expand Down Expand Up @@ -53,7 +53,7 @@ export const snackbarActionButtonClickedSignal = new AlwatrTrigger({
* addCloseButton: true,
* });
*/
export const snackbarSignal = new AlwatrSignal<SnackbarOptions>({name: 'snackbar'});
export const snackbarSignal = /* @__PURE__ */ new AlwatrSignal<SnackbarOptions>({name: 'snackbar'});

// Subscribe to the snackbar signal to show the snackbar when the signal is emitted.
snackbarSignal.subscribe((options) => {
Expand All @@ -71,10 +71,9 @@ async function showSnackbar(options: SnackbarOptions): Promise<void> {
logger.logMethodArgs?.('showSnackbar', {options});

// Parse the duration
if (options.duration != null) options.duration = parseDuration(options.duration);

// Set default duration if not provided
options.duration = parseDuration('4s');
options.duration ??= '4s';

const element = document.createElement('snack-bar') as SnackbarComponent;

Expand Down Expand Up @@ -111,7 +110,7 @@ async function showSnackbar(options: SnackbarOptions): Promise<void> {
document.body.appendChild(element);

// Set a timeout to close the snackbar if duration is not infinite
if (options.duration !== -1) {
if (options.duration !== 'infinite') {
waitForTimeout(parseDuration(options.duration)).then(closeSnackbar_);
}
}
2 changes: 1 addition & 1 deletion packages/typescript-config/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"directory": "packages/typescript-config"
},
"license": "AGPL-3.0-only",
"author": "S. Amir Mohammad Najafi <njfamirm@gmail.com> (www.njfamirm.ir)",
"author": "S. Amir Mohammad Najafi <njfamirm@gmail.com> (https://www.njfamirm.ir)",
"contributors": [
"Arash Ghardashpoor <arash.qardashpoor@gmail.com> (https://www.agpagp.ir)"
],
Expand Down
6 changes: 6 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@
},
{
"path": "./packages/alpine"
},
{
"path": "./packages/service-worker"
},
{
"path": "./packages/snackbar"
}
]
}
Loading

0 comments on commit d428086

Please sign in to comment.