An open API service indexing awesome lists of open source software.

https://github.com/dakirchik/simplepwa

Modern framework for creating adaptive, reactive Progressive Web Applications (PWA) with offline support. Built on Web Components with easy IndexedDB integration, responsive routing, and a reactive store for offline-first applications.
https://github.com/dakirchik/simplepwa

adaptive cli indexeddb manifest offline offline-first progressive-web-app pwa reactive routing store web-components

Last synced: 5 months ago
JSON representation

Modern framework for creating adaptive, reactive Progressive Web Applications (PWA) with offline support. Built on Web Components with easy IndexedDB integration, responsive routing, and a reactive store for offline-first applications.

Awesome Lists containing this project

README

          

# πŸš€ SimplePWA Framework

# Description in English

**SimplePWA** β€” It is a modern framework for creating adaptive, reactive web applications with offline mode support ΠΈ PWA (Progressive Web Applications).
The framework is built on [Web Components](https://developer.mozilla.org/ru/docs/Web/Web_Components) and provides easy integration [IndexedDB](https://developer.mozilla.org/ru/docs/Web/API/IndexedDB_API) for `offline-first` applications.

---

## ✨ Main features

| Feature | Description |
|--------|----------|
| πŸ“± Responsive Routing | Automatic detection of device type and loading of appropriate components |
| πŸ’Ύ Offline Support | Data synchronization and operation without internet connection |
| πŸ”„ Reactive Store | Global state with subscriptions for changes |
| πŸ“¦ Web Components | Encapsulation of styles and logic in custom elements |
| πŸš€ PWA out of the box | Service Worker, manifest, and tools for creating PWA applications |
| πŸ› οΈ CLI Tools | Rapid creation of new PWA projects |

---

## πŸ“¦ Installation

```bash
npm install @dakir/simple-pwa
# or
yarn add @dakir/simple-pwa
```

---

## πŸ§ͺ Quick Start

```js
import { App } from '@dakir/simple-pwa';

// Create an instance of the application
const app = App.create({
rootElement: '#app',
pwa: {
enabled: true,
serviceWorkerPath: '/service-worker.js'
}
});

// Launch the application
app.start();
```

---

## 🧭 Routing

The framework automatically detects the device type (mobile / desktop) and loads the corresponding components:

```js
const routes = [
{
path: '/',
desktopComponent: DesktopHome,
mobileComponent: MobileHome
},
{
path: '/about',
desktopComponent: DesktopAbout,
mobileComponent: MobileAbout
}
];

const app = new App({ routes });
```

---

## 🧩 Components

Create responsive components by inheriting from the base class:

```js
import { Component } from '@dakir/simple-pwa';

class CustomComponent extends Component {
render() {
if (this.shadowRoot) {
this.shadowRoot.innerHTML = `

:host { display: block; padding: 20px; }
h2 { color: #2c3e50; }

My component


Counter: ${this.state.count || 0}


+
`;

this.shadowRoot.getElementById('increment')?.addEventListener('click', () => {
this.setState({ count: (this.state.count || 0) + 1 });
});
}
}
}

customElements.define('custom-component', CustomComponent);
```

---

## πŸ—„οΈ Working with Store

Global state with reactive updates:

```js
import { Store } from '@dakir/simple-pwa';

// Creating a global store
const appStore = new Store({
user: null,
theme: 'light',
counter: 0
});

// Updating state
appStore.setState({ theme: 'dark' });

// Subscribing to changes
const unsubscribe = appStore.subscribe(state => {
console.log('State updated:', state);
});

// Unsubscribe from updates
unsubscribe();
```

---

## πŸ”Œ Offline-First with IndexedDB

SimplePWA includes a powerful sync manager for working with data in offline mode:

```js
import { App } from '@dakir/simple-pwa';

const app = App.create({
syncDataManagerOptions: {
dbName: 'my-app-db'
}
});

// Retrieving data from IndexedDB
const items = await app.getDBList();

// Adding an item
await app.addDBItem({
id: '123',
type: 'task',
data: JSON.stringify({ title: 'Task', completed: false }),
lastModified: new Date().toISOString(),
lastSynced: new Date().toISOString()
});

// Synchronizing with the server when the connection is restored
app.setupAutoSync({
tasks: () => fetch('/api/tasks').then(res => res.json()),
users: () => fetch('/api/users').then(res => res.json())
}, {
onNetworkRestore: true,
interval: 5 * 60 * 1000 // Every 5 minutes
});
```

---

## πŸ› οΈ Creating PWA via CLI

```bash
npx create-pwa my-project
```

CLI will ask questions about the name, description, icons, and other parameters. Upon completion, you'll get a ready-to-use PWA project that you can launch immediately.

### πŸ§‘β€πŸ”§ Generating a Service Worker

```bash
npx create-sw
```

CLI will help set up caching strategies and handle offline states.

---

## πŸ“˜ API Reference

### `App`

The `App` class encapsulates routing, global state, synchronization, and PWA operations.

#### 🏁 Creating an Application

```js
import { App } from '@dakir/simple-pwa';

const app = App.create({
rootElement: '#app', // or HTMLElement
routes: [ /* ... */ ],
pwa: { enabled: true },
syncDataManagerOptions: { dbName: 'my-db' }
});
```

#### πŸ”§ Main Methods

| Method | Description |
|------|----------|
| `start()` | Start the application |
| `navigate(path)` | Navigate to a route |
| `getDeviceType()` | Get current device type (`mobile` / `desktop`) |
| `getState()` | Get store state object |
| `setState(patch)` | Update store state |
| `addEventListener()` | Add a global event handler |

---

### 🧲 Working with IndexedDB (SyncDataManager)

| Method | Description |
|------|----------|
| `getDBList(...)` | Retrieve a list of objects from IndexedDB |
| `getDBListByIndex(...)` | Retrieve a list by index |
| `getDBItem(id)` | Retrieve an object by ID |
| `getDBItemByIndex(...)` | Retrieve an object by a specific index |
| `addDBItem(obj)` | Add an object |
| `updateDBItem(obj)` | Update an object |
| `deleteDBItem(id)` | Delete an object by ID |
| `syncDBList(fetchMethod)` | Synchronize a list with the server |
| `syncDBItem(fetchMethod)` | Synchronize a single object with the server |
| `isSyncNeeded(lastSynced, [timeDelay])` | Check if re-synchronization is needed |
| `deleteDB([nameDB])` | Delete a database |
| `isOfflineMode()` | Check if the app is in offline mode |
| `isSyncing()` | Check if synchronization is currently in progress |
| `syncAllData(methods)` | Run synchronization of all collections (e.g., tasks and users simultaneously) |
| `setupAutoSync(methods, options)` | Configure auto-sync on network restoration, by timer, or at startup |
| `checkAndSync(id, method, [timeDelay])` | Check and synchronize data as needed |

#### Example usage of synchronization methods

```js
// Retrieve all tasks
const tasks = await app.getDBList();

// Add a new task
await app.addDBItem({ id: '1', type: 'task', ... });

// Synchronize tasks with the server
await app.syncDBList(() => fetch('/api/tasks').then(r => r.json()));

// Auto-sync when the network is restored
app.setupAutoSync({
tasks: () => fetch('/api/tasks').then(r => r.json())
});
```

---

### 🧠 Store

Reactive global store for storing application state.

```js
import { Store } from '@dakir/simple-pwa';

// Create a store
const store = new Store({ theme: 'light', counter: 0 });

// Subscribe to changes
const unsubscribe = store.subscribe(state => {
console.log('New state:', state);
});

// Update state
store.setState({ theme: 'dark' });

// Unsubscribe
unsubscribe();
```

---

### 🧱 Components (Web Components)

Base class `Component` and derived classes `DesktopComponent`, `MobileComponent`.

```js
import { Component } from '@dakir/simple-pwa';

class MyCounter extends Component {
render() {
if (this.shadowRoot) {
this.shadowRoot.innerHTML = `
Count: ${this.state.count || 0}
+
`;
this.shadowRoot.getElementById('inc')?.addEventListener('click', () => {
this.setState({ count: (this.state.count || 0) + 1 });
});
}
}
}

customElements.define('my-counter', MyCounter);
```

---

### 🧭 Adaptive Routing

Use mobile and desktop components for different devices:

```js
const routes = [
{
path: '/',
desktopComponent: DesktopHome,
mobileComponent: MobileHome
}
];
const app = new App({ routes });
```

---

## πŸ“œ License

MIT

---

## πŸ“¬ Feedback and Support

Found a bug or want to suggest an improvement?
Open an [issue](https://github.com/Dakirchik/simplePWA/issues) or [PR](https://github.com/Dakirchik/simplePWA/pulls)!

---

πŸš€ **SimplePWA β€” your fast path to production-ready PWA!**

# ОписаниС Π½Π° русском

**SimplePWA** β€” это соврСмСнный Ρ„Ρ€Π΅ΠΉΠΌΠ²ΠΎΡ€ΠΊ для создания Π°Π΄Π°ΠΏΡ‚ΠΈΠ²Π½Ρ‹Ρ…, Ρ€Π΅Π°ΠΊΡ‚ΠΈΠ²Π½Ρ‹Ρ… Π²Π΅Π±-ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠΉ с ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΊΠΎΠΉ ΠΎΡ„Ρ„Π»Π°ΠΉΠ½-Ρ€Π΅ΠΆΠΈΠΌΠ° ΠΈ PWA (Progressive Web Applications).
Π€Ρ€Π΅ΠΉΠΌΠ²ΠΎΡ€ΠΊ построСн Π½Π° [Web Components](https://developer.mozilla.org/ru/docs/Web/Web_Components) ΠΈ обСспСчиваСт ΠΏΡ€ΠΎΡΡ‚ΡƒΡŽ ΠΈΠ½Ρ‚Π΅Π³Ρ€Π°Ρ†ΠΈΡŽ [IndexedDB](https://developer.mozilla.org/ru/docs/Web/API/IndexedDB_API) для `offline-first` ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠΉ.

---

## ✨ ΠžΡΠ½ΠΎΠ²Π½Ρ‹Π΅ возмоТности

| Ѐункция | ОписаниС |
|--------|----------|
| πŸ“± Адаптивная ΠΌΠ°Ρ€ΡˆΡ€ΡƒΡ‚ΠΈΠ·Π°Ρ†ΠΈΡ | АвтоматичСскоС ΠΎΠΏΡ€Π΅Π΄Π΅Π»Π΅Π½ΠΈΠ΅ Ρ‚ΠΈΠΏΠ° устройства ΠΈ Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠ° ΡΠΎΠΎΡ‚Π²Π΅Ρ‚ΡΡ‚Π²ΡƒΡŽΡ‰ΠΈΡ… ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ΠΎΠ² |
| πŸ’Ύ ΠŸΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΊΠ° ΠΎΡ„Ρ„Π»Π°ΠΉΠ½-Ρ€Π΅ΠΆΠΈΠΌΠ° | Бинхронизация Π΄Π°Π½Π½Ρ‹Ρ… ΠΈ Ρ€Π°Π±ΠΎΡ‚Π° Π±Π΅Π· ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΡ ΠΊ ΠΈΠ½Ρ‚Π΅Ρ€Π½Π΅Ρ‚Ρƒ |
| πŸ”„ Π Π΅Π°ΠΊΡ‚ΠΈΠ²Π½Ρ‹ΠΉ стор | Π“Π»ΠΎΠ±Π°Π»ΡŒΠ½ΠΎΠ΅ состояниС с подписками Π½Π° измСнСния |
| πŸ“¦ Web Components | Π˜Π½ΠΊΠ°ΠΏΡΡƒΠ»ΡΡ†ΠΈΡ стилСй ΠΈ Π»ΠΎΠ³ΠΈΠΊΠΈ Π² ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΡΠΊΠΈΠ΅ элСмСнты |
| πŸš€ PWA ΠΈΠ· ΠΊΠΎΡ€ΠΎΠ±ΠΊΠΈ | Service Worker, манифСст ΠΈ инструмСнты для создания PWA |
| πŸ› οΈ CLI-инструмСнты | БыстроС созданиС Π½ΠΎΠ²Ρ‹Ρ… PWA ΠΏΡ€ΠΎΠ΅ΠΊΡ‚ΠΎΠ² |

---

## πŸ“¦ Установка

```bash
npm install @dakir/simple-pwa
# ΠΈΠ»ΠΈ
yarn add @dakir/simple-pwa
```

---

## πŸ§ͺ Быстрый старт

```js
import { App } from '@dakir/simple-pwa';

// Π‘ΠΎΠ·Π΄Π°ΠΉΡ‚Π΅ экзСмпляр прилоТСния
const app = App.create({
rootElement: '#app',
pwa: {
enabled: true,
serviceWorkerPath: '/service-worker.js'
}
});

// ЗапуститС ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅
app.start();
```

---

## 🧭 ΠœΠ°Ρ€ΡˆΡ€ΡƒΡ‚ΠΈΠ·Π°Ρ†ΠΈΡ

Π€Ρ€Π΅ΠΉΠΌΠ²ΠΎΡ€ΠΊ автоматичСски опрСдСляСт Ρ‚ΠΈΠΏ устройства (мобильноС / дСсктопноС) ΠΈ Π·Π°Π³Ρ€ΡƒΠΆΠ°Π΅Ρ‚ ΡΠΎΠΎΡ‚Π²Π΅Ρ‚ΡΡ‚Π²ΡƒΡŽΡ‰ΠΈΠ΅ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Ρ‹:

```js
const routes = [
{
path: '/',
desktopComponent: DesktopHome,
mobileComponent: MobileHome
},
{
path: '/about',
desktopComponent: DesktopAbout,
mobileComponent: MobileAbout
}
];

const app = new App({ routes });
```

---

## 🧩 ΠšΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Ρ‹

Π‘ΠΎΠ·Π΄Π°Π²Π°ΠΉΡ‚Π΅ Π°Π΄Π°ΠΏΡ‚ΠΈΠ²Π½Ρ‹Π΅ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Ρ‹, Π½Π°ΡΠ»Π΅Π΄ΡƒΡΡΡŒ ΠΎΡ‚ Π±Π°Π·ΠΎΠ²ΠΎΠ³ΠΎ класса:

```js
import { Component } from '@dakir/simple-pwa';

class CustomComponent extends Component {
render() {
if (this.shadowRoot) {
this.shadowRoot.innerHTML = `

:host { display: block; padding: 20px; }
h2 { color: #2c3e50; }

Мой ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚


Π‘Ρ‡Ρ‘Ρ‚Ρ‡ΠΈΠΊ: ${this.state.count || 0}


+
`;

this.shadowRoot.getElementById('increment')?.addEventListener('click', () => {
this.setState({ count: (this.state.count || 0) + 1 });
});
}
}
}

customElements.define('custom-component', CustomComponent);
```

---

## πŸ—„οΈ Π Π°Π±ΠΎΡ‚Π° с Ρ…Ρ€Π°Π½ΠΈΠ»ΠΈΡ‰Π΅ΠΌ (Store)

Π“Π»ΠΎΠ±Π°Π»ΡŒΠ½ΠΎΠ΅ состояниС с Ρ€Π΅Π°ΠΊΡ‚ΠΈΠ²Π½Ρ‹ΠΌΠΈ обновлСниями:

```js
import { Store } from '@dakir/simple-pwa';

// Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ глобального Ρ…Ρ€Π°Π½ΠΈΠ»ΠΈΡ‰Π°
const appStore = new Store({
user: null,
theme: 'light',
counter: 0
});

// ОбновлСниС состояния
appStore.setState({ theme: 'dark' });

// Подписка на измСнСния
const unsubscribe = appStore.subscribe(state => {
console.log('БостояниС ΠΎΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΎ:', state);
});

// ΠžΡ‚ΠΏΠΈΡΠ°Ρ‚ΡŒΡΡ ΠΎΡ‚ ΠΎΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΠΉ
unsubscribe();
```

---

## πŸ”Œ Offline-First с IndexedDB

SimplePWA Π²ΠΊΠ»ΡŽΡ‡Π°Π΅Ρ‚ ΠΌΠΎΡ‰Π½Ρ‹ΠΉ ΠΌΠ΅Π½Π΅Π΄ΠΆΠ΅Ρ€ синхронизации для Ρ€Π°Π±ΠΎΡ‚Ρ‹ с Π΄Π°Π½Π½Ρ‹ΠΌΠΈ Π² ΠΎΡ„Ρ„Π»Π°ΠΉΠ½-Ρ€Π΅ΠΆΠΈΠΌΠ΅:

```js
import { App } from '@dakir/simple-pwa';

const app = App.create({
syncDataManagerOptions: {
dbName: 'my-app-db'
}
});

// ΠŸΠΎΠ»ΡƒΡ‡Π΅Π½ΠΈΠ΅ Π΄Π°Π½Π½Ρ‹Ρ… ΠΈΠ· IndexedDB
const items = await app.getDBList();

// Π”ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ элСмСнта
await app.addDBItem({
id: '123',
type: 'task',
data: JSON.stringify({ title: 'Π—Π°Π΄Π°Ρ‡Π°', completed: false }),
lastModified: new Date().toISOString(),
lastSynced: new Date().toISOString()
});

// Бинхронизация с сСрвСром ΠΏΡ€ΠΈ восстановлСнии соСдинСния
app.setupAutoSync({
tasks: () => fetch('/api/tasks').then(res => res.json()),
users: () => fetch('/api/users').then(res => res.json())
}, {
onNetworkRestore: true,
interval: 5 * 60 * 1000 // ΠšΠ°ΠΆΠ΄Ρ‹Π΅ 5 ΠΌΠΈΠ½ΡƒΡ‚
});
```

---

## πŸ› οΈ Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ PWA Ρ‡Π΅Ρ€Π΅Π· CLI

```bash
npx create-pwa my-project
```

CLI задаст вопросы ΠΎ Π½Π°Π·Π²Π°Π½ΠΈΠΈ, описании, ΠΈΠΊΠΎΠ½ΠΊΠ°Ρ… ΠΈ Π΄Ρ€ΡƒΠ³ΠΈΡ… ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Π°Ρ…. ПослС Π·Π°Π²Π΅Ρ€ΡˆΠ΅Π½ΠΈΡ Π²Ρ‹ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΡ‚Π΅ Π³ΠΎΡ‚ΠΎΠ²Ρ‹ΠΉ PWA ΠΏΡ€ΠΎΠ΅ΠΊΡ‚, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ ΠΌΠΎΠΆΠ½ΠΎ сразу Π·Π°ΠΏΡƒΡΡ‚ΠΈΡ‚ΡŒ.

### πŸ§‘β€πŸ”§ ГСнСрация Service Worker

```bash
npx create-sw
```

CLI ΠΏΠΎΠΌΠΎΠΆΠ΅Ρ‚ Π½Π°ΡΡ‚Ρ€ΠΎΠΈΡ‚ΡŒ стратСгии ΠΊΠ΅ΡˆΠΈΡ€ΠΎΠ²Π°Π½ΠΈΡ ΠΈ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΡƒ ΠΎΡ„Ρ„Π»Π°ΠΉΠ½-состояния.

---

## πŸ“˜ API Reference

### `App`

Класс `App` инкапсулируСт ΠΌΠ°Ρ€ΡˆΡ€ΡƒΡ‚ΠΈΠ·Π°Ρ†ΠΈΡŽ, глобальноС состояниС, ΡΠΈΠ½Ρ…Ρ€ΠΎΠ½ΠΈΠ·Π°Ρ†ΠΈΡŽ ΠΈ Ρ€Π°Π±ΠΎΡ‚Ρƒ с PWA.

#### 🏁 Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ прилоТСния

```js
import { App } from '@dakir/simple-pwa';

const app = App.create({
rootElement: '#app', // ΠΈΠ»ΠΈ HTMLElement
routes: [ /* ... */ ],
pwa: { enabled: true },
syncDataManagerOptions: { dbName: 'my-db' }
});
```

#### πŸ”§ ΠžΡΠ½ΠΎΠ²Π½Ρ‹Π΅ ΠΌΠ΅Ρ‚ΠΎΠ΄Ρ‹

| ΠœΠ΅Ρ‚ΠΎΠ΄ | ОписаниС |
|------|----------|
| `start()` | Π—Π°ΠΏΡƒΡΡ‚ΠΈΡ‚ΡŒ ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ |
| `navigate(path)` | ΠŸΠ΅Ρ€Π΅ΠΉΡ‚ΠΈ ΠΏΠΎ ΠΌΠ°Ρ€ΡˆΡ€ΡƒΡ‚Ρƒ |
| `getDeviceType()` | ΠŸΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒ Ρ‚Π΅ΠΊΡƒΡ‰ΠΈΠΉ Ρ‚ΠΈΠΏ устройства (`mobile` / `desktop`) |
| `getState()` | ΠŸΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ состояния store |
| `setState(patch)` | ΠžΠ±Π½ΠΎΠ²ΠΈΡ‚ΡŒ состояниС store |
| `addEventListener()` | Π”ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ Π³Π»ΠΎΠ±Π°Π»ΡŒΠ½Ρ‹ΠΉ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ событий |

---

### 🧲 Π Π°Π±ΠΎΡ‚Π° с IndexedDB (SyncDataManager)

| ΠœΠ΅Ρ‚ΠΎΠ΄ | ОписаниС |
|------|----------|
| `getDBList(...)` | ΠŸΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒ список ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ² ΠΈΠ· IndexedDB |
| `getDBListByIndex(...)` | ΠŸΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒ список ΠΏΠΎ индСксу |
| `getDBItem(id)` | ΠŸΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ ΠΏΠΎ ID |
| `getDBItemByIndex(...)` | ΠŸΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ ΠΏΠΎ ΠΎΠΏΡ€Π΅Π΄Π΅Π»Ρ‘Π½Π½ΠΎΠΌΡƒ индСксу |
| `addDBItem(obj)` | Π”ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ |
| `updateDBItem(obj)` | ΠžΠ±Π½ΠΎΠ²ΠΈΡ‚ΡŒ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ |
| `deleteDBItem(id)` | Π£Π΄Π°Π»ΠΈΡ‚ΡŒ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ ΠΏΠΎ ID |
| `syncDBList(fetchMethod)` | Π‘ΠΈΠ½Ρ…Ρ€ΠΎΠ½ΠΈΠ·ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ список с сСрвСром |
| `syncDBItem(fetchMethod)` | Π‘ΠΈΠ½Ρ…Ρ€ΠΎΠ½ΠΈΠ·ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ ΠΎΠ΄ΠΈΠ½ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ с сСрвСром |
| `isSyncNeeded(lastSynced, [timeDelay])` | ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΈΡ‚ΡŒ, трСбуСтся Π»ΠΈ повторная синхронизация |
| `deleteDB([nameDB])` | Π£Π΄Π°Π»ΠΈΡ‚ΡŒ Π±Π°Π·Ρƒ Π΄Π°Π½Π½Ρ‹Ρ… |
| `isOfflineMode()` | ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΈΡ‚ΡŒ, Π² ΠΎΡ„Ρ„Π»Π°ΠΉΠ½-Ρ€Π΅ΠΆΠΈΠΌΠ΅ Π»ΠΈ Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚ ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ |
| `isSyncing()` | ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΈΡ‚ΡŒ, ΠΈΠ΄Ρ‘Ρ‚ Π»ΠΈ сСйчас синхронизация |
| `syncAllData(methods)` | Π—Π°ΠΏΡƒΡΡ‚ΠΈΡ‚ΡŒ ΡΠΈΠ½Ρ…Ρ€ΠΎΠ½ΠΈΠ·Π°Ρ†ΠΈΡŽ всСх ΠΊΠΎΠ»Π»Π΅ΠΊΡ†ΠΈΠΉ (Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, Π·Π°Π΄Π°Ρ‡ ΠΈ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Π΅ΠΉ ΠΎΠ΄Π½ΠΎΠ²Ρ€Π΅ΠΌΠ΅Π½Π½ΠΎ) |
| `setupAutoSync(methods, options)` | ΠΠ°ΡΡ‚Ρ€ΠΎΠΈΡ‚ΡŒ Π°Π²Ρ‚ΠΎΡΠΈΠ½Ρ…Ρ€ΠΎΠ½ΠΈΠ·Π°Ρ†ΠΈΡŽ ΠΏΡ€ΠΈ восстановлСнии сСти, ΠΏΠΎ Ρ‚Π°ΠΉΠΌΠ΅Ρ€Ρƒ, ΠΏΡ€ΠΈ стартС |
| `checkAndSync(id, method, [timeDelay])` | ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΈΡ‚ΡŒ ΠΈ ΡΠΈΠ½Ρ…Ρ€ΠΎΠ½ΠΈΠ·ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ Π΄Π°Π½Π½Ρ‹Π΅ ΠΏΠΎ нСобходимости |

#### ΠŸΡ€ΠΈΠΌΠ΅Ρ€ использования ΠΌΠ΅Ρ‚ΠΎΠ΄ΠΎΠ² синхронизации

```js
// ΠŸΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒ всС Π·Π°Π΄Π°Ρ‡ΠΈ
const tasks = await app.getDBList();

// Π”ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ Π½ΠΎΠ²ΡƒΡŽ Π·Π°Π΄Π°Ρ‡Ρƒ
await app.addDBItem({ id: '1', type: 'task', ... });

// Π‘ΠΈΠ½Ρ…Ρ€ΠΎΠ½ΠΈΠ·ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ Π·Π°Π΄Π°Ρ‡ΠΈ с сСрвСром
await app.syncDBList(() => fetch('/api/tasks').then(r => r.json()));

// АвтоматичСская синхронизация ΠΏΡ€ΠΈ восстановлСнии сСти
app.setupAutoSync({
tasks: () => fetch('/api/tasks').then(r => r.json())
});
```

---

### 🧠 Store

Π Π΅Π°ΠΊΡ‚ΠΈΠ²Π½Ρ‹ΠΉ Π³Π»ΠΎΠ±Π°Π»ΡŒΠ½Ρ‹ΠΉ стор для хранСния состояния прилоТСния.

```js
import { Store } from '@dakir/simple-pwa';

// Π‘ΠΎΠ·Π΄Π°Ρ‚ΡŒ стор
const store = new Store({ theme: 'light', counter: 0 });

// Подписка на измСнСния
const unsubscribe = store.subscribe(state => {
console.log('НовоС состояниС:', state);
});

// ΠžΠ±Π½ΠΎΠ²ΠΈΡ‚ΡŒ состояниС
store.setState({ theme: 'dark' });

// ΠžΡ‚ΠΏΠΈΡΠΊΠ°
unsubscribe();
```

---

### 🧱 ΠšΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Ρ‹ (Web Components)

Π‘Π°Π·ΠΎΠ²Ρ‹ΠΉ класс `Component` ΠΈ ΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ΄Π½Ρ‹Π΅ `DesktopComponent`, `MobileComponent`.

```js
import { Component } from '@dakir/simple-pwa';

class MyCounter extends Component {
render() {
if (this.shadowRoot) {
this.shadowRoot.innerHTML = `
Count: ${this.state.count || 0}
+
`;
this.shadowRoot.getElementById('inc')?.addEventListener('click', () => {
this.setState({ count: (this.state.count || 0) + 1 });
});
}
}
}

customElements.define('my-counter', MyCounter);
```

---

### 🧭 Адаптивная ΠΌΠ°Ρ€ΡˆΡ€ΡƒΡ‚ΠΈΠ·Π°Ρ†ΠΈΡ

Π˜ΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠΉΡ‚Π΅ ΠΌΠΎΠ±ΠΈΠ»ΡŒΠ½Ρ‹Π΅ ΠΈ дСсктопныС ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Ρ‹ для Ρ€Π°Π·Π½Ρ‹Ρ… устройств:

```js
const routes = [
{
path: '/',
desktopComponent: DesktopHome,
mobileComponent: MobileHome
}
];
const app = new App({ routes });
```

---

## πŸ“œ ЛицСнзия

MIT

---

## πŸ“¬ ΠžΠ±Ρ€Π°Ρ‚Π½Π°Ρ связь ΠΈ ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΊΠ°

Нашли Π±Π°Π³ ΠΈΠ»ΠΈ Ρ…ΠΎΡ‚ΠΈΡ‚Π΅ ΠΏΡ€Π΅Π΄Π»ΠΎΠΆΠΈΡ‚ΡŒ ΡƒΠ»ΡƒΡ‡ΡˆΠ΅Π½ΠΈΠ΅?
ΠžΡ‚ΠΊΡ€Ρ‹Π²Π°ΠΉΡ‚Π΅ [issue](https://github.com/Dakirchik/simplePWA/issues) ΠΈΠ»ΠΈ [PR](https://github.com/Dakirchik/simplePWA/pulls)!

---

πŸš€ **SimplePWA β€” ваш быстрый ΠΏΡƒΡ‚ΡŒ ΠΊ production-ready PWA!**