shelving 1.110.1 → 1.110.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/db/CacheProvider.d.ts +6 -0
- package/db/CacheProvider.js +18 -0
- package/db/ItemStore.d.ts +2 -10
- package/db/ItemStore.js +9 -41
- package/db/MemoryProvider.d.ts +2 -0
- package/db/MemoryProvider.js +6 -0
- package/db/QueryStore.d.ts +2 -10
- package/db/QueryStore.js +9 -41
- package/firestore/server/FirestoreServerProvider.js +4 -2
- package/markup/rule.d.ts +30 -15
- package/markup/rule.js +63 -45
- package/markup/rules.d.ts +34 -64
- package/markup/rules.js +13 -13
- package/package.json +1 -1
- package/react/createDataContext.d.ts +19 -10
- package/react/createDataContext.js +8 -9
- package/sequence/DeferredSequence.d.ts +3 -4
- package/sequence/DeferredSequence.js +8 -8
- package/store/LazyStore.d.ts +17 -0
- package/store/LazyStore.js +39 -0
- package/store/index.d.ts +1 -0
- package/store/index.js +1 -0
package/db/CacheProvider.d.ts
CHANGED
|
@@ -11,6 +11,9 @@ export declare class CacheProvider<T extends Database> implements AsyncProvider<
|
|
|
11
11
|
constructor(source: AsyncProvider<T>, cache?: MemoryProvider<T>);
|
|
12
12
|
getItem<K extends DataKey<T>>(collection: K, id: string): Promise<OptionalItem<T[K]>>;
|
|
13
13
|
getItemSequence<K extends DataKey<T>>(collection: K, id: string): AsyncIterable<OptionalItem<T[K]>>;
|
|
14
|
+
getCachedItem<K extends DataKey<T>>(collection: K, id: string): OptionalItem<T[K]>;
|
|
15
|
+
getCachedItemSequence<K extends DataKey<T>>(collection: K, id: string): AsyncIterable<OptionalItem<T[K]>>;
|
|
16
|
+
getCachedItemTime<K extends DataKey<T>>(collection: K, id: string): number | undefined;
|
|
14
17
|
addItem<K extends DataKey<T>>(collection: K, data: T[K]): Promise<string>;
|
|
15
18
|
setItem<K extends DataKey<T>>(collection: K, id: string, data: T[K]): Promise<void>;
|
|
16
19
|
updateItem<K extends DataKey<T>>(collection: K, id: string, updates: Updates<T[K]>): Promise<void>;
|
|
@@ -18,6 +21,9 @@ export declare class CacheProvider<T extends Database> implements AsyncProvider<
|
|
|
18
21
|
countQuery<K extends DataKey<T>>(collection: K, query?: ItemQuery<T[K]>): Promise<number>;
|
|
19
22
|
getQuery<K extends DataKey<T>>(collection: K, query?: ItemQuery<T[K]>): Promise<Items<T[K]>>;
|
|
20
23
|
getQuerySequence<K extends DataKey<T>>(collection: K, query?: ItemQuery<T[K]>): AsyncIterable<Items<T[K]>>;
|
|
24
|
+
getCachedQuery<K extends DataKey<T>>(collection: K, query: ItemQuery<T[K]>): Items<T[K]>;
|
|
25
|
+
getCachedQuerySequence<K extends DataKey<T>>(collection: K, query: ItemQuery<T[K]>): AsyncIterable<Items<T[K]>>;
|
|
26
|
+
getCachedQueryTime<K extends DataKey<T>>(collection: K, query: ItemQuery<T[K]>): number | undefined;
|
|
21
27
|
setQuery<K extends DataKey<T>>(collection: K, query: ItemQuery<T[K]>, data: T[K]): Promise<void>;
|
|
22
28
|
updateQuery<K extends DataKey<T>>(collection: K, query: ItemQuery<T[K]>, updates: Updates<T[K]>): Promise<void>;
|
|
23
29
|
deleteQuery<K extends DataKey<T>>(collection: K, query: ItemQuery<T[K]>): Promise<void>;
|
package/db/CacheProvider.js
CHANGED
|
@@ -15,6 +15,15 @@ export class CacheProvider {
|
|
|
15
15
|
getItemSequence(collection, id) {
|
|
16
16
|
return this.memory.setItemSequence(collection, id, this.source.getItemSequence(collection, id));
|
|
17
17
|
}
|
|
18
|
+
getCachedItem(collection, id) {
|
|
19
|
+
return this.memory.getItem(collection, id);
|
|
20
|
+
}
|
|
21
|
+
getCachedItemSequence(collection, id) {
|
|
22
|
+
return this.memory.getItemSequence(collection, id);
|
|
23
|
+
}
|
|
24
|
+
getCachedItemTime(collection, id) {
|
|
25
|
+
return this.memory.getItemTime(collection, id);
|
|
26
|
+
}
|
|
18
27
|
async addItem(collection, data) {
|
|
19
28
|
const id = await this.source.addItem(collection, data);
|
|
20
29
|
this.memory.setItem(collection, id, data);
|
|
@@ -43,6 +52,15 @@ export class CacheProvider {
|
|
|
43
52
|
getQuerySequence(collection, query) {
|
|
44
53
|
return this.memory.setItemsSequence(collection, this.source.getQuerySequence(collection, query), query);
|
|
45
54
|
}
|
|
55
|
+
getCachedQuery(collection, query) {
|
|
56
|
+
return this.memory.getQuery(collection, query);
|
|
57
|
+
}
|
|
58
|
+
getCachedQuerySequence(collection, query) {
|
|
59
|
+
return this.memory.getQuerySequence(collection, query);
|
|
60
|
+
}
|
|
61
|
+
getCachedQueryTime(collection, query) {
|
|
62
|
+
return this.memory.getQueryTime(collection, query);
|
|
63
|
+
}
|
|
46
64
|
async setQuery(collection, query, data) {
|
|
47
65
|
await this.source.setQuery(collection, query, data);
|
|
48
66
|
this.memory.setQuery(collection, query, data);
|
package/db/ItemStore.d.ts
CHANGED
|
@@ -3,9 +3,9 @@ import type { StopCallback } from "../util/callback.js";
|
|
|
3
3
|
import type { DataKey, Database } from "../util/data.js";
|
|
4
4
|
import type { Item, OptionalItem } from "../util/item.js";
|
|
5
5
|
import { BooleanStore } from "../store/BooleanStore.js";
|
|
6
|
-
import {
|
|
6
|
+
import { LazyStore } from "../store/LazyStore.js";
|
|
7
7
|
/** Store a single item. */
|
|
8
|
-
export declare class ItemStore<T extends Database, K extends DataKey<T>> extends
|
|
8
|
+
export declare class ItemStore<T extends Database, K extends DataKey<T>> extends LazyStore<OptionalItem<T[K]>> {
|
|
9
9
|
readonly provider: AbstractProvider<T>;
|
|
10
10
|
readonly collection: K;
|
|
11
11
|
readonly id: string;
|
|
@@ -24,12 +24,4 @@ export declare class ItemStore<T extends Database, K extends DataKey<T>> extends
|
|
|
24
24
|
refreshStale(maxAge: number, provider?: AbstractProvider<T>): void;
|
|
25
25
|
/** Subscribe this store to a provider. */
|
|
26
26
|
connect(provider?: AbstractProvider<T>): StopCallback;
|
|
27
|
-
[Symbol.asyncIterator](): AsyncGenerator<OptionalItem<T[K]>, void, void>;
|
|
28
|
-
private _iterating;
|
|
29
|
-
/** Start subscription to `MemoryProvider` if there is one. */
|
|
30
|
-
start(): void;
|
|
31
|
-
/** Stop subscription to `MemoryProvider` if there is one. */
|
|
32
|
-
stop(): void;
|
|
33
|
-
private _stop;
|
|
34
|
-
[Symbol.dispose](): void;
|
|
35
27
|
}
|
package/db/ItemStore.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { BooleanStore } from "../store/BooleanStore.js";
|
|
2
|
-
import {
|
|
3
|
-
import { call } from "../util/callback.js";
|
|
2
|
+
import { LazyStore } from "../store/LazyStore.js";
|
|
4
3
|
import { NONE } from "../util/constants.js";
|
|
4
|
+
import { BLACKHOLE } from "../util/function.js";
|
|
5
5
|
import { getItem } from "../util/item.js";
|
|
6
6
|
import { getRequired } from "../util/optional.js";
|
|
7
7
|
import { runSequence } from "../util/sequence.js";
|
|
8
8
|
import { getOptionalSource } from "../util/source.js";
|
|
9
9
|
import { CacheProvider } from "./CacheProvider.js";
|
|
10
10
|
/** Store a single item. */
|
|
11
|
-
export class ItemStore extends
|
|
11
|
+
export class ItemStore extends LazyStore {
|
|
12
12
|
provider;
|
|
13
13
|
collection;
|
|
14
14
|
id;
|
|
@@ -26,15 +26,15 @@ export class ItemStore extends Store {
|
|
|
26
26
|
return !!this.value;
|
|
27
27
|
}
|
|
28
28
|
constructor(provider, collection, id) {
|
|
29
|
-
const
|
|
30
|
-
const time =
|
|
31
|
-
const value =
|
|
32
|
-
super(value, time);
|
|
29
|
+
const cache = getOptionalSource(CacheProvider, provider);
|
|
30
|
+
const time = cache?.getCachedItemTime(collection, id);
|
|
31
|
+
const value = cache && typeof time === "number" ? cache.getCachedItem(collection, id) : NONE; // Use the value in the cache if it's cached, or use mark this store as loading otherwise (which will trigger `refresh()` below).
|
|
32
|
+
super(store => (cache ? runSequence(store.through(cache.getCachedItemSequence(collection, id))) : BLACKHOLE), value, time);
|
|
33
33
|
this.provider = provider;
|
|
34
34
|
this.collection = collection;
|
|
35
35
|
this.id = id;
|
|
36
|
-
//
|
|
37
|
-
if (
|
|
36
|
+
// Start loading the value from the provider if it doesn't exist.
|
|
37
|
+
if (this.loading)
|
|
38
38
|
this.refresh();
|
|
39
39
|
}
|
|
40
40
|
/** Refresh this store from the source provider. */
|
|
@@ -64,36 +64,4 @@ export class ItemStore extends Store {
|
|
|
64
64
|
connect(provider = this.provider) {
|
|
65
65
|
return runSequence(this.through(provider.getItemSequence(this.collection, this.id)));
|
|
66
66
|
}
|
|
67
|
-
// Override to subscribe to `MemoryProvider` while things are iterating over this store.
|
|
68
|
-
async *[Symbol.asyncIterator]() {
|
|
69
|
-
this.start();
|
|
70
|
-
this._iterating++;
|
|
71
|
-
try {
|
|
72
|
-
yield* super[Symbol.asyncIterator]();
|
|
73
|
-
}
|
|
74
|
-
finally {
|
|
75
|
-
this._iterating--;
|
|
76
|
-
if (this._iterating < 1)
|
|
77
|
-
this.stop();
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
_iterating = 0;
|
|
81
|
-
/** Start subscription to `MemoryProvider` if there is one. */
|
|
82
|
-
start() {
|
|
83
|
-
if (!this._stop) {
|
|
84
|
-
const table = getOptionalSource(CacheProvider, this.provider)?.memory.getTable(this.collection);
|
|
85
|
-
if (table)
|
|
86
|
-
this._stop = runSequence(this.through(table.getCachedItemSequence(this.id)));
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
/** Stop subscription to `MemoryProvider` if there is one. */
|
|
90
|
-
stop() {
|
|
91
|
-
if (this._stop)
|
|
92
|
-
this._stop = void call(this._stop);
|
|
93
|
-
}
|
|
94
|
-
_stop = undefined;
|
|
95
|
-
// Implement `Disposable`
|
|
96
|
-
[Symbol.dispose]() {
|
|
97
|
-
this.stop();
|
|
98
|
-
}
|
|
99
67
|
}
|
package/db/MemoryProvider.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ export declare class MemoryProvider<T extends Database> implements Provider<T> {
|
|
|
16
16
|
getItemTime<K extends DataKey<T>>(collection: K, id: string): number | undefined;
|
|
17
17
|
getItem<K extends DataKey<T>>(collection: K, id: string): OptionalItem<T[K]>;
|
|
18
18
|
getItemSequence<K extends DataKey<T>>(collection: K, id: string): AsyncIterable<OptionalItem<T[K]>>;
|
|
19
|
+
getCachedItemSequence<K extends DataKey<T>>(collection: K, id: string): AsyncIterable<OptionalItem<T[K]>>;
|
|
19
20
|
addItem<K extends DataKey<T>>(collection: K, data: T[K]): string;
|
|
20
21
|
setItem<K extends DataKey<T>>(collection: K, id: string, data: T[K]): void;
|
|
21
22
|
setItemSequence<K extends DataKey<T>>(collection: K, id: string, sequence: AsyncIterable<OptionalItem<T[K]>>): AsyncIterable<OptionalItem<T[K]>>;
|
|
@@ -25,6 +26,7 @@ export declare class MemoryProvider<T extends Database> implements Provider<T> {
|
|
|
25
26
|
countQuery<K extends DataKey<T>>(collection: K, query?: ItemQuery<T[K]>): number;
|
|
26
27
|
getQuery<K extends DataKey<T>>(collection: K, query?: ItemQuery<T[K]>): Items<T[K]>;
|
|
27
28
|
getQuerySequence<K extends DataKey<T>>(collection: K, query?: ItemQuery<T[K]>): AsyncIterable<Items<T[K]>>;
|
|
29
|
+
getCachedQuerySequence<K extends DataKey<T>>(collection: K, query?: ItemQuery<T[K]>): AsyncIterable<Items<T[K]>>;
|
|
28
30
|
setQuery<K extends DataKey<T>>(collection: K, query: ItemQuery<T[K]>, data: T[K]): void;
|
|
29
31
|
updateQuery<K extends DataKey<T>>(collection: K, query: ItemQuery<T[K]>, updates: Updates<T[K]>): void;
|
|
30
32
|
deleteQuery<K extends DataKey<T>>(collection: K, query: ItemQuery<T[K]>): void;
|
package/db/MemoryProvider.js
CHANGED
|
@@ -27,6 +27,9 @@ export class MemoryProvider {
|
|
|
27
27
|
getItemSequence(collection, id) {
|
|
28
28
|
return this.getTable(collection).getItemSequence(id);
|
|
29
29
|
}
|
|
30
|
+
getCachedItemSequence(collection, id) {
|
|
31
|
+
return this.getTable(collection).getCachedItemSequence(id);
|
|
32
|
+
}
|
|
30
33
|
addItem(collection, data) {
|
|
31
34
|
return this.getTable(collection).addItem(data);
|
|
32
35
|
}
|
|
@@ -54,6 +57,9 @@ export class MemoryProvider {
|
|
|
54
57
|
getQuerySequence(collection, query) {
|
|
55
58
|
return this.getTable(collection).getQuerySequence(query);
|
|
56
59
|
}
|
|
60
|
+
getCachedQuerySequence(collection, query) {
|
|
61
|
+
return this.getTable(collection).getCachedQuerySequence(query);
|
|
62
|
+
}
|
|
57
63
|
setQuery(collection, query, data) {
|
|
58
64
|
return this.getTable(collection).setQuery(query, data);
|
|
59
65
|
}
|
package/db/QueryStore.d.ts
CHANGED
|
@@ -3,9 +3,9 @@ import type { StopCallback } from "../util/callback.js";
|
|
|
3
3
|
import type { DataKey, Database } from "../util/data.js";
|
|
4
4
|
import type { Item, ItemQuery, Items, OptionalItem } from "../util/item.js";
|
|
5
5
|
import { BooleanStore } from "../store/BooleanStore.js";
|
|
6
|
-
import {
|
|
6
|
+
import { LazyStore } from "../store/LazyStore.js";
|
|
7
7
|
/** Store a set of multiple items. */
|
|
8
|
-
export declare class QueryStore<T extends Database, K extends DataKey<T>> extends
|
|
8
|
+
export declare class QueryStore<T extends Database, K extends DataKey<T>> extends LazyStore<Items<T[K]>> implements Iterable<Item<T[K]>> {
|
|
9
9
|
readonly provider: AbstractProvider<T>;
|
|
10
10
|
readonly collection: K;
|
|
11
11
|
readonly query: ItemQuery<T[K]>;
|
|
@@ -40,13 +40,5 @@ export declare class QueryStore<T extends Database, K extends DataKey<T>> extend
|
|
|
40
40
|
*/
|
|
41
41
|
loadMore(): void;
|
|
42
42
|
private _loadMore;
|
|
43
|
-
[Symbol.asyncIterator](): AsyncGenerator<Items<T[K]>, void, void>;
|
|
44
|
-
private _iterating;
|
|
45
|
-
/** Start subscription to `MemoryProvider` if there is one. */
|
|
46
|
-
start(): void;
|
|
47
|
-
/** Stop subscription to `MemoryProvider` if there is one. */
|
|
48
|
-
stop(): void;
|
|
49
|
-
private _stop;
|
|
50
|
-
[Symbol.dispose](): void;
|
|
51
43
|
[Symbol.iterator](): Iterator<Item<T[K]>>;
|
|
52
44
|
}
|
package/db/QueryStore.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { BooleanStore } from "../store/BooleanStore.js";
|
|
2
|
-
import {
|
|
2
|
+
import { LazyStore } from "../store/LazyStore.js";
|
|
3
3
|
import { getFirstItem, getLastItem, getOptionalFirstItem, getOptionalLastItem } from "../util/array.js";
|
|
4
|
-
import { call } from "../util/callback.js";
|
|
5
4
|
import { NONE } from "../util/constants.js";
|
|
5
|
+
import { BLACKHOLE } from "../util/function.js";
|
|
6
6
|
import { getAfterQuery, getLimit } from "../util/query.js";
|
|
7
7
|
import { runSequence } from "../util/sequence.js";
|
|
8
8
|
import { getOptionalSource } from "../util/source.js";
|
|
9
9
|
import { CacheProvider } from "./CacheProvider.js";
|
|
10
10
|
/** Store a set of multiple items. */
|
|
11
|
-
export class QueryStore extends
|
|
11
|
+
export class QueryStore extends LazyStore {
|
|
12
12
|
provider;
|
|
13
13
|
collection;
|
|
14
14
|
query;
|
|
@@ -44,16 +44,16 @@ export class QueryStore extends Store {
|
|
|
44
44
|
return this.value.length;
|
|
45
45
|
}
|
|
46
46
|
constructor(provider, collection, query) {
|
|
47
|
-
const
|
|
48
|
-
const time =
|
|
49
|
-
const value =
|
|
50
|
-
super(value, time);
|
|
47
|
+
const cache = getOptionalSource(CacheProvider, provider);
|
|
48
|
+
const time = cache?.getCachedQueryTime(collection, query);
|
|
49
|
+
const value = cache?.getCachedQuery(collection, query) || NONE; // Always use any matching items currently in the cache (this might update when we call `refresh()` below).
|
|
50
|
+
super(store => (cache ? runSequence(store.through(cache.getCachedQuerySequence(collection, query))) : BLACKHOLE), value, time);
|
|
51
51
|
this.provider = provider;
|
|
52
52
|
this.collection = collection;
|
|
53
53
|
this.query = query;
|
|
54
54
|
this.limit = getLimit(query) ?? Infinity;
|
|
55
|
-
//
|
|
56
|
-
if (
|
|
55
|
+
// Start loading the value from the provider if it doesn't exist.
|
|
56
|
+
if (this.loading)
|
|
57
57
|
this.refresh();
|
|
58
58
|
}
|
|
59
59
|
/** Refresh this store from the source provider. */
|
|
@@ -110,38 +110,6 @@ export class QueryStore extends Store {
|
|
|
110
110
|
this.busy.value = false;
|
|
111
111
|
}
|
|
112
112
|
}
|
|
113
|
-
// Override to subscribe to `MemoryProvider` while things are iterating over this store.
|
|
114
|
-
async *[Symbol.asyncIterator]() {
|
|
115
|
-
this.start();
|
|
116
|
-
this._iterating++;
|
|
117
|
-
try {
|
|
118
|
-
yield* super[Symbol.asyncIterator]();
|
|
119
|
-
}
|
|
120
|
-
finally {
|
|
121
|
-
this._iterating--;
|
|
122
|
-
if (this._iterating < 1)
|
|
123
|
-
this.stop();
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
_iterating = 0;
|
|
127
|
-
/** Start subscription to `MemoryProvider` if there is one. */
|
|
128
|
-
start() {
|
|
129
|
-
if (!this._stop) {
|
|
130
|
-
const table = getOptionalSource(CacheProvider, this.provider)?.memory.getTable(this.collection);
|
|
131
|
-
if (table)
|
|
132
|
-
this._stop = runSequence(this.through(table.getCachedQuerySequence(this.query)));
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
/** Stop subscription to `MemoryProvider` if there is one. */
|
|
136
|
-
stop() {
|
|
137
|
-
if (this._stop)
|
|
138
|
-
this._stop = void call(this._stop);
|
|
139
|
-
}
|
|
140
|
-
_stop = undefined;
|
|
141
|
-
// Implement `Disposable`
|
|
142
|
-
[Symbol.dispose]() {
|
|
143
|
-
this.stop();
|
|
144
|
-
}
|
|
145
113
|
// Implement `Iteratable`
|
|
146
114
|
[Symbol.iterator]() {
|
|
147
115
|
return this.value.values();
|
|
@@ -78,7 +78,8 @@ export class FirestoreServerProvider {
|
|
|
78
78
|
getItemSequence(c, id) {
|
|
79
79
|
const ref = _getCollection(this._firestore, c).doc(id);
|
|
80
80
|
return new LazyDeferredSequence(sequence => ref.onSnapshot(snapshot => sequence.resolve(_getOptionalItem(snapshot)), //
|
|
81
|
-
|
|
81
|
+
//
|
|
82
|
+
reason => sequence.reject(reason)));
|
|
82
83
|
}
|
|
83
84
|
async addItem(c, data) {
|
|
84
85
|
return (await _getCollection(this._firestore, c).add(data)).id;
|
|
@@ -102,7 +103,8 @@ export class FirestoreServerProvider {
|
|
|
102
103
|
getQuerySequence(c, q) {
|
|
103
104
|
const ref = _getQuery(this._firestore, c, q);
|
|
104
105
|
return new LazyDeferredSequence(sequence => ref.onSnapshot(snapshot => sequence.resolve(_getItemArray(snapshot)), //
|
|
105
|
-
|
|
106
|
+
//
|
|
107
|
+
reason => sequence.reject(reason)));
|
|
106
108
|
}
|
|
107
109
|
async setQuery(c, q, data) {
|
|
108
110
|
return await bulkWrite(this._firestore, c, q, (w, s) => void w.set(s.ref, data));
|
package/markup/rule.d.ts
CHANGED
|
@@ -27,22 +27,37 @@ export interface MarkupRule {
|
|
|
27
27
|
/** Match an input string against this and return an element if there was a match. */
|
|
28
28
|
match(input: string, options: MarkupOptions): MarkupElement | undefined;
|
|
29
29
|
}
|
|
30
|
-
export declare
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
30
|
+
export declare class RegExpMarkupRule<T extends string = string> implements MarkupRule {
|
|
31
|
+
readonly regexp: TypedRegExp<T>;
|
|
32
|
+
readonly render: (props: TypedRegExpExecArray<T>, options: MarkupOptions) => JSXElement;
|
|
33
|
+
readonly contexts: ImmutableArray<string>;
|
|
34
|
+
readonly priority?: number | undefined;
|
|
35
|
+
constructor(regexp: TypedRegExp<T>, //
|
|
36
|
+
render: (props: TypedRegExpExecArray<T>, options: MarkupOptions) => JSXElement, contexts: ImmutableArray<string>, priority?: number | undefined);
|
|
37
|
+
match(input: string, options: MarkupOptions): MarkupElement | undefined;
|
|
38
|
+
}
|
|
39
|
+
export declare class NamedRegExpMarkupRule<T extends NamedRegExpData> implements MarkupRule {
|
|
40
|
+
readonly regexp: NamedRegExp<T>;
|
|
41
|
+
readonly render: (data: T, options: MarkupOptions) => JSXElement;
|
|
42
|
+
readonly contexts: ImmutableArray<string>;
|
|
43
|
+
readonly priority?: number | undefined;
|
|
44
|
+
constructor(regexp: NamedRegExp<T>, //
|
|
45
|
+
render: (data: T, options: MarkupOptions) => JSXElement, contexts: ImmutableArray<string>, priority?: number | undefined);
|
|
46
|
+
match(input: string, options: MarkupOptions): MarkupElement | undefined;
|
|
47
|
+
}
|
|
48
|
+
export declare class LinkRegExpMarkupRule implements MarkupRule {
|
|
49
|
+
readonly regexp: NamedRegExp<{
|
|
44
50
|
title?: string;
|
|
45
51
|
href: string;
|
|
46
52
|
}>;
|
|
47
|
-
|
|
53
|
+
readonly render: (title: string, href: string, options: MarkupOptions) => JSXElement;
|
|
54
|
+
readonly contexts: ImmutableArray<string>;
|
|
55
|
+
readonly priority?: number | undefined;
|
|
56
|
+
constructor(regexp: NamedRegExp<{
|
|
57
|
+
title?: string;
|
|
58
|
+
href: string;
|
|
59
|
+
}>, //
|
|
60
|
+
render: (title: string, href: string, options: MarkupOptions) => JSXElement, contexts: ImmutableArray<string>, priority?: number | undefined);
|
|
61
|
+
match(input: string, options: MarkupOptions): MarkupElement | undefined;
|
|
62
|
+
}
|
|
48
63
|
export type MarkupRules = MarkupRule[];
|
package/markup/rule.js
CHANGED
|
@@ -1,49 +1,67 @@
|
|
|
1
1
|
import { formatURL, getOptionalURL } from "../util/url.js";
|
|
2
|
-
export
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
2
|
+
export class RegExpMarkupRule {
|
|
3
|
+
regexp;
|
|
4
|
+
render;
|
|
5
|
+
contexts;
|
|
6
|
+
priority;
|
|
7
|
+
constructor(regexp, //
|
|
8
|
+
render, contexts, priority) {
|
|
9
|
+
this.regexp = regexp;
|
|
10
|
+
this.render = render;
|
|
11
|
+
this.contexts = contexts;
|
|
12
|
+
this.priority = priority;
|
|
13
|
+
//
|
|
14
|
+
}
|
|
15
|
+
match(input, options) {
|
|
16
|
+
const match = this.regexp.exec(input);
|
|
17
|
+
if (match) {
|
|
18
|
+
const { index, 0: { length }, } = match;
|
|
19
|
+
return { index, length, ...this.render(match, options) };
|
|
20
|
+
}
|
|
21
|
+
}
|
|
16
22
|
}
|
|
17
|
-
export
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
23
|
+
export class NamedRegExpMarkupRule {
|
|
24
|
+
regexp;
|
|
25
|
+
render;
|
|
26
|
+
contexts;
|
|
27
|
+
priority;
|
|
28
|
+
constructor(regexp, //
|
|
29
|
+
render, contexts, priority) {
|
|
30
|
+
this.regexp = regexp;
|
|
31
|
+
this.render = render;
|
|
32
|
+
this.contexts = contexts;
|
|
33
|
+
this.priority = priority;
|
|
34
|
+
//
|
|
35
|
+
}
|
|
36
|
+
match(input, options) {
|
|
37
|
+
const match = this.regexp.exec(input);
|
|
38
|
+
if (match) {
|
|
39
|
+
const { index, 0: { length }, groups, } = match;
|
|
40
|
+
return { index, length, ...this.render(groups, options) };
|
|
41
|
+
}
|
|
42
|
+
}
|
|
31
43
|
}
|
|
32
|
-
export
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
44
|
+
export class LinkRegExpMarkupRule {
|
|
45
|
+
regexp;
|
|
46
|
+
render;
|
|
47
|
+
contexts;
|
|
48
|
+
priority;
|
|
49
|
+
constructor(regexp, //
|
|
50
|
+
render, contexts, priority) {
|
|
51
|
+
this.regexp = regexp;
|
|
52
|
+
this.render = render;
|
|
53
|
+
this.contexts = contexts;
|
|
54
|
+
this.priority = priority;
|
|
55
|
+
//
|
|
56
|
+
}
|
|
57
|
+
match(input, options) {
|
|
58
|
+
const match = this.regexp.exec(input);
|
|
59
|
+
if (match) {
|
|
60
|
+
const { schemes, url: base } = options;
|
|
61
|
+
const { 0: { length }, index, groups: { href, title }, } = match;
|
|
62
|
+
const url = getOptionalURL(href, base);
|
|
63
|
+
if (url && schemes.includes(url.protocol))
|
|
64
|
+
return { index, length, ...this.render(title?.trim() || formatURL(url), url.href, options) };
|
|
65
|
+
}
|
|
66
|
+
}
|
|
49
67
|
}
|
package/markup/rules.d.ts
CHANGED
|
@@ -1,18 +1,16 @@
|
|
|
1
1
|
import type { MarkupOptions } from "./options.js";
|
|
2
2
|
import type { MarkupRules } from "./rule.js";
|
|
3
|
-
import
|
|
3
|
+
import { LinkRegExpMarkupRule, NamedRegExpMarkupRule, RegExpMarkupRule } from "./rule.js";
|
|
4
4
|
/**
|
|
5
5
|
* Headings are single line only (don't allow multiline).
|
|
6
6
|
* - 1-6 hashes then 1+ spaces, then the title.
|
|
7
7
|
* - Same as Markdown syntax.
|
|
8
8
|
* - Markdown's underline syntax is not supported (for simplification).
|
|
9
9
|
*/
|
|
10
|
-
export declare const HEADING_RULE:
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}>;
|
|
15
|
-
};
|
|
10
|
+
export declare const HEADING_RULE: NamedRegExpMarkupRule<{
|
|
11
|
+
prefix: string;
|
|
12
|
+
heading: string;
|
|
13
|
+
}>;
|
|
16
14
|
/**
|
|
17
15
|
* Separator (horizontal rule / thematic break).
|
|
18
16
|
* - Same as Markdown syntax but also allows `•` bullet character (in addition to `-` dash, `+` plus, `*` asterisk, `_` underscore).
|
|
@@ -20,24 +18,16 @@ export declare const HEADING_RULE: import("./rule.js").MarkupRule & {
|
|
|
20
18
|
* - Character must be the same every time (can't mix)
|
|
21
19
|
* - Might have infinite number of spaces between the characters.
|
|
22
20
|
*/
|
|
23
|
-
export declare const SEPARATOR_RULE:
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
list: string;
|
|
34
|
-
}>;
|
|
35
|
-
};
|
|
36
|
-
export declare const BLOCKQUOTE_RULE: import("./rule.js").MarkupRule & {
|
|
37
|
-
regexp: NamedRegExp<{
|
|
38
|
-
quote: string;
|
|
39
|
-
}>;
|
|
40
|
-
};
|
|
21
|
+
export declare const SEPARATOR_RULE: RegExpMarkupRule<string>;
|
|
22
|
+
export declare const UNORDERED_RULE: NamedRegExpMarkupRule<{
|
|
23
|
+
list: string;
|
|
24
|
+
}>;
|
|
25
|
+
export declare const ORDERED_RULE: NamedRegExpMarkupRule<{
|
|
26
|
+
list: string;
|
|
27
|
+
}>;
|
|
28
|
+
export declare const BLOCKQUOTE_RULE: NamedRegExpMarkupRule<{
|
|
29
|
+
quote: string;
|
|
30
|
+
}>;
|
|
41
31
|
/**
|
|
42
32
|
* Fenced code blocks
|
|
43
33
|
* - Same as Markdown syntax.
|
|
@@ -45,22 +35,18 @@ export declare const BLOCKQUOTE_RULE: import("./rule.js").MarkupRule & {
|
|
|
45
35
|
* - If there's no closing fence the code block will run to the end of the current string.
|
|
46
36
|
* - Markdown-style four-space indent syntax is not supported (only fenced code, since it's easier to use).
|
|
47
37
|
*/
|
|
48
|
-
export declare const FENCED_CODE_RULE:
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}>;
|
|
54
|
-
};
|
|
38
|
+
export declare const FENCED_CODE_RULE: NamedRegExpMarkupRule<{
|
|
39
|
+
wrap: string;
|
|
40
|
+
title?: string;
|
|
41
|
+
code: string;
|
|
42
|
+
}>;
|
|
55
43
|
/**
|
|
56
44
|
* Paragraph.
|
|
57
45
|
* - When ordering rules, paragraph should go after other "block" context elements (because it has a very generous capture).
|
|
58
46
|
*/
|
|
59
|
-
export declare const PARAGRAPH_RULE:
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
}>;
|
|
63
|
-
};
|
|
47
|
+
export declare const PARAGRAPH_RULE: NamedRegExpMarkupRule<{
|
|
48
|
+
paragraph: string;
|
|
49
|
+
}>;
|
|
64
50
|
/** Render function for URL and LINK rules. */
|
|
65
51
|
export declare function renderLinkRule(title: string, href: string, { rel }: MarkupOptions): {
|
|
66
52
|
type: string;
|
|
@@ -81,12 +67,7 @@ export declare function renderLinkRule(title: string, href: string, { rel }: Mar
|
|
|
81
67
|
* - If link is not valid (using `new URL(url)` then unparsed text will be returned.
|
|
82
68
|
* - For security only schemes that appear in `options.schemes` will match (defaults to `http:` and `https:`).
|
|
83
69
|
*/
|
|
84
|
-
export declare const URL_RULE:
|
|
85
|
-
regexp: NamedRegExp<{
|
|
86
|
-
title?: string;
|
|
87
|
-
href: string;
|
|
88
|
-
}>;
|
|
89
|
-
};
|
|
70
|
+
export declare const URL_RULE: LinkRegExpMarkupRule;
|
|
90
71
|
/**
|
|
91
72
|
* Markdown-style link.
|
|
92
73
|
* - Link in standard Markdown format, e.g. `[Google Maps](http://google.com/maps)`
|
|
@@ -95,12 +76,7 @@ export declare const URL_RULE: import("./rule.js").MarkupRule & {
|
|
|
95
76
|
* - If link is not valid (using `new URL(url)` then unparsed text will be returned.
|
|
96
77
|
* - For security only `http://` or `https://` links will work (if invalid the unparsed text will be returned).
|
|
97
78
|
*/
|
|
98
|
-
export declare const LINK_RULE:
|
|
99
|
-
regexp: NamedRegExp<{
|
|
100
|
-
title?: string;
|
|
101
|
-
href: string;
|
|
102
|
-
}>;
|
|
103
|
-
};
|
|
79
|
+
export declare const LINK_RULE: LinkRegExpMarkupRule;
|
|
104
80
|
/**
|
|
105
81
|
* Inline code.
|
|
106
82
|
* - Text surrounded by one or more "`" backtick tilde characters.
|
|
@@ -108,11 +84,9 @@ export declare const LINK_RULE: import("./rule.js").MarkupRule & {
|
|
|
108
84
|
* - Closing characters must exactly match opening characters.
|
|
109
85
|
* - Same as Markdown syntax.
|
|
110
86
|
*/
|
|
111
|
-
export declare const CODE_RULE:
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
}>;
|
|
115
|
-
};
|
|
87
|
+
export declare const CODE_RULE: NamedRegExpMarkupRule<{
|
|
88
|
+
code: string;
|
|
89
|
+
}>;
|
|
116
90
|
/**
|
|
117
91
|
* Inline strong, emphasis, insert, delete, highlight.
|
|
118
92
|
* - Inline strong text wrapped in one or more `*` asterisks.
|
|
@@ -135,13 +109,11 @@ declare const INLINE_CHARS: {
|
|
|
135
109
|
"=": string;
|
|
136
110
|
":": string;
|
|
137
111
|
};
|
|
138
|
-
export declare const INLINE_RULE:
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
}>;
|
|
144
|
-
};
|
|
112
|
+
export declare const INLINE_RULE: NamedRegExpMarkupRule<{
|
|
113
|
+
char: keyof typeof INLINE_CHARS;
|
|
114
|
+
wrap: string;
|
|
115
|
+
text: string;
|
|
116
|
+
}>;
|
|
145
117
|
/**
|
|
146
118
|
* Hard linebreak (`<br />` tag).
|
|
147
119
|
* - Any line break in a paragraph will become a hard `<br />` tag.
|
|
@@ -150,9 +122,7 @@ export declare const INLINE_RULE: import("./rule.js").MarkupRule & {
|
|
|
150
122
|
* - This is more intuitive (a linebreak becomes a linebreak is isn't silently ignored).
|
|
151
123
|
* - This works better with textareas that wrap text (since manually breaking up long lines is no longer necessary).
|
|
152
124
|
*/
|
|
153
|
-
export declare const LINEBREAK_RULE:
|
|
154
|
-
regexp: import("../util/regexp.js").TypedRegExp<string>;
|
|
155
|
-
};
|
|
125
|
+
export declare const LINEBREAK_RULE: RegExpMarkupRule<string>;
|
|
156
126
|
/**
|
|
157
127
|
* All markup rules.
|
|
158
128
|
* - Syntax parsed by `renderMarkup()` is defined entirely by the list of rules (i.e. not by code).
|
package/markup/rules.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { getRegExp } from "../util/regexp.js";
|
|
2
2
|
import { BLOCK_REGEXP, LINE_REGEXP, WordRegExp, getBlockRegExp, getLineRegExp } from "./regexp.js";
|
|
3
|
-
import {
|
|
3
|
+
import { LinkRegExpMarkupRule, NamedRegExpMarkupRule, RegExpMarkupRule } from "./rule.js";
|
|
4
4
|
/** React security symbol — see https://github.com/facebook/react/pull/4832 */
|
|
5
5
|
const $$typeof = Symbol.for("react.element");
|
|
6
6
|
/**
|
|
@@ -9,7 +9,7 @@ const $$typeof = Symbol.for("react.element");
|
|
|
9
9
|
* - Same as Markdown syntax.
|
|
10
10
|
* - Markdown's underline syntax is not supported (for simplification).
|
|
11
11
|
*/
|
|
12
|
-
export const HEADING_RULE =
|
|
12
|
+
export const HEADING_RULE = new NamedRegExpMarkupRule(getLineRegExp(`(?<prefix>#{1,6}) +(?<heading>${LINE_REGEXP.source})`), ({ prefix, heading }) => ({
|
|
13
13
|
type: `h${prefix.length}`,
|
|
14
14
|
key: null,
|
|
15
15
|
ref: null,
|
|
@@ -24,7 +24,7 @@ export const HEADING_RULE = getNamedMarkupRule(getLineRegExp(`(?<prefix>#{1,6})
|
|
|
24
24
|
* - Character must be the same every time (can't mix)
|
|
25
25
|
* - Might have infinite number of spaces between the characters.
|
|
26
26
|
*/
|
|
27
|
-
export const SEPARATOR_RULE =
|
|
27
|
+
export const SEPARATOR_RULE = new RegExpMarkupRule(getLineRegExp("([-*•+_=])(?: *\\1){2,}"), () => ({
|
|
28
28
|
type: "hr",
|
|
29
29
|
key: null,
|
|
30
30
|
ref: null,
|
|
@@ -41,7 +41,7 @@ export const SEPARATOR_RULE = getMarkupRule(getLineRegExp("([-*•+_=])(?: *\\1)
|
|
|
41
41
|
const UNORDERED_PREFIX = "[-*•+] +";
|
|
42
42
|
const UNORDERED_SPLIT = new RegExp(`(?:^|\n)+${UNORDERED_PREFIX}`, "g");
|
|
43
43
|
const UNORDERED_INDENT = /^\t/gm;
|
|
44
|
-
export const UNORDERED_RULE =
|
|
44
|
+
export const UNORDERED_RULE = new NamedRegExpMarkupRule(getBlockRegExp(`(?<list>${UNORDERED_PREFIX}${BLOCK_REGEXP.source})`), ({ list }) => ({
|
|
45
45
|
type: "ul",
|
|
46
46
|
key: null,
|
|
47
47
|
ref: null,
|
|
@@ -64,7 +64,7 @@ const _mapUnordered = (item, key) => ({
|
|
|
64
64
|
const ORDERED_PREFIX = "[1-9][0-9]{0,8}[.):] +"; // Number for a numbered list, e.g. `1.` or `2)` or `3:`
|
|
65
65
|
const ORDERED_SPLIT = new RegExp(`\n+(?=${ORDERED_PREFIX})`, "g");
|
|
66
66
|
const ORDERED_INDENT = UNORDERED_INDENT;
|
|
67
|
-
export const ORDERED_RULE =
|
|
67
|
+
export const ORDERED_RULE = new NamedRegExpMarkupRule(getBlockRegExp(`(?<list>${ORDERED_PREFIX}${BLOCK_REGEXP.source})`), ({ list }) => ({
|
|
68
68
|
type: "ol",
|
|
69
69
|
key: null,
|
|
70
70
|
ref: null,
|
|
@@ -93,7 +93,7 @@ const _mapOrdered = (item, key) => ({
|
|
|
93
93
|
*/
|
|
94
94
|
const BLOCKQUOTE_PREFIX = "> *";
|
|
95
95
|
const BLOCKQUOTE_INDENT = new RegExp(`^${BLOCKQUOTE_PREFIX}`, "gm");
|
|
96
|
-
export const BLOCKQUOTE_RULE =
|
|
96
|
+
export const BLOCKQUOTE_RULE = new NamedRegExpMarkupRule(getLineRegExp(`(?<quote>${BLOCKQUOTE_PREFIX}${LINE_REGEXP.source}(?:\n${BLOCKQUOTE_PREFIX}${LINE_REGEXP.source})*)`), ({ quote }) => ({
|
|
97
97
|
type: "blockquote",
|
|
98
98
|
key: null,
|
|
99
99
|
ref: null,
|
|
@@ -108,7 +108,7 @@ export const BLOCKQUOTE_RULE = getNamedMarkupRule(getLineRegExp(`(?<quote>${BLOC
|
|
|
108
108
|
* - If there's no closing fence the code block will run to the end of the current string.
|
|
109
109
|
* - Markdown-style four-space indent syntax is not supported (only fenced code, since it's easier to use).
|
|
110
110
|
*/
|
|
111
|
-
export const FENCED_CODE_RULE =
|
|
111
|
+
export const FENCED_CODE_RULE = new NamedRegExpMarkupRule(
|
|
112
112
|
// Matcher has its own end that only stops when it reaches a matching closing fence or the end of the string.
|
|
113
113
|
getLineRegExp(`(?<wrap>\`{3,}|~{3,}) *(?<title>${LINE_REGEXP.source})\n(?<code>${BLOCK_REGEXP.source})`, `(?:\n\\k<wrap>|$)`), ({ title, code }) => ({
|
|
114
114
|
type: "pre",
|
|
@@ -129,7 +129,7 @@ getLineRegExp(`(?<wrap>\`{3,}|~{3,}) *(?<title>${LINE_REGEXP.source})\n(?<code>$
|
|
|
129
129
|
* Paragraph.
|
|
130
130
|
* - When ordering rules, paragraph should go after other "block" context elements (because it has a very generous capture).
|
|
131
131
|
*/
|
|
132
|
-
export const PARAGRAPH_RULE =
|
|
132
|
+
export const PARAGRAPH_RULE = new NamedRegExpMarkupRule(getBlockRegExp(`(?<paragraph>${BLOCK_REGEXP.source})`), ({ paragraph }) => ({
|
|
133
133
|
type: `p`,
|
|
134
134
|
key: null,
|
|
135
135
|
ref: null,
|
|
@@ -155,7 +155,7 @@ export function renderLinkRule(title, href, { rel }) {
|
|
|
155
155
|
* - If link is not valid (using `new URL(url)` then unparsed text will be returned.
|
|
156
156
|
* - For security only schemes that appear in `options.schemes` will match (defaults to `http:` and `https:`).
|
|
157
157
|
*/
|
|
158
|
-
export const URL_RULE =
|
|
158
|
+
export const URL_RULE = new LinkRegExpMarkupRule(getRegExp(/(?<href>[a-z]+:[-$_@.&!*,=;/#?:%a-zA-Z0-9]+)(?: +(?:\((?<title>[^)]*?)\)))?/), //
|
|
159
159
|
renderLinkRule, ["inline", "list"]);
|
|
160
160
|
/**
|
|
161
161
|
* Markdown-style link.
|
|
@@ -165,7 +165,7 @@ renderLinkRule, ["inline", "list"]);
|
|
|
165
165
|
* - If link is not valid (using `new URL(url)` then unparsed text will be returned.
|
|
166
166
|
* - For security only `http://` or `https://` links will work (if invalid the unparsed text will be returned).
|
|
167
167
|
*/
|
|
168
|
-
export const LINK_RULE =
|
|
168
|
+
export const LINK_RULE = new LinkRegExpMarkupRule(getRegExp(/\[(?<title>[^\]]*?)\]\((?<href>[^)]*?)\)/), //
|
|
169
169
|
renderLinkRule, ["inline", "list"]);
|
|
170
170
|
/**
|
|
171
171
|
* Inline code.
|
|
@@ -174,7 +174,7 @@ renderLinkRule, ["inline", "list"]);
|
|
|
174
174
|
* - Closing characters must exactly match opening characters.
|
|
175
175
|
* - Same as Markdown syntax.
|
|
176
176
|
*/
|
|
177
|
-
export const CODE_RULE =
|
|
177
|
+
export const CODE_RULE = new NamedRegExpMarkupRule(new RegExp(`(?<wrap>\`+)(?<code>${BLOCK_REGEXP.source})\\k<wrap>`), ({ code }) => ({
|
|
178
178
|
type: "code",
|
|
179
179
|
key: null,
|
|
180
180
|
ref: null,
|
|
@@ -195,7 +195,7 @@ export const CODE_RULE = getNamedMarkupRule(new RegExp(`(?<wrap>\`+)(?<code>${BL
|
|
|
195
195
|
* - Different to Markdown: strong is always surrounded by `*asterisks*` and emphasis is always surrounded by `_underscores_` (strong isn't 'double emphasis').
|
|
196
196
|
*/
|
|
197
197
|
const INLINE_CHARS = { "-": "del", "~": "del", "+": "ins", "*": "strong", "_": "em", "=": "mark", ":": "mark" }; // Hyphen must be first so it works when we use the keys as a character class.
|
|
198
|
-
export const INLINE_RULE =
|
|
198
|
+
export const INLINE_RULE = new NamedRegExpMarkupRule(new WordRegExp(`(?<wrap>(?<char>[${Object.keys(INLINE_CHARS).join("")}])+)(?<text>(?!\\k<char>)\\S|(?!\\k<char>)\\S[\\s\\S]*?(?!\\k<char>)\\S)\\k<wrap>`), // prettier-ignore
|
|
199
199
|
({ char, text }) => ({
|
|
200
200
|
type: INLINE_CHARS[char],
|
|
201
201
|
key: null,
|
|
@@ -212,7 +212,7 @@ export const INLINE_RULE = getNamedMarkupRule(new WordRegExp(`(?<wrap>(?<char>[$
|
|
|
212
212
|
* - This is more intuitive (a linebreak becomes a linebreak is isn't silently ignored).
|
|
213
213
|
* - This works better with textareas that wrap text (since manually breaking up long lines is no longer necessary).
|
|
214
214
|
*/
|
|
215
|
-
export const LINEBREAK_RULE =
|
|
215
|
+
export const LINEBREAK_RULE = new RegExpMarkupRule(/\n/, () => ({
|
|
216
216
|
type: "br",
|
|
217
217
|
key: null,
|
|
218
218
|
ref: null,
|
package/package.json
CHANGED
|
@@ -2,18 +2,27 @@
|
|
|
2
2
|
import type { AbstractProvider } from "../db/Provider.js";
|
|
3
3
|
import type { DataKey, Database } from "../util/data.js";
|
|
4
4
|
import type { ItemQuery } from "../util/item.js";
|
|
5
|
+
import type { Optional } from "../util/optional.js";
|
|
5
6
|
import { ItemStore } from "../db/ItemStore.js";
|
|
6
7
|
import { QueryStore } from "../db/QueryStore.js";
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
8
|
+
export interface DataContext<T extends Database> {
|
|
9
|
+
/** Get an `ItemStore` for the specified collection item in the current `DataProvider` context. */
|
|
10
|
+
useCacheItem<K extends DataKey<T>>(this: void, collection: K, id: string): ItemStore<T, K>;
|
|
11
|
+
useCacheItem<K extends DataKey<T>>(this: void, collection: Optional<K>, id: Optional<string>): ItemStore<T, K> | undefined;
|
|
12
|
+
/** Get an `QueryStore` for the specified collection query in the current `DataProvider` context. */
|
|
13
|
+
useCacheQuery<K extends DataKey<T>>(this: void, collection: K, query: ItemQuery<T[K]>): QueryStore<T, K>;
|
|
14
|
+
useCacheQuery<K extends DataKey<T>>(this: void, collection: Optional<K>, query: Optional<ItemQuery<T[K]>>): QueryStore<T, K> | undefined;
|
|
15
|
+
/** Get an `ItemStore` for the specified collection item in the current `DataProvider` context and subscribe to any changes in it. */
|
|
16
|
+
useItem<K extends DataKey<T>>(this: void, collection: K, id: string): ItemStore<T, K>;
|
|
17
|
+
useItem<K extends DataKey<T>>(this: void, collection: Optional<K>, id: Optional<string>): ItemStore<T, K> | undefined;
|
|
18
|
+
/** Get an `QueryStore` for the specified collection query in the current `DataProvider` context and subscribe to any changes in it. */
|
|
19
|
+
useQuery<K extends DataKey<T>>(this: void, collection: K, query: ItemQuery<T[K]>): QueryStore<T, K>;
|
|
20
|
+
useQuery<K extends DataKey<T>>(this: void, collection: Optional<K>, query: Optional<ItemQuery<T[K]>>): QueryStore<T, K> | undefined;
|
|
16
21
|
readonly DataProvider: ({ children }: {
|
|
17
22
|
children: React.ReactNode;
|
|
18
23
|
}) => React.ReactElement;
|
|
19
|
-
}
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Create a data context that can be provided to React elements and allows them to call `useItem()` and `useQuery()`
|
|
27
|
+
*/
|
|
28
|
+
export declare function createDataContext<T extends Database>(provider: AbstractProvider<T>): DataContext<T>;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { ItemStore } from "../db/ItemStore.js";
|
|
2
2
|
import { QueryStore } from "../db/QueryStore.js";
|
|
3
3
|
import { setMapItem } from "../util/map.js";
|
|
4
|
-
import { getRequired } from "../util/optional.js";
|
|
5
4
|
import { createCacheContext } from "./createCacheContext.js";
|
|
6
5
|
import { useStore } from "./useStore.js";
|
|
7
6
|
/**
|
|
@@ -9,21 +8,21 @@ import { useStore } from "./useStore.js";
|
|
|
9
8
|
*/
|
|
10
9
|
export function createDataContext(provider) {
|
|
11
10
|
const { CacheProvider, useCache } = createCacheContext(); // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
12
|
-
const
|
|
11
|
+
const useCacheItem = (collection, id) => {
|
|
13
12
|
const cache = useCache();
|
|
14
13
|
const key = collection && id && `${collection}/${id}`;
|
|
15
|
-
return
|
|
14
|
+
return key ? cache.get(key) || setMapItem(cache, key, new ItemStore(provider, collection, id)) : undefined;
|
|
16
15
|
};
|
|
17
|
-
const
|
|
16
|
+
const useCacheQuery = (collection, query) => {
|
|
18
17
|
const cache = useCache();
|
|
19
18
|
const key = collection && query && `${collection}?${JSON.stringify(query)}`;
|
|
20
|
-
return
|
|
19
|
+
return key ? cache.get(key) || setMapItem(cache, key, new QueryStore(provider, collection, query)) : undefined;
|
|
21
20
|
};
|
|
22
21
|
return {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
useItem: (collection, id) =>
|
|
26
|
-
useQuery: (collection, query) =>
|
|
22
|
+
useCacheItem,
|
|
23
|
+
useCacheQuery,
|
|
24
|
+
useItem: (collection, id) => useStore(useCacheItem(collection, id)),
|
|
25
|
+
useQuery: (collection, query) => useStore(useCacheQuery(collection, query)),
|
|
27
26
|
DataProvider: CacheProvider,
|
|
28
27
|
};
|
|
29
28
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { Deferred } from "../util/async.js";
|
|
2
|
-
import type { ErrorCallback, ValueCallback } from "../util/callback.js";
|
|
3
2
|
import { AbstractSequence } from "./AbstractSequence.js";
|
|
4
3
|
/**
|
|
5
4
|
* Deferred sequence of values that can be async iterated and new values can be published.
|
|
@@ -16,13 +15,13 @@ export declare class DeferredSequence<T = void> extends AbstractSequence<T, void
|
|
|
16
15
|
/** Get the next promise to be deferred/rejected. */
|
|
17
16
|
get promise(): Promise<T>;
|
|
18
17
|
/** Resolve the current deferred in the sequence. */
|
|
19
|
-
|
|
18
|
+
resolve(value: T): void;
|
|
20
19
|
private _nextValue;
|
|
21
20
|
/** Reject the current deferred in the sequence. */
|
|
22
|
-
|
|
21
|
+
reject(reason: unknown): void;
|
|
23
22
|
private _nextReason;
|
|
24
23
|
/** Fulfill the current deferred by resolving or rejecting it. */
|
|
25
|
-
private
|
|
24
|
+
private _fulfill;
|
|
26
25
|
next(): Promise<IteratorResult<T, void>>;
|
|
27
26
|
then<X = T, Y = never>(onNext?: (v: T) => X | PromiseLike<X>, onError?: (r: unknown) => Y | PromiseLike<Y>): Promise<X | Y>;
|
|
28
27
|
catch<Y>(onError: (r: unknown) => Y | PromiseLike<Y>): Promise<T | Y>;
|
|
@@ -19,21 +19,21 @@ export class DeferredSequence extends AbstractSequence {
|
|
|
19
19
|
return (this._deferred ||= getDeferred()).promise;
|
|
20
20
|
}
|
|
21
21
|
/** Resolve the current deferred in the sequence. */
|
|
22
|
-
resolve
|
|
22
|
+
resolve(value) {
|
|
23
23
|
this._nextValue = value;
|
|
24
24
|
this._nextReason = _NOVALUE;
|
|
25
|
-
queueMicrotask(this._fulfill);
|
|
26
|
-
}
|
|
25
|
+
queueMicrotask(() => this._fulfill());
|
|
26
|
+
}
|
|
27
27
|
_nextValue = _NOVALUE;
|
|
28
28
|
/** Reject the current deferred in the sequence. */
|
|
29
|
-
reject
|
|
29
|
+
reject(reason) {
|
|
30
30
|
this._nextValue = _NOVALUE;
|
|
31
31
|
this._nextReason = reason;
|
|
32
|
-
queueMicrotask(this._fulfill);
|
|
33
|
-
}
|
|
32
|
+
queueMicrotask(() => this._fulfill());
|
|
33
|
+
}
|
|
34
34
|
_nextReason = _NOVALUE;
|
|
35
35
|
/** Fulfill the current deferred by resolving or rejecting it. */
|
|
36
|
-
_fulfill
|
|
36
|
+
_fulfill() {
|
|
37
37
|
const { _deferred, _nextReason, _nextValue } = this;
|
|
38
38
|
this._deferred = undefined;
|
|
39
39
|
this._nextReason = _NOVALUE;
|
|
@@ -44,7 +44,7 @@ export class DeferredSequence extends AbstractSequence {
|
|
|
44
44
|
else if (_nextValue !== _NOVALUE)
|
|
45
45
|
_deferred.resolve(_nextValue);
|
|
46
46
|
}
|
|
47
|
-
}
|
|
47
|
+
}
|
|
48
48
|
// Implement `AsyncIterator`
|
|
49
49
|
async next() {
|
|
50
50
|
return { value: await this.promise };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/// <reference types="node" resolution-mode="require"/>
|
|
2
|
+
import type { NONE } from "../util/constants.js";
|
|
3
|
+
import { type StartCallback } from "../util/callback.js";
|
|
4
|
+
import { Store } from "./Store.js";
|
|
5
|
+
/** Store that starts/stops a source subscription when things subscribe to it, and stops when everything has unsubscribed from it. */
|
|
6
|
+
export declare class LazyStore<T> extends Store<T> implements Disposable {
|
|
7
|
+
constructor(start: StartCallback<Store<T>>, value: T | typeof NONE, time?: number);
|
|
8
|
+
[Symbol.asyncIterator](): AsyncGenerator<T, void, void>;
|
|
9
|
+
private _iterating;
|
|
10
|
+
/** Start subscription to `MemoryProvider` if there is one. */
|
|
11
|
+
start(): void;
|
|
12
|
+
private _start;
|
|
13
|
+
/** Stop subscription to `MemoryProvider` if there is one. */
|
|
14
|
+
stop(): void;
|
|
15
|
+
private _stop;
|
|
16
|
+
[Symbol.dispose](): void;
|
|
17
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { call } from "../util/callback.js";
|
|
2
|
+
import { Store } from "./Store.js";
|
|
3
|
+
/** Store that starts/stops a source subscription when things subscribe to it, and stops when everything has unsubscribed from it. */
|
|
4
|
+
export class LazyStore extends Store {
|
|
5
|
+
constructor(start, value, time) {
|
|
6
|
+
super(value, time);
|
|
7
|
+
this._start = start;
|
|
8
|
+
}
|
|
9
|
+
// Override to subscribe to start/stop the source subscription when things subscribe to this.
|
|
10
|
+
async *[Symbol.asyncIterator]() {
|
|
11
|
+
this.start();
|
|
12
|
+
this._iterating++;
|
|
13
|
+
try {
|
|
14
|
+
yield* super[Symbol.asyncIterator]();
|
|
15
|
+
}
|
|
16
|
+
finally {
|
|
17
|
+
this._iterating--;
|
|
18
|
+
if (this._iterating < 1)
|
|
19
|
+
this.stop();
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
_iterating = 0;
|
|
23
|
+
/** Start subscription to `MemoryProvider` if there is one. */
|
|
24
|
+
start() {
|
|
25
|
+
if (!this._stop)
|
|
26
|
+
this._stop = this._start(this);
|
|
27
|
+
}
|
|
28
|
+
_start;
|
|
29
|
+
/** Stop subscription to `MemoryProvider` if there is one. */
|
|
30
|
+
stop() {
|
|
31
|
+
if (this._stop)
|
|
32
|
+
this._stop = void call(this._stop);
|
|
33
|
+
}
|
|
34
|
+
_stop = undefined;
|
|
35
|
+
// Implement `Disposable`
|
|
36
|
+
[Symbol.dispose]() {
|
|
37
|
+
this.stop();
|
|
38
|
+
}
|
|
39
|
+
}
|
package/store/index.d.ts
CHANGED