Skip to content

watch method #108

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 186 commits into from
Oct 8, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
186 commits
Select commit Hold shift + click to select a range
f402492
feat: new JSONSchem, and shortcuts and auto-inference for basic types
cyrilletuzi Jan 27, 2019
a88cab4
revert: remove shortcuts
cyrilletuzi Jan 27, 2019
582f972
tests: fix typo
cyrilletuzi Jan 27, 2019
1b614fa
test: test json schema standard support
cyrilletuzi Jan 27, 2019
e3df460
tests: remove unecessary casts
cyrilletuzi Jan 27, 2019
d274bfd
merge commit
cyrilletuzi Jan 27, 2019
199c57b
doc: documentation for v8 validation
cyrilletuzi Jan 27, 2019
ce1ff45
doc: delete old statement
cyrilletuzi Jan 27, 2019
6e70771
tests: fix const tests
cyrilletuzi Jan 27, 2019
8ce0a87
feat: update to TS 3.2
cyrilletuzi Jan 27, 2019
f518717
feat: update peerDependencies to Angular 8 and RxJS 6.3
cyrilletuzi Jan 28, 2019
78c9f5e
revert: rxjs peer 6.0
cyrilletuzi Jan 28, 2019
8163bac
fix: minor updates
cyrilletuzi Jan 28, 2019
2da53df
fix: remove unused deps
cyrilletuzi Jan 28, 2019
5cba136
feat: Angular 8.0.0-beta.1
cyrilletuzi Jan 28, 2019
cd97f1d
build: next tag for beta
cyrilletuzi Jan 28, 2019
022a7a6
docs: update README to v8
cyrilletuzi Jan 28, 2019
60bede9
doc: info about maintenance
cyrilletuzi Jan 28, 2019
47231e5
deps: update to latest beta, pin ts to minor and tsickle to patch
cyrilletuzi Feb 4, 2019
c576497
docs: flag specific JSONSchema interfaces as to be ignored
cyrilletuzi Feb 4, 2019
e13f4f8
Merge branch 'newjsonschema' into v8beta
cyrilletuzi Feb 4, 2019
848a205
docs: migration guide to v8
cyrilletuzi Feb 4, 2019
e8cf02b
feat: disallow null or undefined in setItem
cyrilletuzi Feb 4, 2019
ae91e8f
docs: changelog for v8.0.0-beta.0
cyrilletuzi Feb 4, 2019
8bd2742
lint
cyrilletuzi Feb 4, 2019
5742b25
Merge branch 'nonullorundefined' into v8beta
cyrilletuzi Feb 4, 2019
f94e64e
fix: align app test to new schema
cyrilletuzi Feb 4, 2019
7af8cdf
tests: update to latest tools
cyrilletuzi Feb 5, 2019
1daf1be
feat: allow custom indexeddb database and store names (#78)
cyrilletuzi Feb 5, 2019
4cd56c8
docs: indexeddb db and store names doc
cyrilletuzi Feb 5, 2019
dcaaf2a
refactor: indexeddb
cyrilletuzi Feb 5, 2019
f20d423
docs: info about special chars in options
cyrilletuzi Feb 6, 2019
eea6792
refactor: rename indexedb things
cyrilletuzi Feb 6, 2019
a8d176a
refactor: rename IDB things
cyrilletuzi Feb 6, 2019
054dae7
docs: indentify all storage scenarios
cyrilletuzi Feb 6, 2019
31a272a
refactor: localstorage
cyrilletuzi Feb 6, 2019
57dbf40
refactor: mock storage
cyrilletuzi Feb 6, 2019
c627cdf
refactor: rename mock to memory
cyrilletuzi Feb 6, 2019
717760f
docs
cyrilletuzi Feb 6, 2019
b7f9e78
refactor: jsonvalidator
cyrilletuzi Feb 6, 2019
7278f4c
refactor: main service
cyrilletuzi Feb 6, 2019
32ce819
tests: remove useless validator tests
cyrilletuzi Feb 6, 2019
928c0b1
tests: align to new error message
cyrilletuzi Feb 6, 2019
b2e12e6
feat: better error management and move idb fallback to main service
cyrilletuzi Feb 6, 2019
c0cf472
internal: reduce surface api
cyrilletuzi Feb 6, 2019
02f9144
docs: remove some done todos
cyrilletuzi Feb 6, 2019
ac58b7c
docs: how to force a standard json schema
cyrilletuzi Feb 6, 2019
62711bc
refactor: validator tests
cyrilletuzi Feb 6, 2019
5cb6161
refator: storages tests
cyrilletuzi Feb 6, 2019
26fbeee
feat: export default IDB database and store names
cyrilletuzi Feb 6, 2019
d745ffe
tests
cyrilletuzi Feb 6, 2019
d682737
release: v8.0.0-beta.2
cyrilletuzi Feb 6, 2019
22e40eb
cleanup: remove unecessary e2e project
cyrilletuzi Feb 6, 2019
0b323f1
lint
cyrilletuzi Feb 6, 2019
482fc56
tests: clear localStorage directly
cyrilletuzi Feb 6, 2019
ea82032
docs: add references to issues
cyrilletuzi Feb 7, 2019
dd0eca6
refactor: explicit conditions
cyrilletuzi Feb 7, 2019
04e77cd
docs: add todo
cyrilletuzi Feb 7, 2019
63c26ff
tests: clean indexeddb correctly
cyrilletuzi Feb 7, 2019
9b0afe3
tests: do compilation tests on memory storage
cyrilletuzi Feb 7, 2019
d6e1812
feat: allow usage of idb when in workers
cyrilletuzi Feb 7, 2019
b392f7a
docs: changelog
cyrilletuzi Feb 7, 2019
f4427a6
fix: stringify all keys for interoperability
cyrilletuzi Feb 7, 2019
fe71c5a
docs: info about keys type
cyrilletuzi Feb 7, 2019
b1cb16d
fix: better error management
cyrilletuzi Feb 7, 2019
0a91aed
tests: better compilation tests
cyrilletuzi Feb 7, 2019
5c225fe
feat: new getItem() signature (#79)
cyrilletuzi Feb 7, 2019
585c585
merge commit
cyrilletuzi Feb 7, 2019
8e943a0
docs: migration
cyrilletuzi Feb 7, 2019
3c42132
fix: catch stringify error
cyrilletuzi Feb 7, 2019
ae9d083
fix: catch quota error on localStorage setItem
cyrilletuzi Feb 7, 2019
b3d22ae
fix: different returns in getItem to avoid any future mess
cyrilletuzi Feb 7, 2019
317e38f
fix: catch missing store error
cyrilletuzi Feb 7, 2019
e6cd950
docs: remove done todo
cyrilletuzi Feb 7, 2019
2b6f0cb
deps: Angular 8 beta 3
cyrilletuzi Feb 7, 2019
7f15fa4
release: v8.0.0-beta.3
cyrilletuzi Feb 7, 2019
5ee8f1d
fix: remove getUnsafeItem()
cyrilletuzi Feb 7, 2019
752ee45
merge commit
cyrilletuzi Feb 7, 2019
62350d4
deps: CLI v8.0.0-beta.0
cyrilletuzi Feb 8, 2019
be9cba7
docs: list of errors
cyrilletuzi Feb 8, 2019
8214441
docs: delete ignore on json schema interfaces
cyrilletuzi Feb 8, 2019
710bc38
revert: allow any in setItem()
cyrilletuzi Feb 8, 2019
8411bb5
docs
cyrilletuzi Feb 8, 2019
37117d9
docs
cyrilletuzi Feb 8, 2019
07b32be
docs
cyrilletuzi Feb 8, 2019
582aa06
docs
cyrilletuzi Feb 8, 2019
dc08109
docs
cyrilletuzi Feb 8, 2019
2032a07
docs
cyrilletuzi Feb 8, 2019
0246cfc
tests
cyrilletuzi Feb 8, 2019
6d7c5df
docs
cyrilletuzi Feb 8, 2019
6b59032
docs
cyrilletuzi Feb 8, 2019
2ee33d5
feat: new IDB storing behavior with no wrapping (#82)
cyrilletuzi Feb 11, 2019
b16e1be
fix: typing
cyrilletuzi Feb 11, 2019
62be22e
feat: new store system for interop with backward compat (#84)
cyrilletuzi Feb 13, 2019
a3e05fb
release: 8.0.0-beta.4
cyrilletuzi Feb 13, 2019
b399d82
todos
cyrilletuzi Feb 13, 2019
a3381bf
doc: info for testing types
cyrilletuzi Feb 22, 2019
8b952c8
fix: use observers in subscribe to align with RxJS 6.4 recommendations
cyrilletuzi Feb 23, 2019
b757914
merge commit
cyrilletuzi Feb 25, 2019
8df07c9
tests: move demo app in projects
cyrilletuzi Feb 25, 2019
2e4ad68
deps: update to latest betas
cyrilletuzi Feb 25, 2019
4291276
Merge branch 'master' into v8beta
cyrilletuzi Feb 26, 2019
52d0520
feat: add length alias
cyrilletuzi Mar 10, 2019
9fd39e1
fix: move to rxjs observers when error callback
cyrilletuzi Mar 10, 2019
17b0c19
Merge branch 'master' into v8beta
cyrilletuzi Mar 10, 2019
3f43eca
Merge branch 'master' into v8beta
cyrilletuzi Mar 10, 2019
08cb436
tests: e2e tests
cyrilletuzi Mar 11, 2019
6ea17a6
tests: test against v7
cyrilletuzi Mar 11, 2019
4989a38
tests
cyrilletuzi Mar 11, 2019
3dd7ec7
tests
cyrilletuzi Mar 11, 2019
2123e40
feat: allow ng7 as peerDependency
cyrilletuzi Mar 11, 2019
43d294e
docs: info about versions
cyrilletuzi Mar 11, 2019
f0dcb18
release: v8.0.0-beta.5
cyrilletuzi Mar 11, 2019
e568328
Tuple (#92)
cyrilletuzi Mar 17, 2019
63a942e
feat: removeItem when storing null or undefined (#93)
cyrilletuzi Mar 17, 2019
654d13b
docs: info about serialization (#94)
cyrilletuzi Mar 17, 2019
9a712d4
docs: v8 beta6 changelog
cyrilletuzi Mar 17, 2019
db3a269
release: v8 beta6
cyrilletuzi Mar 17, 2019
154e480
feat: ts 3.3 and last ng v8 beta
cyrilletuzi Mar 21, 2019
debd12c
doc: changelog v8 beta 7
cyrilletuzi Mar 21, 2019
10975f2
release: v8 beta 7
cyrilletuzi Mar 21, 2019
7c2dd76
test: lazy-loading
cyrilletuzi Mar 21, 2019
9f56d28
feat: last cli beta
cyrilletuzi Mar 22, 2019
33006b5
merge commit
cyrilletuzi Mar 25, 2019
0c67b9e
feat: remove xSubscribe() methods (#96)
cyrilletuzi Mar 25, 2019
af8546e
tests: remove tests concerning last pr
cyrilletuzi Mar 25, 2019
188ad02
feat: last betas, align lint to cli, ng-packagr@5
cyrilletuzi Mar 28, 2019
318e15a
tests: ci config
cyrilletuzi Mar 28, 2019
7504e54
tests: ci config
cyrilletuzi Mar 28, 2019
71b266f
tests: better e2e test
cyrilletuzi Apr 3, 2019
f7d1d43
tests: revert to core-js@2 until cli support
cyrilletuzi Apr 3, 2019
2c5db85
tests: temp fix for non interactive cli in ci
cyrilletuzi Apr 3, 2019
e633c99
tests: update webdriver for e2e tests
cyrilletuzi Apr 3, 2019
8640d05
tests: e2e test in firefox too
cyrilletuzi Apr 3, 2019
7e16f2c
feat: update to last betas
cyrilletuzi Apr 22, 2019
4dbe90f
fix: keep old TestBed API for CI
cyrilletuzi Apr 22, 2019
b1253cd
fix: go back to normal npm install in ci
cyrilletuzi Apr 22, 2019
e8b2aa9
fix: revert to ignore-scripts, cli still brokes ci
cyrilletuzi Apr 22, 2019
99c8ce8
feat: separate localStorage and Map APIs (#100)
cyrilletuzi Apr 24, 2019
534f272
fix: reintroduce xSubscribe methods but deprecated
cyrilletuzi Apr 24, 2019
b10177c
release: v8 beta8
cyrilletuzi Apr 24, 2019
1827bbf
deps: update to last betas
cyrilletuzi Apr 24, 2019
32146a0
release: v8 beta9
cyrilletuzi Apr 24, 2019
520f274
feat: first draft of a watch() method
cyrilletuzi Apr 24, 2019
b275845
feat: watch() with optional JSON schema
cyrilletuzi Apr 25, 2019
db9dd3f
fix: save notifier
cyrilletuzi Apr 25, 2019
d8eb139
test: watch()
cyrilletuzi Apr 25, 2019
8d9a9c3
doc: watch()
cyrilletuzi Apr 25, 2019
5d2ac31
doc: todos
cyrilletuzi Apr 26, 2019
03075c8
feat: Angular v8 rc0
cyrilletuzi Apr 26, 2019
3dab749
ci: back to classic config
cyrilletuzi Apr 26, 2019
7c027e1
ci: test with rxjs@6.0.0
cyrilletuzi Apr 26, 2019
0322690
ci: exact versions
cyrilletuzi Apr 26, 2019
250282c
ci: fix cli 7 version
cyrilletuzi Apr 26, 2019
3f28b72
ci: correct versions
cyrilletuzi Apr 26, 2019
c314db1
ci: remove es5 option not supported by old CLI
cyrilletuzi Apr 26, 2019
4f2cab3
feat: rxjs 6.4 as minimum
cyrilletuzi Apr 26, 2019
6a24100
release: v8 beta.10
cyrilletuzi Apr 26, 2019
578d1b8
doc: todos
cyrilletuzi Apr 26, 2019
ee35788
test: ivy (#102)
cyrilletuzi Apr 27, 2019
589a85f
feat: ng v8 rc1
cyrilletuzi Apr 27, 2019
8319278
fix: align to v8 browserslist config
cyrilletuzi Apr 27, 2019
50c251f
tests: align to v8 e2e structure
cyrilletuzi Apr 27, 2019
75dffc1
fix: align project name to CLI constraints
cyrilletuzi Apr 27, 2019
9b92065
fix: use openKeyCursor() when possible for perf
cyrilletuzi Apr 27, 2019
0616a03
feat: schema option in set()
cyrilletuzi Apr 27, 2019
bbecc17
fix: cleaner get() overloads
cyrilletuzi Apr 27, 2019
ca5dbf0
merge
cyrilletuzi Apr 27, 2019
277a839
fix: cleaner overloads
cyrilletuzi Apr 27, 2019
e4457eb
doc
cyrilletuzi Apr 28, 2019
48748fe
merge
cyrilletuzi May 28, 2019
3564a91
merge
cyrilletuzi May 28, 2019
d96b27d
docs: update version info
cyrilletuzi May 28, 2019
b9b9f7e
Merge branch 'master' into watch
cyrilletuzi Jul 12, 2019
acb1a5d
release: v8.1.0-beta.0
cyrilletuzi Jul 12, 2019
5f46fd9
merge commit
cyrilletuzi Jul 26, 2019
c23bd6b
merge commit
cyrilletuzi Aug 17, 2019
4286ac4
docs: update version
cyrilletuzi Aug 17, 2019
607d1c4
release: change version to v9
cyrilletuzi Aug 18, 2019
1ae8376
merge commit
cyrilletuzi Aug 21, 2019
250a04f
docs
cyrilletuzi Aug 21, 2019
ecb67ec
Merge branch 'v9beta' into watch
cyrilletuzi Aug 21, 2019
f617317
Merge branch 'v9beta' into watch
cyrilletuzi Sep 27, 2019
c22f8dd
Merge branch 'v9beta' into watch
cyrilletuzi Oct 6, 2019
cb6a26b
Merge branch 'v9beta' into watch
cyrilletuzi Oct 6, 2019
2469778
docs
cyrilletuzi Oct 8, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -189,11 +189,8 @@ this.storage.get('notexisting').subscribe((data) => {
```

Note you'll only get *one* value: the `Observable` is here for asynchrony but is not meant to
emit again when the stored data is changed. And it's normal: if app data change, it's the role of your app
to keep track of it, not of this lib. See [#16](https://github.com/cyrilletuzi/angular-async-local-storage/issues/16)
for more context and [#4](https://github.com/cyrilletuzi/angular-async-local-storage/issues/4)
for an example. A `watch()` method may come soon
(see [#108](https://github.com/cyrilletuzi/angular-async-local-storage/pull/108))
emit again when the stored data is changed. If you need to watch the value,
version 9 introduced a `watch()` method, see the [watching guide](./docs/WATCHING.md).

### Checking data

Expand Down
113 changes: 113 additions & 0 deletions docs/WATCHING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# Watching guide

*Since version 9 of this lib*, you can now watch an item via the `StorageMap` service.

Notes:

- Client-side storage only makes sense for keeping data when the user *leaves* the application
and comes back later; if you just need to track data when the user stays in the application,
you do *not* need to watch and not even to use client-side storage: you just need a variable/property.

- Unlike all other `Observable`s of this lib, this one won't auto-complete,
as the purpose is to watch indefinitely. So **be sure to unsubscribe**.

- The lib can only detect changes in storage done *with this lib* (ie. via `set()` / `.delete()` or `.clear()`).
It *cannot* detect external changes (for examples via the native `indexedDB` API or via another lib like `localForage`).

## Via manual subscription

```typescript
import { Subscription } from 'rxjs';
import { StorageMap } from '@ngx-pwa/local-storage';

@Component({
template: `<p>{{data}}<p>`
})
export class SomeComponent implements OnInit, OnDestroy {

data: string;
dataSubscription: Subscription;

constructor(private storageMap: StorageMap) {}

ngOnInit() {
this.dataSubscription = this.storageMap.watch('somekey', { type: 'string' })
.subscribe((result) => {
this.data = result;
});
}

ngOnDestroy() {
this.dataSubscription.unsubcribe();
}

}
```

## Via the `async` pipe

```typescript
import { Observable } from 'rxjs';
import { StorageMap } from '@ngx-pwa/local-storage';

@Component({
template: `<p>{{data$ | async}}<p>`
})
export class SomeComponent implements OnInit {

data$: Observable<string>;

constructor(private storageMap: StorageMap) {}

ngOnInit() {
this.data$ = this.storageMap.watch('somekey', { type: 'string' });
}

}
```

Note as usual in Angular, do *not* use the `async` pipe twice on the same `Observable`.
If you need the data in several places:

```typescript
import { Observable } from 'rjxs';
import { StorageMap, JSONSchema } from '@ngx-pwa/local-storage';

interface Data {
hello: string;
world: string;
}

@Component({
template: `
<div *ngIf="data$ | async as data">
<p>{{data.hello}}</p>
<p>{{data.world}}</p>
<div>
`
})
export class SomeComponent implements OnInit {

data$: Observable<Data>;

constructor(private storageMap: StorageMap) {}

ngOnInit() {

const schema: JSONSchema = {
type: 'object',
properties: {
hello: { type: 'string' },
world: { type: 'string' },
},
required: ['hello', 'world'],
};

this.data$ = this.storageMap.watch<Data>('somekey', schema);

}

}
```

[Back to general documentation](../README.md)
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,87 @@ function tests(description: string, localStorageServiceFactory: () => StorageMap

});

describe('watch()', () => {

it('with valid schema', (done) => {

const watchedKey = 'watched1';
const values = [undefined, 'test1', undefined, 'test2', undefined];
let i = 0;

localStorageService.watch(watchedKey, { type: 'string' }).subscribe((result) => {

expect(result).toBe(values[i]);

i += 1;

if (i === 1) {

localStorageService.set(watchedKey, values[1]).pipe(
mergeMap(() => localStorageService.delete(watchedKey)),
mergeMap(() => localStorageService.set(watchedKey, values[3])),
mergeMap(() => localStorageService.clear()),
).subscribe();

}

if (i === values.length) {
done();
}

});

});

it('with invalid schema', (done) => {

const watchedKey = 'watched2';

localStorageService.set(watchedKey, 'test').subscribe(() => {

localStorageService.watch(watchedKey, { type: 'number' }).subscribe({
error: () => {
expect().nothing();
done();
}
});

});

});

it('without schema', (done) => {

const watchedKey = 'watched3';
const values = [undefined, 'test1', undefined, 'test2', undefined];
let i = 0;

localStorageService.watch(watchedKey).subscribe((result) => {

expect(result).toBe(values[i]);

i += 1;

if (i === 1) {

localStorageService.set(watchedKey, values[1]).pipe(
mergeMap(() => localStorageService.delete(watchedKey)),
mergeMap(() => localStorageService.set(watchedKey, values[3])),
mergeMap(() => localStorageService.clear()),
).subscribe();

}

if (i === values.length) {
done();
}

});

});

});

/* Avoid https://github.com/cyrilletuzi/angular-async-local-storage/issues/25
* Avoid https://github.com/cyrilletuzi/angular-async-local-storage/issues/5 */
describe('complete', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Injectable, Inject } from '@angular/core';
import { Observable, throwError, of, OperatorFunction } from 'rxjs';
import { mergeMap, catchError } from 'rxjs/operators';
import { Observable, throwError, of, OperatorFunction, ReplaySubject } from 'rxjs';
import { mergeMap, catchError, tap } from 'rxjs/operators';

import { ValidationError } from './exceptions';
import {
Expand All @@ -15,6 +15,8 @@ import { LS_PREFIX } from '../tokens';
})
export class StorageMap {

protected notifiers = new Map<string, ReplaySubject<any>>();

/**
* Constructor params are provided by Angular (but can also be passed manually in tests)
* @param database Storage to use
Expand Down Expand Up @@ -220,10 +222,12 @@ export class StorageMap {
return throwError(new ValidationError());
}

return this.database.set(key, data)
return this.database.set(key, data).pipe(
/* Catch if `indexedDb` is broken */
.pipe(this.catchIDBBroken(() => this.database.set(key, data)));

this.catchIDBBroken(() => this.database.set(key, data)),
/* Notify watchers (must be last because it should only happen if the operation succeeds) */
tap(() => { this.notify(key, data); }),
);
}

/**
Expand All @@ -236,9 +240,12 @@ export class StorageMap {
*/
delete(key: string): Observable<undefined> {

return this.database.delete(key)
return this.database.delete(key).pipe(
/* Catch if `indexedDb` is broken */
.pipe(this.catchIDBBroken(() => this.database.delete(key)));
this.catchIDBBroken(() => this.database.delete(key)),
/* Notify watchers (must be last because it should only happen if the operation succeeds) */
tap(() => { this.notify(key, undefined); }),
);

}

Expand All @@ -251,9 +258,16 @@ export class StorageMap {
*/
clear(): Observable<undefined> {

return this.database.clear()
return this.database.clear().pipe(
/* Catch if `indexedDb` is broken */
.pipe(this.catchIDBBroken(() => this.database.clear()));
this.catchIDBBroken(() => this.database.clear()),
/* Notify watchers (must be last because it should only happen if the operation succeeds) */
tap(() => {
for (const key of this.notifiers.keys()) {
this.notify(key, undefined);
}
}),
);

}

Expand Down Expand Up @@ -295,6 +309,64 @@ export class StorageMap {

}

/**
* Watch an item value in storage.
* **Note only changes done via this lib will be watched**, external changes in storage can't be detected.
* The signature has many overloads due to validation, **please refer to the documentation.**
* @see https://github.com/cyrilletuzi/angular-async-local-storage/blob/master/docs/VALIDATION.md
* @param key The item's key to watch
* @param schema Optional JSON schema to validate the initial value
* @returns An infinite `Observable` giving the current value
*/
watch<T = string>(key: string, schema: JSONSchemaString): Observable<string | undefined>;
watch<T = number>(key: string, schema: JSONSchemaInteger | JSONSchemaNumber): Observable<number | undefined>;
watch<T = boolean>(key: string, schema: JSONSchemaBoolean): Observable<boolean | undefined>;
watch<T = string[]>(key: string, schema: JSONSchemaArrayOf<JSONSchemaString>): Observable<string[] | undefined>;
watch<T = number[]>(key: string, schema: JSONSchemaArrayOf<JSONSchemaInteger | JSONSchemaNumber>): Observable<number[] | undefined>;
watch<T = boolean[]>(key: string, schema: JSONSchemaArrayOf<JSONSchemaBoolean>): Observable<boolean[] | undefined>;
watch<T = any>(key: string, schema: JSONSchema): Observable<T | undefined>;
watch<T = unknown>(key: string, schema?: JSONSchema): Observable<unknown>;
watch<T = any>(key: string, schema?: JSONSchema) {

/* Check if there is already a notifier and cast according to schema */
let notifier = this.notifiers.get(key) as ReplaySubject<typeof schema extends JSONSchema ? (T | undefined) : unknown>;

if (!notifier) {

/* Create a notifier and cast according to schema */
notifier = new ReplaySubject<typeof schema extends JSONSchema ? (T | undefined) : unknown>(1);

/* Memorize the notifier */
this.notifiers.set(key, notifier);

/* Get the current item value */
this.get<T>(key, schema).subscribe({
next: (result) => notifier.next(result),
error: (error) => notifier.error(error),
});

}

/* Only the public API of the `Observable` should be returned */
return notifier.asObservable();

}

/**
* Notify when a value changes
* @param key The item's key
* @param data The new value
*/
protected notify(key: string, value: any): void {

const notifier = this.notifiers.get(key);

if (notifier) {
notifier.next(value);
}

}

/**
* RxJS operator to catch if `indexedDB` is broken
* @param operationCallback Callback with the operation to redo
Expand Down