shelving 1.20.3 → 1.22.2

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.
Files changed (44) hide show
  1. package/api/Resource.d.ts +13 -7
  2. package/api/Resource.js +9 -9
  3. package/db/Database.d.ts +7 -19
  4. package/db/Database.js +10 -22
  5. package/feedback/index.d.ts +0 -1
  6. package/feedback/index.js +0 -1
  7. package/firestore/client/FirestoreClientProvider.d.ts +3 -3
  8. package/firestore/client/FirestoreClientProvider.js +2 -2
  9. package/firestore/lite/FirestoreLiteProvider.d.ts +3 -3
  10. package/firestore/server/FirestoreServerProvider.d.ts +3 -3
  11. package/firestore/server/FirestoreServerProvider.js +2 -2
  12. package/package.json +1 -1
  13. package/provider/MemoryProvider.js +14 -14
  14. package/provider/ValidationProvider.js +2 -3
  15. package/react/useDocument.js +3 -3
  16. package/react/useFetch.js +2 -2
  17. package/react/usePureState.d.ts +4 -4
  18. package/react/useQuery.js +3 -3
  19. package/react/useSubscribe.js +1 -1
  20. package/schema/SlugSchema.d.ts +18 -0
  21. package/schema/SlugSchema.js +25 -0
  22. package/schema/index.d.ts +3 -2
  23. package/schema/index.js +3 -2
  24. package/stream/LastStream.js +1 -1
  25. package/stream/LazyState.d.ts +1 -1
  26. package/stream/LazyState.js +4 -4
  27. package/stream/LazyStream.d.ts +1 -1
  28. package/stream/LazyStream.js +5 -5
  29. package/stream/State.d.ts +1 -1
  30. package/stream/State.js +4 -4
  31. package/stream/Stream.d.ts +7 -7
  32. package/stream/Stream.js +21 -21
  33. package/util/async.d.ts +6 -6
  34. package/util/error.js +1 -1
  35. package/util/function.d.ts +8 -0
  36. package/util/function.js +19 -0
  37. package/util/index.d.ts +0 -1
  38. package/util/index.js +0 -1
  39. package/util/observable.d.ts +10 -14
  40. package/util/observable.js +17 -23
  41. package/feedback/util.d.ts +0 -3
  42. package/feedback/util.js +0 -7
  43. package/util/dispatch.d.ts +0 -29
  44. package/util/dispatch.js +0 -43
package/api/Resource.d.ts CHANGED
@@ -6,24 +6,22 @@ import { Validator, Validatable } from "../util/index.js";
6
6
  * @param returns The `Validator` the function's returned value must conform to (defaults to `undefined` if not specified).
7
7
  */
8
8
  export declare class Resource<P = unknown, R = void> implements Validatable<R> {
9
- static create<X, Y>(payload: Validator<X>, result: Validator<Y>): Resource<X, Y>;
10
- static create<Y>(payload: undefined, result: Y): Resource<undefined, Y>;
11
- static create<X>(payload: Validator<X>, result?: undefined): Resource<X, void>;
12
- static create(payload?: undefined, result?: undefined): Resource<undefined, void>;
13
9
  /** Payload validator. */
14
10
  readonly payload: Validator<P>;
15
11
  /** Result validator. */
16
12
  readonly result: Validator<R>;
17
- protected constructor(payload: Validator<P>, result: Validator<R>);
13
+ constructor(payload: Validator<P>, result: Validator<R>);
18
14
  /**
19
15
  * Validate a payload for this resource.
20
16
  *
17
+ * @returns The validated payload for this resource.
18
+ * @throws InvalidFeedback if the payload could not be validated.
21
19
  */
22
- validatePayload(unsafePayload: unknown): P;
20
+ prepare(unsafePayload: unknown): P;
23
21
  /**
24
22
  * Validate a result for this resource.
25
23
  *
26
- * @returns The validated payload for this resource.
24
+ * @returns The validated result for this resource.
27
25
  * @throws ValidationError if the result could not be validated.
28
26
  */
29
27
  validate(unsafeResult: unknown): R;
@@ -32,3 +30,11 @@ export declare class Resource<P = unknown, R = void> implements Validatable<R> {
32
30
  export declare type PayloadType<X extends Resource> = X extends Resource<infer Y, unknown> ? Y : never;
33
31
  /** Extract the result type from a `Resource`. */
34
32
  export declare type ResourceType<X extends Resource> = X extends Resource<unknown, infer Y> ? Y : never;
33
+ /**
34
+ * Shortcut to create a new `Resource` (consistent with `Schema` shortcuts.
35
+ * - Sets `undefined` as the default type for payload and result.
36
+ */
37
+ export declare function RESOURCE<X, Y>(payload: Validator<X>, result: Validator<Y>): Resource<X, Y>;
38
+ export declare function RESOURCE<Y>(payload: undefined, result: Y): Resource<undefined, Y>;
39
+ export declare function RESOURCE<X>(payload: Validator<X>, result?: undefined): Resource<X, void>;
40
+ export declare function RESOURCE(payload?: undefined, result?: undefined): Resource<undefined, void>;
package/api/Resource.js CHANGED
@@ -1,8 +1,6 @@
1
1
  import { UNDEFINED, validate } from "../util/index.js";
2
- import { Feedback, throwFeedback } from "../feedback/index.js";
2
+ import { Feedback } from "../feedback/index.js";
3
3
  import { ResourceValidationError } from "./errors.js";
4
- /** Validator that always returns void/undefined. */
5
- const UNDEFINED_VALIDATOR = UNDEFINED;
6
4
  /**
7
5
  * An abstract API resource definition, used to specify types for e.g. serverless functions..
8
6
  *
@@ -15,20 +13,19 @@ export class Resource {
15
13
  this.payload = payload;
16
14
  this.result = result;
17
15
  }
18
- static create(payload = UNDEFINED_VALIDATOR, result = UNDEFINED_VALIDATOR) {
19
- return new Resource(payload, result);
20
- }
21
16
  /**
22
17
  * Validate a payload for this resource.
23
18
  *
19
+ * @returns The validated payload for this resource.
20
+ * @throws InvalidFeedback if the payload could not be validated.
24
21
  */
25
- validatePayload(unsafePayload) {
26
- return throwFeedback(validate(unsafePayload, this.payload));
22
+ prepare(unsafePayload) {
23
+ return validate(unsafePayload, this.payload);
27
24
  }
28
25
  /**
29
26
  * Validate a result for this resource.
30
27
  *
31
- * @returns The validated payload for this resource.
28
+ * @returns The validated result for this resource.
32
29
  * @throws ValidationError if the result could not be validated.
33
30
  */
34
31
  validate(unsafeResult) {
@@ -40,3 +37,6 @@ export class Resource {
40
37
  }
41
38
  }
42
39
  }
40
+ export function RESOURCE(payload = UNDEFINED, result = UNDEFINED) {
41
+ return new Resource(payload, result);
42
+ }
package/db/Database.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Dispatcher, Entry, Observable, Observer, Result, Unsubscriber, ResultsMap, Validatable, Validator, Key, Data, Results, Datas, Validators, ValidatorType } from "../util/index.js";
1
+ import { Entry, Observable, Observer, Result, Unsubscriber, ResultsMap, Validatable, Validator, Key, Data, Results, Datas, Validators, ValidatorType, Dispatcher } from "../util/index.js";
2
2
  import { Transform, Transforms } from "../transform/index.js";
3
3
  import type { Provider } from "../provider/Provider.js";
4
4
  import { Filters, Sorts, Query } from "../query/index.js";
@@ -58,26 +58,18 @@ export declare class DataQuery<T extends Data = Data> extends Query<T> implement
58
58
  * Subscribe to all matching documents.
59
59
  * - `next()` is called once with the initial results, and again any time the results change.
60
60
  *
61
- * @param observer Observer with `next`, `error`, or `complete` methods that the document results are reported back to.
62
- * @param next Callback that is called once initially and again whenever the results change.
63
- * @param error Callback that is called if an error occurs.
64
- * @param complete Callback that is called when the subscription is done.
65
- *
61
+ * @param next Observer with `next`, `error`, or `complete` methods or a `next()` dispatcher.
66
62
  * @return Function that ends the subscription.
67
63
  */
68
- subscribe(next: Observer<Results<T>> | Dispatcher<Results<T>>, error?: Dispatcher<Error | unknown>, complete?: Dispatcher<void>): Unsubscriber;
64
+ subscribe(next: Observer<Results<T>> | Dispatcher<[Results<T>]>): Unsubscriber;
69
65
  /**
70
66
  * Subscribe to all matching documents.
71
67
  * - `next()` is called once with the initial results, and again any time the results change.
72
68
  *
73
- * @param observer Observer with `next`, `error`, or `complete` methods that the document results are reported back to.
74
- * @param next Callback that is called once initially and again whenever the results change.
75
- * @param error Callback that is called if an error occurs.
76
- * @param complete Callback that is called when the subscription is done.
77
- *
69
+ * @param next Observer with `next`, `error`, or `complete` methods or a `next()` dispatcher.
78
70
  * @return Function that ends the subscription.
79
71
  */
80
- subscribeMap(next: Observer<ResultsMap<T>> | Dispatcher<ResultsMap<T>>, error?: Dispatcher<Error | unknown>, complete?: Dispatcher<void>): Unsubscriber;
72
+ subscribeMap(next: Observer<ResultsMap<T>> | Dispatcher<[ResultsMap<T>]>): Unsubscriber;
81
73
  /**
82
74
  * Set all matching documents to the same exact value.
83
75
  *
@@ -145,14 +137,10 @@ export declare class DataDocument<T extends Data = Data> implements Observable<R
145
137
  * Subscribe to the result of this document (indefinitely).
146
138
  * - `next()` is called once with the initial result, and again any time the result changes.
147
139
  *
148
- * @param observer Observer with `next`, `error`, or `complete` methods.
149
- * @param next Callback that is called once initially and again whenever the result changes.
150
- * @param error Callback that is called if an error occurs.
151
- * @param complete Callback that is called when the subscription is done.
152
- *
140
+ * @param next Observer with `next`, `error`, or `complete` methods or a `next()` dispatcher.
153
141
  * @return Function that ends the subscription.
154
142
  */
155
- subscribe(next: Observer<Result<T>> | Dispatcher<Result<T>>, error?: Dispatcher<Error | unknown>, complete?: Dispatcher<void>): Unsubscriber;
143
+ subscribe(next: Observer<Result<T>> | Dispatcher<[Result<T>]>): Unsubscriber;
156
144
  /**
157
145
  * Set the complete data of this document.
158
146
  *
package/db/Database.js CHANGED
@@ -1,4 +1,4 @@
1
- import { callAsync, createObserver, getFirstItem, throwAsync, validate, toMap, countItems, DeriveObserver, } from "../util/index.js";
1
+ import { callAsync, getFirstItem, throwAsync, validate, toMap, countItems, DeriveObserver, } from "../util/index.js";
2
2
  import { DataTransform, Transform } from "../transform/index.js";
3
3
  import { Feedback, InvalidFeedback } from "../feedback/index.js";
4
4
  import { Filters, Query, EqualFilter } from "../query/index.js";
@@ -79,29 +79,21 @@ export class DataQuery extends Query {
79
79
  * Subscribe to all matching documents.
80
80
  * - `next()` is called once with the initial results, and again any time the results change.
81
81
  *
82
- * @param observer Observer with `next`, `error`, or `complete` methods that the document results are reported back to.
83
- * @param next Callback that is called once initially and again whenever the results change.
84
- * @param error Callback that is called if an error occurs.
85
- * @param complete Callback that is called when the subscription is done.
86
- *
82
+ * @param next Observer with `next`, `error`, or `complete` methods or a `next()` dispatcher.
87
83
  * @return Function that ends the subscription.
88
84
  */
89
- subscribe(next, error, complete) {
90
- return this.provider.subscribeQuery(this, createObserver(next, error, complete));
85
+ subscribe(next) {
86
+ return this.provider.subscribeQuery(this, typeof next === "function" ? { next } : next);
91
87
  }
92
88
  /**
93
89
  * Subscribe to all matching documents.
94
90
  * - `next()` is called once with the initial results, and again any time the results change.
95
91
  *
96
- * @param observer Observer with `next`, `error`, or `complete` methods that the document results are reported back to.
97
- * @param next Callback that is called once initially and again whenever the results change.
98
- * @param error Callback that is called if an error occurs.
99
- * @param complete Callback that is called when the subscription is done.
100
- *
92
+ * @param next Observer with `next`, `error`, or `complete` methods or a `next()` dispatcher.
101
93
  * @return Function that ends the subscription.
102
94
  */
103
- subscribeMap(next, error, complete) {
104
- return this.provider.subscribeQuery(this, new DeriveObserver(toMap, createObserver(next, error, complete)));
95
+ subscribeMap(next) {
96
+ return this.provider.subscribeQuery(this, new DeriveObserver(toMap, typeof next === "function" ? { next } : next));
105
97
  }
106
98
  /**
107
99
  * Set all matching documents to the same exact value.
@@ -210,15 +202,11 @@ export class DataDocument {
210
202
  * Subscribe to the result of this document (indefinitely).
211
203
  * - `next()` is called once with the initial result, and again any time the result changes.
212
204
  *
213
- * @param observer Observer with `next`, `error`, or `complete` methods.
214
- * @param next Callback that is called once initially and again whenever the result changes.
215
- * @param error Callback that is called if an error occurs.
216
- * @param complete Callback that is called when the subscription is done.
217
- *
205
+ * @param next Observer with `next`, `error`, or `complete` methods or a `next()` dispatcher.
218
206
  * @return Function that ends the subscription.
219
207
  */
220
- subscribe(next, error, complete) {
221
- return this.provider.subscribe(this, createObserver(next, error, complete));
208
+ subscribe(next) {
209
+ return this.provider.subscribe(this, typeof next === "function" ? { next } : next);
222
210
  }
223
211
  /**
224
212
  * Set the complete data of this document.
@@ -4,4 +4,3 @@ export * from "./WarningFeedback.js";
4
4
  export * from "./ErrorFeedback.js";
5
5
  export * from "./InvalidFeedback.js";
6
6
  export * from "./hydrations.js";
7
- export * from "./util.js";
package/feedback/index.js CHANGED
@@ -4,4 +4,3 @@ export * from "./WarningFeedback.js";
4
4
  export * from "./ErrorFeedback.js";
5
5
  export * from "./InvalidFeedback.js";
6
6
  export * from "./hydrations.js";
7
- export * from "./util.js";
@@ -1,5 +1,5 @@
1
1
  import type { Firestore } from "firebase/firestore";
2
- import { Results, Provider, DataDocument, DataQuery, Result, Observer, Transform, AsynchronousProvider, Data } from "../../index.js";
2
+ import { Results, Provider, DataDocument, DataQuery, Result, Observer, Transform, AsynchronousProvider, Data, Unsubscriber } from "../../index.js";
3
3
  /**
4
4
  * Firestore client database provider.
5
5
  * - Works with the Firebase JS SDK.
@@ -10,10 +10,10 @@ export declare class FirestoreClientProvider extends Provider implements Asynchr
10
10
  readonly firestore: Firestore;
11
11
  constructor(firestore: Firestore);
12
12
  get<T extends Data>(ref: DataDocument<T>): Promise<Result<T>>;
13
- subscribe<T extends Data>(ref: DataDocument<T>, observer: Observer<Result<T>>): () => void;
13
+ subscribe<T extends Data>(ref: DataDocument<T>, observer: Observer<Result<T>>): Unsubscriber;
14
14
  add<T extends Data>(ref: DataQuery<T>, data: T): Promise<string>;
15
15
  write<T extends Data>(ref: DataDocument<T>, value: T | Transform<T> | undefined): Promise<void>;
16
16
  getQuery<T extends Data>(ref: DataQuery<T>): Promise<Results<T>>;
17
- subscribeQuery<T extends Data>(ref: DataQuery<T>, observer: Observer<Results<T>>): () => void;
17
+ subscribeQuery<T extends Data>(ref: DataQuery<T>, observer: Observer<Results<T>>): Unsubscriber;
18
18
  writeQuery<T extends Data>(ref: DataQuery<T>, value: T | Transform<T> | undefined): Promise<void>;
19
19
  }
@@ -88,7 +88,7 @@ export class FirestoreClientProvider extends Provider {
88
88
  return snapshot.data();
89
89
  }
90
90
  subscribe(ref, observer) {
91
- return onSnapshot(getDocument(this.firestore, ref), snapshot => dispatchNext(snapshot.data(), observer), thrown => dispatchError(thrown, observer));
91
+ return onSnapshot(getDocument(this.firestore, ref), snapshot => dispatchNext(observer, snapshot.data()), thrown => dispatchError(observer, thrown));
92
92
  }
93
93
  async add(ref, data) {
94
94
  const reference = await addDoc(getCollection(this.firestore, ref), data); // eslint-disable-line @typescript-eslint/no-explicit-any
@@ -106,7 +106,7 @@ export class FirestoreClientProvider extends Provider {
106
106
  return getResults(await getDocs(getQuery(this.firestore, ref)));
107
107
  }
108
108
  subscribeQuery(ref, observer) {
109
- return onSnapshot(getQuery(this.firestore, ref), snapshot => dispatchNext(getResults(snapshot), observer), thrown => dispatchError(thrown, observer));
109
+ return onSnapshot(getQuery(this.firestore, ref), snapshot => dispatchNext(observer, getResults(snapshot)), thrown => dispatchError(observer, thrown));
110
110
  }
111
111
  async writeQuery(ref, value) {
112
112
  const snapshot = await getDocs(getQuery(this.firestore, ref));
@@ -1,5 +1,5 @@
1
1
  import type { Firestore } from "firebase/firestore/lite";
2
- import { Provider, DataDocument, DataQuery, Result, Transform, AsynchronousProvider, Data, Results } from "../../index.js";
2
+ import { Provider, DataDocument, DataQuery, Result, Transform, AsynchronousProvider, Data, Results, Unsubscriber } from "../../index.js";
3
3
  /**
4
4
  * Firestore Lite client database provider.
5
5
  * - Works with the Firebase JS SDK.
@@ -10,10 +10,10 @@ export declare class FirestoreClientProvider extends Provider implements Asynchr
10
10
  readonly firestore: Firestore;
11
11
  constructor(firestore: Firestore);
12
12
  get<T extends Data>(ref: DataDocument<T>): Promise<Result<T>>;
13
- subscribe(): () => void;
13
+ subscribe(): Unsubscriber;
14
14
  add<T extends Data>(ref: DataQuery<T>, data: T): Promise<string>;
15
15
  write<T extends Data>(ref: DataDocument<T>, value: T | Transform<T> | undefined): Promise<void>;
16
16
  getQuery<T extends Data>(ref: DataQuery<T>): Promise<Results<T>>;
17
- subscribeQuery(): () => void;
17
+ subscribeQuery(): Unsubscriber;
18
18
  writeQuery<T extends Data>(ref: DataQuery<T>, value: T | Transform<T> | undefined): Promise<void>;
19
19
  }
@@ -1,5 +1,5 @@
1
1
  import { Firestore } from "@google-cloud/firestore";
2
- import { Provider, DataDocument, DataQuery, Observer, Result, Transform, Data, AsynchronousProvider, Entry, Results } from "../../index.js";
2
+ import { Provider, DataDocument, DataQuery, Observer, Result, Transform, Data, AsynchronousProvider, Entry, Results, Unsubscriber } from "../../index.js";
3
3
  /**
4
4
  * Firestore server database provider.
5
5
  * - Works with the Firebase Admin SDK for Node.JS
@@ -8,10 +8,10 @@ export declare class FirestoreServerProvider extends Provider implements Asynchr
8
8
  readonly firestore: Firestore;
9
9
  constructor(firestore?: Firestore);
10
10
  get<T extends Data>(ref: DataDocument<T>): Promise<Result<T>>;
11
- subscribe<T extends Data>(ref: DataDocument<T>, observer: Observer<Result<T>>): () => void;
11
+ subscribe<T extends Data>(ref: DataDocument<T>, observer: Observer<Result<T>>): Unsubscriber;
12
12
  add<T extends Data>(ref: DataQuery<T>, data: T): Promise<string>;
13
13
  write<T extends Data>(ref: DataDocument<T>, value: T | Transform<T> | undefined): Promise<void>;
14
14
  getQuery<T extends Data>(ref: DataQuery<T>): Promise<Iterable<Entry<T>>>;
15
- subscribeQuery<T extends Data>(ref: DataQuery<T>, observer: Observer<Results<T>>): () => void;
15
+ subscribeQuery<T extends Data>(ref: DataQuery<T>, observer: Observer<Results<T>>): Unsubscriber;
16
16
  writeQuery<T extends Data>(ref: DataQuery<T>, value: T | Transform<T> | undefined): Promise<void>;
17
17
  }
@@ -85,7 +85,7 @@ export class FirestoreServerProvider extends Provider {
85
85
  return (await getDocument(this.firestore, ref).get()).data();
86
86
  }
87
87
  subscribe(ref, observer) {
88
- return getDocument(this.firestore, ref).onSnapshot(snapshot => dispatchNext(snapshot.data(), observer), thrown => dispatchError(thrown, observer));
88
+ return getDocument(this.firestore, ref).onSnapshot(snapshot => dispatchNext(observer, snapshot.data()), thrown => dispatchError(observer, thrown));
89
89
  }
90
90
  async add(ref, data) {
91
91
  return (await getCollection(this.firestore, ref).add(data)).id;
@@ -102,7 +102,7 @@ export class FirestoreServerProvider extends Provider {
102
102
  return getResults(await getQuery(this.firestore, ref).get());
103
103
  }
104
104
  subscribeQuery(ref, observer) {
105
- return getQuery(this.firestore, ref).onSnapshot(snapshot => dispatchNext(getResults(snapshot), observer), thrown => dispatchError(thrown, observer));
105
+ return getQuery(this.firestore, ref).onSnapshot(snapshot => dispatchNext(observer, getResults(snapshot)), thrown => dispatchError(observer, thrown));
106
106
  }
107
107
  async writeQuery(ref, value) {
108
108
  const writer = this.firestore.bulkWriter();
package/package.json CHANGED
@@ -11,7 +11,7 @@
11
11
  "state-management",
12
12
  "query-builder"
13
13
  ],
14
- "version": "1.20.3",
14
+ "version": "1.22.2",
15
15
  "repository": "https://github.com/dhoulb/shelving",
16
16
  "author": "Dave Houlbrooke <dave@shax.com>",
17
17
  "license": "0BSD",
@@ -1,4 +1,4 @@
1
- import { randomId, dispatch, dispatchNext, isMapEqual } from "../util/index.js";
1
+ import { randomId, dispatchNext, isMapEqual, } from "../util/index.js";
2
2
  import { Transform } from "../transform/index.js";
3
3
  import { DocumentRequiredError } from "../db/index.js";
4
4
  import { Provider } from "./Provider.js";
@@ -25,10 +25,10 @@ export class MemoryProvider extends Provider {
25
25
  const table = this._table(ref);
26
26
  const id = ref.id;
27
27
  // Call next() immediately with initial results.
28
- dispatchNext(table.data.get(id), observer);
28
+ dispatchNext(observer, table.data.get(id));
29
29
  // Call next() every time the collection changes.
30
30
  return table.on(changes => {
31
- changes.has(id) && dispatchNext(changes.get(id), observer);
31
+ changes.has(id) && dispatchNext(observer, changes.get(id));
32
32
  });
33
33
  }
34
34
  add(ref, data) {
@@ -59,7 +59,7 @@ export class MemoryProvider extends Provider {
59
59
  const table = this._table(ref);
60
60
  // Call `next()` immediately with the initial results.
61
61
  let lastResults = new Map(ref.derive(table.data));
62
- dispatchNext(lastResults, observer);
62
+ dispatchNext(observer, lastResults);
63
63
  // Possibly call `next()` when the collection changes if any changes affect the subscription.
64
64
  return table.on(changes => {
65
65
  for (const [id, next] of changes) {
@@ -71,7 +71,7 @@ export class MemoryProvider extends Provider {
71
71
  const nextResults = new Map(ref.derive(table.data));
72
72
  if (!isMapEqual(lastResults, nextResults)) {
73
73
  lastResults = nextResults;
74
- dispatchNext(lastResults, observer);
74
+ dispatchNext(observer, lastResults);
75
75
  }
76
76
  return;
77
77
  }
@@ -99,11 +99,11 @@ class Table {
99
99
  constructor() {
100
100
  this.data = new Map();
101
101
  this.changes = new Map();
102
- this.dispatchers = new Set();
102
+ this.listeners = new Set();
103
103
  this.fire = () => {
104
104
  if (this.changes.size) {
105
- for (const dispatcher of this.dispatchers)
106
- dispatch(this.changes, dispatcher);
105
+ for (const dispatcher of this.listeners)
106
+ dispatcher(this.changes);
107
107
  this.changes.clear();
108
108
  }
109
109
  };
@@ -120,16 +120,16 @@ class Table {
120
120
  this.changes.set(id, value);
121
121
  }
122
122
  }
123
- on(dispatcher) {
124
- this.dispatchers.add(dispatcher);
125
- return this.off.bind(this, dispatcher);
123
+ on(listener) {
124
+ this.listeners.add(listener);
125
+ return this.off.bind(this, listener);
126
126
  }
127
- off(dispatcher) {
128
- this.dispatchers.delete(dispatcher);
127
+ off(listener) {
128
+ this.listeners.delete(listener);
129
129
  }
130
130
  reset() {
131
131
  this.data.clear();
132
132
  this.changes.clear();
133
- this.dispatchers.clear();
133
+ this.listeners.clear();
134
134
  }
135
135
  }
@@ -1,6 +1,5 @@
1
1
  import { validate, ValidateObserver, callAsync } from "../util/index.js";
2
2
  import { Transform, validateTransform } from "../transform/index.js";
3
- import { throwFeedback } from "../feedback/index.js";
4
3
  import { ThroughProvider } from "./ThroughProvider.js";
5
4
  /** Validates any values that are read from or written to a source provider. */
6
5
  export class ValidationProvider extends ThroughProvider {
@@ -11,7 +10,7 @@ export class ValidationProvider extends ThroughProvider {
11
10
  return super.subscribe(ref, new ValidateObserver(ref, observer));
12
11
  }
13
12
  add(ref, data) {
14
- return super.add(ref, throwFeedback(validate(data, ref.validator)));
13
+ return super.add(ref, validate(data, ref.validator));
15
14
  }
16
15
  write(ref, value) {
17
16
  return super.write(ref, value ? _validateWrite(value, ref.validator) : value);
@@ -28,5 +27,5 @@ export class ValidationProvider extends ThroughProvider {
28
27
  }
29
28
  /** Validate data or a transform for a path. */
30
29
  function _validateWrite(value, validator) {
31
- return throwFeedback(value instanceof Transform ? validateTransform(value, validator) : validate(value, validator));
30
+ return value instanceof Transform ? validateTransform(value, validator) : validate(value, validator);
32
31
  }
@@ -1,5 +1,5 @@
1
1
  import { useState } from "react";
2
- import { CacheProvider, throwAsync, NOERROR, findSourceProvider, NOVALUE, dispatchAsync } from "../index.js";
2
+ import { CacheProvider, throwAsync, NOERROR, findSourceProvider, NOVALUE } from "../index.js";
3
3
  import { usePureEffect } from "./usePureEffect.js";
4
4
  import { usePureMemo } from "./usePureMemo.js";
5
5
  import { usePureState } from "./usePureState.js";
@@ -22,7 +22,7 @@ export function useAsyncDocument(ref, maxAge = 1000) {
22
22
  // If `maxAge` is `true` open a subscription for 10 seconds.
23
23
  // Done before `ref.get()` because efficient providers (i.e. `BatchProvider`) will reuse the subscription's first result as its first get request.
24
24
  if (maxAge === true)
25
- setTimeout(ref.subscribe(setNext, setError), 10000);
25
+ setTimeout(ref.subscribe({ next: setNext, error: setError }), 10000);
26
26
  // Return a promise for the result.
27
27
  return ref.result;
28
28
  }
@@ -49,7 +49,7 @@ function subscribeEffect(ref, maxAge, next, error) {
49
49
  else {
50
50
  // If cache provider's cached document is older than maxAge then force refresh the data.
51
51
  if (provider.getCachedAge(ref) > maxAge)
52
- dispatchAsync(ref.result, next, error);
52
+ Promise.resolve(ref.result).then(next, error);
53
53
  }
54
54
  return stopCache;
55
55
  }
package/react/useFetch.js CHANGED
@@ -23,7 +23,7 @@ export function useFetch(fetcher, deps, maxAge = 86400000) {
23
23
  // Create a new state and start a fetch.
24
24
  state = new State();
25
25
  sources.set(key, state);
26
- dispatchAsyncNext(fetcher(...deps), state);
26
+ dispatchAsyncNext(state, fetcher(...deps));
27
27
  }
28
28
  else if (state.closed) {
29
29
  // Clean up source in a few seconds if it's closed.
@@ -32,7 +32,7 @@ export function useFetch(fetcher, deps, maxAge = 86400000) {
32
32
  }
33
33
  else if (state.age > maxAge) {
34
34
  // Refetch if state has value and it's older than `maxAge`
35
- dispatchAsyncNext(fetcher(...deps), state);
35
+ dispatchAsyncNext(state, fetcher(...deps));
36
36
  }
37
37
  useSubscribe(state);
38
38
  return state;
@@ -1,4 +1,4 @@
1
- import { Arguments, Dispatcher } from "../index.js";
1
+ import { Arguments } from "../index.js";
2
2
  /**
3
3
  * Version of React's `useState()` that allows the use of a pure (side-effect free) function.
4
4
  * - Unlike `useState()` the initialiser will be re-run and the state will be regenerated if `args` changes.
@@ -13,6 +13,6 @@ import { Arguments, Dispatcher } from "../index.js";
13
13
  * - This means you can create the function once (outside the component) rather than creating it on every render.
14
14
  * - This improves performance (though probably only noticeable on functions that render 1,000s of times).
15
15
  */
16
- export declare function usePureState<T, A extends Arguments>(initial: (...args: A) => T, ...args: A): readonly [T, Dispatcher<T>];
17
- export declare function usePureState<T, A extends Arguments>(initial: new (...args: A) => T, ...args: A): readonly [T, Dispatcher<T>];
18
- export declare function usePureState<T>(initial: T, ...args: Arguments): readonly [T, Dispatcher<T>];
16
+ export declare function usePureState<T, A extends Arguments>(initial: (...args: A) => T, ...args: A): readonly [T, (next: T) => void];
17
+ export declare function usePureState<T, A extends Arguments>(initial: new (...args: A) => T, ...args: A): readonly [T, (next: T) => void];
18
+ export declare function usePureState<T>(initial: T, ...args: Arguments): readonly [T, (next: T) => void];
package/react/useQuery.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { useState } from "react";
2
- import { CacheProvider, throwAsync, NOERROR, findSourceProvider, NOVALUE, dispatchAsync, DeriveObserver, toMap, } from "../index.js";
2
+ import { CacheProvider, throwAsync, NOERROR, findSourceProvider, NOVALUE, DeriveObserver, toMap, } from "../index.js";
3
3
  import { usePureEffect } from "./usePureEffect.js";
4
4
  import { usePureMemo } from "./usePureMemo.js";
5
5
  import { usePureState } from "./usePureState.js";
@@ -22,7 +22,7 @@ export function useAsyncQuery(ref, maxAge = 1000) {
22
22
  // If `maxAge` is `true` open a subscription for 10 seconds.
23
23
  // Done before `ref.get()` because efficient providers (i.e. `BatchProvider`) will reuse the subscription's first result as its first get request.
24
24
  if (maxAge === true)
25
- setTimeout(ref.subscribeMap(setNext, setError), 10000);
25
+ setTimeout(ref.subscribeMap({ next: setNext, error: setError }), 10000);
26
26
  // Return a promise for the result.
27
27
  return ref.resultsMap;
28
28
  }
@@ -50,7 +50,7 @@ function subscribeEffect(ref, maxAge, next, error) {
50
50
  else {
51
51
  // If cache provider's cached document is older than maxAge then force refresh the data.
52
52
  if (provider.getCachedAge(ref) > maxAge)
53
- dispatchAsync(ref.resultsMap, next, error);
53
+ Promise.resolve(ref.resultsMap).then(next, error);
54
54
  }
55
55
  return stopCache;
56
56
  }
@@ -14,4 +14,4 @@ export function useSubscribe(subscribable) {
14
14
  usePureEffect(subscribeEffect, useState(LOADING)[1], subscribable);
15
15
  }
16
16
  /** Effect that subscribes the component to changes in the `State` instance for the lifetime of the component. */
17
- const subscribeEffect = (setChange, subscribable) => subscribable ? subscribe(subscribable, { next: setChange, error: setChange }) : undefined;
17
+ const subscribeEffect = (change, subscribable) => subscribable ? subscribe(subscribable, { next: change, error: change }) : undefined;
@@ -0,0 +1,18 @@
1
+ import { StringSchema } from "./StringSchema.js";
2
+ /**
3
+ * Define a valid slug, e.g. `this-is-a-slug`
4
+ *
5
+ * - Useful for URL components, usernames, etc.
6
+ * - Minimum slug length is 2 characters.
7
+ * - Maximum slug length is 64 characters.
8
+ */
9
+ export declare class SlugSchema extends StringSchema {
10
+ readonly multiline = false;
11
+ readonly min = 2;
12
+ readonly max = 32;
13
+ sanitize(unsafeString: string): string;
14
+ }
15
+ /** Valid slug, e.g. `this-is-a-slug` */
16
+ export declare const SLUG: SlugSchema;
17
+ /** Valid slug, e.g. `this-is-a-slug`, or `null` */
18
+ export declare const OPTIONAL_SLUG: import("./OptionalSchema.js").OptionalSchema<string>;
@@ -0,0 +1,25 @@
1
+ import { toSlug } from "../util/index.js";
2
+ import { OPTIONAL } from "./OptionalSchema.js";
3
+ import { StringSchema } from "./StringSchema.js";
4
+ /**
5
+ * Define a valid slug, e.g. `this-is-a-slug`
6
+ *
7
+ * - Useful for URL components, usernames, etc.
8
+ * - Minimum slug length is 2 characters.
9
+ * - Maximum slug length is 64 characters.
10
+ */
11
+ export class SlugSchema extends StringSchema {
12
+ constructor() {
13
+ super(...arguments);
14
+ this.multiline = false;
15
+ this.min = 2;
16
+ this.max = 32;
17
+ }
18
+ sanitize(unsafeString) {
19
+ return toSlug(unsafeString);
20
+ }
21
+ }
22
+ /** Valid slug, e.g. `this-is-a-slug` */
23
+ export const SLUG = new SlugSchema({});
24
+ /** Valid slug, e.g. `this-is-a-slug`, or `null` */
25
+ export const OPTIONAL_SLUG = OPTIONAL(SLUG);
package/schema/index.d.ts CHANGED
@@ -7,12 +7,13 @@ export * from "./DataSchema.js";
7
7
  export * from "./DateSchema.js";
8
8
  export * from "./EmailSchema.js";
9
9
  export * from "./KeySchema.js";
10
+ export * from "./LinkSchema.js";
10
11
  export * from "./MapSchema.js";
11
- export * from "./OptionalSchema.js";
12
12
  export * from "./NumberSchema.js";
13
13
  export * from "./ObjectSchema.js";
14
+ export * from "./OptionalSchema.js";
14
15
  export * from "./PhoneSchema.js";
15
16
  export * from "./RequiredSchema.js";
17
+ export * from "./SlugSchema.js";
16
18
  export * from "./StringSchema.js";
17
19
  export * from "./ThroughSchema.js";
18
- export * from "./LinkSchema.js";
package/schema/index.js CHANGED
@@ -7,12 +7,13 @@ export * from "./DataSchema.js";
7
7
  export * from "./DateSchema.js";
8
8
  export * from "./EmailSchema.js";
9
9
  export * from "./KeySchema.js";
10
+ export * from "./LinkSchema.js";
10
11
  export * from "./MapSchema.js";
11
- export * from "./OptionalSchema.js";
12
12
  export * from "./NumberSchema.js";
13
13
  export * from "./ObjectSchema.js";
14
+ export * from "./OptionalSchema.js";
14
15
  export * from "./PhoneSchema.js";
15
16
  export * from "./RequiredSchema.js";
17
+ export * from "./SlugSchema.js";
16
18
  export * from "./StringSchema.js";
17
19
  export * from "./ThroughSchema.js";
18
- export * from "./LinkSchema.js";
@@ -4,7 +4,7 @@ import { Stream } from "./Stream.js";
4
4
  export class LastStream extends Stream {
5
5
  // Override to dispatch only to a slice of the subscribers.
6
6
  _dispatch(value) {
7
- const subscriber = getLastItem(this._subscribers);
7
+ const subscriber = getLastItem(this._observers);
8
8
  if (subscriber)
9
9
  dispatchNext(subscriber, value);
10
10
  }
@@ -8,5 +8,5 @@ export declare class LazyState<T> extends State<T> {
8
8
  private _delay;
9
9
  private _timeout?;
10
10
  constructor(delay?: number);
11
- off(observer: Observer<T>): void;
11
+ _off(observer: Observer<T>): void;
12
12
  }
@@ -9,20 +9,20 @@ export class LazyState extends State {
9
9
  this._delay = delay;
10
10
  }
11
11
  // Override to stop the source subscription when the last subscriber unsubscribes.
12
- off(observer) {
13
- super.off(observer);
12
+ _off(observer) {
13
+ super._off(observer);
14
14
  if (this._delay) {
15
15
  // Maybe stop in a bit (if there are still no subscribers).
16
16
  if (this._timeout)
17
17
  clearTimeout(this._timeout);
18
18
  this._timeout = setTimeout(() => {
19
- if (!this._subscribers.size && !this.closed)
19
+ if (!this._observers.size && !this.closed)
20
20
  this.complete();
21
21
  }, this._delay);
22
22
  }
23
23
  else {
24
24
  // Stop now.
25
- if (!this._subscribers.size && !this.closed)
25
+ if (!this._observers.size && !this.closed)
26
26
  this.complete();
27
27
  }
28
28
  }
@@ -8,5 +8,5 @@ export declare class LazyStream<T> extends Stream<T> {
8
8
  private _delay;
9
9
  private _timeout?;
10
10
  constructor(delay?: number);
11
- off(observer: Observer<T>): void;
11
+ _off(observer: Observer<T>): void;
12
12
  }
@@ -9,19 +9,19 @@ export class LazyStream extends Stream {
9
9
  this._delay = delay;
10
10
  }
11
11
  // Override to stop the source subscription when the last subscriber unsubscribes.
12
- off(observer) {
13
- super.off(observer);
12
+ _off(observer) {
13
+ super._off(observer);
14
14
  if (this._delay) {
15
15
  // Maybe stop in a bit (if there are still no subscribers).
16
- if (!this._subscribers.size && !this.closed) {
16
+ if (!this._observers.size && !this.closed) {
17
17
  if (this._timeout)
18
18
  clearTimeout(this._timeout);
19
- this._timeout = setTimeout(() => !this._subscribers.size && !this.closed && this.complete(), this._delay);
19
+ this._timeout = setTimeout(() => !this._observers.size && !this.closed && this.complete(), this._delay);
20
20
  }
21
21
  }
22
22
  else {
23
23
  // Stop now.
24
- if (!this._subscribers.size && !this.closed)
24
+ if (!this._observers.size && !this.closed)
25
25
  this.complete();
26
26
  }
27
27
  }
package/stream/State.d.ts CHANGED
@@ -35,7 +35,7 @@ export declare class State<T> extends Stream<T> {
35
35
  /** Apply a deriver to this state. */
36
36
  apply(deriver: Deriver<T, T>): void;
37
37
  error(reason: Error | unknown): void;
38
- on(observer: Observer<T>): void;
38
+ _on(observer: Observer<T>): void;
39
39
  protected _dispatch(value: T): void;
40
40
  }
41
41
  /** Create a state with an initial value. */
package/stream/State.js CHANGED
@@ -45,14 +45,14 @@ export class State extends Stream {
45
45
  super.error(reason);
46
46
  }
47
47
  // Override to send the current error or value to any new subscribers.
48
- on(observer) {
49
- super.on(observer);
48
+ _on(observer) {
49
+ super._on(observer);
50
50
  if (this.reason !== NOERROR)
51
- dispatchError(this.reason, observer);
51
+ dispatchError(observer, this.reason);
52
52
  else if (this.closed)
53
53
  dispatchComplete(observer);
54
54
  else if (this._value !== LOADING)
55
- dispatchNext(this.value, observer);
55
+ dispatchNext(observer, this._value);
56
56
  }
57
57
  // Dispatcher saves any values that are dispatched.
58
58
  _dispatch(value) {
@@ -1,4 +1,4 @@
1
- import { Deriver, ObserverType, Subscribable, Dispatcher, Unsubscriber, Observer, Observable, Constructor } from "../util/index.js";
1
+ import { Deriver, ObserverType, Subscribable, Unsubscriber, Observer, Observable, Constructor, Dispatcher } from "../util/index.js";
2
2
  /** Any stream (useful for `extends AnyStream` clauses). */
3
3
  export declare type AnyStream = Stream<any>;
4
4
  /**
@@ -11,7 +11,7 @@ export declare class Stream<T> implements Observer<T>, Observable<T> {
11
11
  /** List of sources this stream is subscribed to. */
12
12
  protected readonly _cleanups: Set<Unsubscriber>;
13
13
  /** List of subscribed observers that values are forwarded to. */
14
- protected readonly _subscribers: Set<Observer<T>>;
14
+ protected readonly _observers: Set<Observer<T>>;
15
15
  /** Get the number of current subscribers. */
16
16
  get subscribers(): number;
17
17
  /** Is this stream open or closed (i.e. `error()` or `complete()` have been called. */
@@ -55,9 +55,9 @@ export declare class Stream<T> implements Observer<T>, Observable<T> {
55
55
  * - Allows either an `Observer` object or separate `next()`, `error()` and `complete()` functions.
56
56
  * - Implements `Observable`
57
57
  */
58
- subscribe(next: Observer<T> | Dispatcher<T>, error?: Dispatcher<unknown>, complete?: Dispatcher<void>): Unsubscriber;
59
- /** Add an observer to this stream. */
60
- on(observer: Observer<T>): void;
61
- /** Remove an observer from this stream. */
62
- off(observer: Observer<T>): void;
58
+ subscribe(next: Observer<T> | Dispatcher<[T]>): Unsubscriber;
59
+ /** Add an observer. */
60
+ _on(observer: Observer<T>): void;
61
+ /** Remove an observer. */
62
+ _off(observer: Observer<T>): void;
63
63
  }
package/stream/Stream.js CHANGED
@@ -1,5 +1,5 @@
1
1
  var _a;
2
- import { AsyncObserver, DeriveObserver, dispatchNext, dispatchError, dispatchComplete, createObserver, subscribe, dispatch, } from "../util/index.js";
2
+ import { AsyncObserver, DeriveObserver, dispatchNext, dispatchError, dispatchComplete, subscribe, dispatch, } from "../util/index.js";
3
3
  import { ConditionError } from "../error/index.js";
4
4
  /**
5
5
  * Simple stream.
@@ -10,13 +10,13 @@ export class Stream {
10
10
  /** List of sources this stream is subscribed to. */
11
11
  this._cleanups = new Set();
12
12
  /** List of subscribed observers that values are forwarded to. */
13
- this._subscribers = new Set();
13
+ this._observers = new Set();
14
14
  /** Is this stream open or closed (i.e. `error()` or `complete()` have been called. */
15
15
  this.closed = false;
16
16
  }
17
17
  /** Get the number of current subscribers. */
18
18
  get subscribers() {
19
- return this._subscribers.size;
19
+ return this._observers.size;
20
20
  }
21
21
  /**
22
22
  * Send a next value.
@@ -29,8 +29,8 @@ export class Stream {
29
29
  }
30
30
  /** Call `next()` on the subscribers. */
31
31
  _dispatch(value) {
32
- for (const subscriber of this._subscribers)
33
- dispatchNext(value, subscriber);
32
+ for (const observer of this._observers)
33
+ dispatchNext(observer, value);
34
34
  }
35
35
  /**
36
36
  * Complete this stream with an error.
@@ -41,9 +41,9 @@ export class Stream {
41
41
  if (this.closed)
42
42
  throw new ConditionError("Stream is closed");
43
43
  this._close();
44
- for (const subscriber of this._subscribers) {
45
- dispatchError(reason, subscriber);
46
- this.off(subscriber);
44
+ for (const subscriber of this._observers) {
45
+ this._observers.delete(subscriber);
46
+ dispatchError(subscriber, reason);
47
47
  }
48
48
  }
49
49
  /**
@@ -55,17 +55,17 @@ export class Stream {
55
55
  if (this.closed)
56
56
  throw new ConditionError("Stream is closed");
57
57
  this._close();
58
- for (const subscriber of this._subscribers) {
58
+ for (const subscriber of this._observers) {
59
+ this._observers.delete(subscriber);
59
60
  dispatchComplete(subscriber);
60
- this.off(subscriber);
61
61
  }
62
62
  }
63
63
  /** Close this stream. */
64
64
  _close() {
65
65
  this.closed = true;
66
66
  for (const cleanup of this._cleanups) {
67
- dispatch(undefined, cleanup);
68
67
  this._cleanups.delete(cleanup);
68
+ dispatch(cleanup);
69
69
  }
70
70
  }
71
71
  /** Start a subscription that gets cleaned up when this subscriber ends. */
@@ -102,18 +102,18 @@ export class Stream {
102
102
  * - Allows either an `Observer` object or separate `next()`, `error()` and `complete()` functions.
103
103
  * - Implements `Observable`
104
104
  */
105
- subscribe(next, error, complete) {
106
- const observer = createObserver(next, error, complete);
107
- this.on(observer);
108
- return this.off.bind(this, observer);
105
+ subscribe(next) {
106
+ const observer = typeof next === "function" ? { next } : next;
107
+ this._on(observer);
108
+ return this._off.bind(this, observer);
109
109
  }
110
- /** Add an observer to this stream. */
111
- on(observer) {
112
- this._subscribers.add(observer);
110
+ /** Add an observer. */
111
+ _on(observer) {
112
+ this._observers.add(observer);
113
113
  }
114
- /** Remove an observer from this stream. */
115
- off(observer) {
116
- this._subscribers.delete(observer);
114
+ /** Remove an observer. */
115
+ _off(observer) {
116
+ this._observers.delete(observer);
117
117
  }
118
118
  }
119
119
  _a = Symbol.species;
package/util/async.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { DONE } from "./constants.js";
2
- import type { Dispatcher } from "./dispatch.js";
3
- import type { Arguments } from "./function.js";
2
+ import { Handler } from "./error.js";
3
+ import type { Arguments, Dispatcher } from "./function.js";
4
4
  /**
5
5
  * Throw the value if it's an async (promised) value.
6
6
  * @returns Synchronous (not promised) value.
@@ -15,15 +15,15 @@ export declare const _awaitCallAsync: <I, O, A extends Arguments>(callback: (v:
15
15
  /** Type of `Promise` with its `resolve()` and `reject()` methods exposed publicly. */
16
16
  export declare class Deferred<T> extends Promise<T> {
17
17
  static get [Symbol.species](): PromiseConstructor;
18
- readonly resolve: Dispatcher<T | PromiseLike<T>>;
19
- readonly reject: Dispatcher<Error | unknown | PromiseLike<Error | unknown>>;
18
+ readonly resolve: Dispatcher<[T]>;
19
+ readonly reject: Handler;
20
20
  constructor();
21
21
  }
22
22
  /** Type of `Promise` with `._resolve()` and `._reject()` methods available. */
23
23
  export declare abstract class AbstractPromise<T> extends Promise<T> {
24
24
  static get [Symbol.species](): PromiseConstructor;
25
- protected readonly _resolve: Dispatcher<T | PromiseLike<T>>;
26
- protected readonly _reject: Dispatcher<Error | unknown>;
25
+ protected readonly _resolve: Dispatcher<[T]>;
26
+ protected readonly _reject: Handler;
27
27
  constructor();
28
28
  }
29
29
  /** Promise that resolves after a specified delay in milliseconds. */
package/util/error.js CHANGED
@@ -1,3 +1,3 @@
1
1
  /** Handle an error by logging it to the console. */
2
2
  // eslint-disable-next-line no-console
3
- export const logError = (reason) => void console.error(reason);
3
+ export const logError = reason => console.error(reason);
@@ -8,3 +8,11 @@ export declare type Arguments = readonly unknown[];
8
8
  export declare const PASSTHROUGH: <T>(value: T) => T;
9
9
  /** Function that does nothing with its arguments and always returns void. */
10
10
  export declare const BLACKHOLE: (...args: Arguments) => void | undefined;
11
+ /** Function that receives a dispatched value. */
12
+ export declare type Dispatcher<T extends Arguments = []> = (...value: T) => void;
13
+ /** Safely dispatch a value to a dispatcher function. */
14
+ export declare function dispatch<T extends Arguments>(dispatcher: Dispatcher<T>, ...value: T): void;
15
+ /** Safely dispatch a value to a dispatcher method on an object. */
16
+ export declare function dispatchMethod<T extends Arguments, M extends string | symbol>(obj: {
17
+ [K in M]: Dispatcher<T>;
18
+ }, key: M, ...value: T): void;
package/util/function.js CHANGED
@@ -1,6 +1,25 @@
1
+ import { logError } from "./error.js";
1
2
  /** Is a value a function? */
2
3
  export const isFunction = (v) => typeof v === "function";
3
4
  /** Function that just passes through the first argument. */
4
5
  export const PASSTHROUGH = (value) => value;
5
6
  /** Function that does nothing with its arguments and always returns void. */
6
7
  export const BLACKHOLE = () => undefined;
8
+ /** Safely dispatch a value to a dispatcher function. */
9
+ export function dispatch(dispatcher, ...value) {
10
+ try {
11
+ dispatcher(...value);
12
+ }
13
+ catch (thrown) {
14
+ logError(thrown);
15
+ }
16
+ }
17
+ /** Safely dispatch a value to a dispatcher method on an object. */
18
+ export function dispatchMethod(obj, key, ...value) {
19
+ try {
20
+ obj[key](...value);
21
+ }
22
+ catch (thrown) {
23
+ logError(thrown);
24
+ }
25
+ }
package/util/index.d.ts CHANGED
@@ -9,7 +9,6 @@ export * from "./date.js";
9
9
  export * from "./debug.js";
10
10
  export * from "./derive.js";
11
11
  export * from "./diff.js";
12
- export * from "./dispatch.js";
13
12
  export * from "./entry.js";
14
13
  export * from "./equal.js";
15
14
  export * from "./error.js";
package/util/index.js CHANGED
@@ -9,7 +9,6 @@ export * from "./date.js";
9
9
  export * from "./debug.js";
10
10
  export * from "./derive.js";
11
11
  export * from "./diff.js";
12
- export * from "./dispatch.js";
13
12
  export * from "./entry.js";
14
13
  export * from "./equal.js";
15
14
  export * from "./error.js";
@@ -1,14 +1,12 @@
1
- import { Dispatcher } from "./dispatch.js";
1
+ import { Dispatcher } from "./function.js";
2
+ import { Handler } from "./error.js";
2
3
  import { Deriver } from "./derive.js";
3
4
  import { Validator } from "./validate.js";
4
5
  /** Function that ends a subscription. */
5
6
  export declare type Unsubscriber = () => void;
6
7
  /** An object that can initiate a subscription with its `subscribe()`. */
7
8
  export interface Observable<T> {
8
- /** Subscribe allows either an `Observer` or separate `next()`, `error()` and `complete()` functions. */
9
- subscribe(observer: Observer<T>): Unsubscriber;
10
- subscribe(next: Dispatcher<T>, error?: Dispatcher<unknown>, complete?: Dispatcher<void>): Unsubscriber;
11
- subscribe(either: Observer<T> | Dispatcher<T>, error?: Dispatcher<unknown>, complete?: Dispatcher<void>): Unsubscriber;
9
+ subscribe(observer: Observer<T> | Dispatcher<[T]>): Unsubscriber;
12
10
  }
13
11
  /** Any observable (useful for `extends AnyObservable` clauses). */
14
12
  export declare type AnyObservable = Observable<any>;
@@ -40,11 +38,11 @@ export interface Observer<T> {
40
38
  */
41
39
  readonly from?: (source: Subscribable<T>) => this;
42
40
  /** Receive the next value. */
43
- readonly next?: Dispatcher<T>;
41
+ readonly next: Dispatcher<[T]>;
44
42
  /** End the subscription with an error. */
45
- readonly error?: Dispatcher<unknown>;
43
+ readonly error?: Handler;
46
44
  /** End the subscription with success. */
47
- readonly complete?: Dispatcher<void>;
45
+ readonly complete?: Dispatcher;
48
46
  /** Whether the subscription has ended (either with success or failure). */
49
47
  readonly closed?: boolean;
50
48
  }
@@ -53,15 +51,13 @@ export declare type ObserverType<T extends AnyObserver> = T extends Observer<inf
53
51
  /** Any observer (useful for `extends AnyObserver` clauses). */
54
52
  export declare type AnyObserver = Observer<any>;
55
53
  /** Dispatch the next value to an observer (and if the next value errors, calls a handler). */
56
- export declare function dispatchNext<T>(value: T, observer: Observer<T>, handler?: import("./error.js").Handler): void;
54
+ export declare function dispatchNext<T>(observer: Observer<T>, value: T): void;
57
55
  /** Dispatch the next value to an observer (and if the next value errors, calls a handler). */
58
- export declare function dispatchAsyncNext<T>(value: PromiseLike<T>, observer: Observer<T>, handler?: import("./error.js").Handler): void;
56
+ export declare function dispatchAsyncNext<T>(observer: Observer<T>, value: PromiseLike<T>): void;
59
57
  /** Dispatch a complete call an observer (and if the next value errors, calls a handler). */
60
- export declare function dispatchComplete<T>(observer: Observer<T>, handler?: import("./error.js").Handler): void;
58
+ export declare function dispatchComplete<T>(observer: Observer<T>): void;
61
59
  /** Dispatch an error value to an observer. */
62
- export declare function dispatchError<T>(reason: Error | unknown, observer: Observer<T>, handler?: import("./error.js").Handler): void;
63
- /** Create an `Observer` from a set `next()`, `error()` and `complete()` functions. */
64
- export declare function createObserver<T>(next: Observer<T> | Dispatcher<T>, error?: Dispatcher<unknown>, complete?: Dispatcher<void>): Observer<T>;
60
+ export declare function dispatchError<T>(observer: Observer<T>, reason: Error | unknown): void;
65
61
  /** Abstract observer designed to pass values through to an observer. */
66
62
  export declare abstract class AbstractObserver<I, O> implements Observer<I> {
67
63
  protected _cleanup: Unsubscriber | undefined;
@@ -1,5 +1,5 @@
1
1
  import { ConditionError } from "../error/index.js";
2
- import { dispatch } from "./dispatch.js";
2
+ import { BLACKHOLE, dispatch } from "./function.js";
3
3
  import { logError } from "./error.js";
4
4
  import { isData } from "./data.js";
5
5
  import { derive } from "./derive.js";
@@ -15,44 +15,38 @@ export function subscribe(source, target) {
15
15
  return typeof source === "function" ? source(target) : source.subscribe(target);
16
16
  }
17
17
  /** Dispatch the next value to an observer (and if the next value errors, calls a handler). */
18
- export function dispatchNext(value, observer, handler = logError) {
19
- var _a;
18
+ export function dispatchNext(observer, value) {
20
19
  try {
21
- (_a = observer.next) === null || _a === void 0 ? void 0 : _a.call(observer, value);
20
+ observer.next(value);
22
21
  }
23
22
  catch (thrown) {
24
- handler(thrown);
23
+ logError(thrown);
25
24
  }
26
25
  }
27
26
  /** Dispatch the next value to an observer (and if the next value errors, calls a handler). */
28
- export function dispatchAsyncNext(value, observer, handler = logError) {
29
- if (observer.next)
30
- value.then(v => dispatchNext(v, observer, handler), handler);
27
+ export function dispatchAsyncNext(observer, value) {
28
+ value.then(v => dispatchNext(observer, v), logError);
31
29
  }
32
30
  /** Dispatch a complete call an observer (and if the next value errors, calls a handler). */
33
- export function dispatchComplete(observer, handler = logError) {
31
+ export function dispatchComplete(observer) {
34
32
  var _a;
35
33
  try {
36
34
  (_a = observer.complete) === null || _a === void 0 ? void 0 : _a.call(observer);
37
35
  }
38
36
  catch (thrown) {
39
- handler(thrown);
37
+ logError(thrown);
40
38
  }
41
39
  }
42
40
  /** Dispatch an error value to an observer. */
43
- export function dispatchError(reason, observer, handler = logError) {
41
+ export function dispatchError(observer, reason) {
44
42
  var _a;
45
43
  try {
46
44
  (_a = observer.error) === null || _a === void 0 ? void 0 : _a.call(observer, reason);
47
45
  }
48
46
  catch (thrown) {
49
- handler(thrown);
47
+ logError(thrown);
50
48
  }
51
49
  }
52
- /** Create an `Observer` from a set `next()`, `error()` and `complete()` functions. */
53
- export function createObserver(next, error, complete) {
54
- return typeof next === "object" ? next : { next, error, complete };
55
- }
56
50
  /** Abstract observer designed to pass values through to an observer. */
57
51
  export class AbstractObserver {
58
52
  constructor(target) {
@@ -78,7 +72,7 @@ export class AbstractObserver {
78
72
  if (!target)
79
73
  throw new ConditionError("Observer is closed");
80
74
  this._close();
81
- dispatchError(reason, target);
75
+ dispatchError(target, reason);
82
76
  }
83
77
  complete() {
84
78
  const target = this._target;
@@ -91,7 +85,7 @@ export class AbstractObserver {
91
85
  if (this._target)
92
86
  this._target = undefined;
93
87
  if (this._cleanup)
94
- this._cleanup = void dispatch(undefined, this._cleanup);
88
+ this._cleanup = void dispatch(this._cleanup);
95
89
  }
96
90
  }
97
91
  /** Observer that unsubscribes.*/
@@ -99,7 +93,7 @@ export class ThroughObserver extends AbstractObserver {
99
93
  next(value) {
100
94
  if (!this._target)
101
95
  throw new ConditionError("Observer is closed");
102
- dispatchNext(value, this._target);
96
+ dispatchNext(this._target, value);
103
97
  }
104
98
  }
105
99
  /** Observer that fires once then ends itself. */
@@ -119,7 +113,7 @@ export class DeriveObserver extends AbstractObserver {
119
113
  const target = this._target;
120
114
  if (!target)
121
115
  throw new ConditionError("Observer is closed");
122
- dispatchNext(derive(value, this._deriver), target);
116
+ dispatchNext(target, derive(value, this._deriver));
123
117
  }
124
118
  }
125
119
  /** Observer that validates its next values with a validator. */
@@ -132,7 +126,7 @@ export class ValidateObserver extends AbstractObserver {
132
126
  const target = this._target;
133
127
  if (!target)
134
128
  throw new ConditionError("Observer is closed");
135
- dispatchNext(validate(value, this._validator), target);
129
+ dispatchNext(target, validate(value, this._validator));
136
130
  }
137
131
  }
138
132
  /** Oserver that allows promised values to be passed to `next()`. */
@@ -141,7 +135,7 @@ export class AsyncObserver extends AbstractObserver {
141
135
  const target = this._target;
142
136
  if (!target)
143
137
  throw new ConditionError("Observer is closed");
144
- dispatchAsyncNext(value, target);
138
+ dispatchAsyncNext(target, value);
145
139
  }
146
140
  }
147
141
  /** Get a promise that resolves to the next value of a source subscribable. */
@@ -150,5 +144,5 @@ export function awaitNext(source) {
150
144
  }
151
145
  /** Get a promise that resolves when a source subscribable is complete. */
152
146
  export function awaitComplete(source) {
153
- return new Promise((complete, error) => new ThroughObserver({ complete, error }).from(source));
147
+ return new Promise((complete, error) => new ThroughObserver({ next: BLACKHOLE, complete, error }).from(source));
154
148
  }
@@ -1,3 +0,0 @@
1
- import { Feedback } from "./Feedback.js";
2
- /** Throw the value if it's an instance of `Feedback` */
3
- export declare function throwFeedback<T>(value: T | Feedback): T;
package/feedback/util.js DELETED
@@ -1,7 +0,0 @@
1
- import { Feedback } from "./Feedback.js";
2
- /** Throw the value if it's an instance of `Feedback` */
3
- export function throwFeedback(value) {
4
- if (value instanceof Feedback)
5
- throw value;
6
- return value;
7
- }
@@ -1,29 +0,0 @@
1
- /** Function that dispatches a value (we never care about the returned value). */
2
- export declare type Dispatcher<T> = (value: T) => void;
3
- /**
4
- * Safely dispatch a value to a dispatcher function.
5
- *
6
- * @param value The value to dispatch into the dispatcher function.
7
- * @param dispatcher Any dispatcher function.
8
- * - Any errors thrown or rejected by the dispatcher are caught and routed to the catcher.
9
- * @param handler Handler that handles any thrown errors.
10
- */
11
- export declare function dispatch<T>(value: T, dispatcher: Dispatcher<T>, handler?: import("./error.js").Handler): void;
12
- /** Safely dispatch an async value to a dispatcher function. */
13
- export declare function dispatchAsync<T>(value: T | PromiseLike<T>, dispatcher: Dispatcher<T>, handler?: import("./error.js").Handler): void;
14
- /**
15
- * Safely dispatch a value to a dispatcher method on an object.
16
- *
17
- * @param value The value to dispatch into the dispatcher method.
18
- * @param obj The object containing the method.
19
- * @param key The key specifiying which method to call (if the method is not defined, nothing will be dispatched).
20
- * - Any errors thrown or rejected by the dispatcher are caught and routed to the catcher.
21
- * @param handler Handler that handles any thrown errors.
22
- */
23
- export declare function thispatch<T, M extends string | symbol>(value: T, obj: {
24
- [K in M]: Dispatcher<T>;
25
- }, key: M, handler?: import("./error.js").Handler): void;
26
- /** Safely dispatch an async value to a dispatcher method on an object. */
27
- export declare function thispatchAsync<T, M extends string | symbol>(value: T | PromiseLike<T>, obj: {
28
- [K in M]: Dispatcher<T>;
29
- }, key: M, handler?: import("./error.js").Handler): void;
package/util/dispatch.js DELETED
@@ -1,43 +0,0 @@
1
- import { logError } from "./error.js";
2
- import { callAsync } from "./async.js";
3
- /**
4
- * Safely dispatch a value to a dispatcher function.
5
- *
6
- * @param value The value to dispatch into the dispatcher function.
7
- * @param dispatcher Any dispatcher function.
8
- * - Any errors thrown or rejected by the dispatcher are caught and routed to the catcher.
9
- * @param handler Handler that handles any thrown errors.
10
- */
11
- export function dispatch(value, dispatcher, handler = logError) {
12
- try {
13
- dispatcher(value);
14
- }
15
- catch (thrown) {
16
- handler(thrown);
17
- }
18
- }
19
- /** Safely dispatch an async value to a dispatcher function. */
20
- export function dispatchAsync(value, dispatcher, handler = logError) {
21
- void callAsync(dispatch, value, dispatcher, handler);
22
- }
23
- /**
24
- * Safely dispatch a value to a dispatcher method on an object.
25
- *
26
- * @param value The value to dispatch into the dispatcher method.
27
- * @param obj The object containing the method.
28
- * @param key The key specifiying which method to call (if the method is not defined, nothing will be dispatched).
29
- * - Any errors thrown or rejected by the dispatcher are caught and routed to the catcher.
30
- * @param handler Handler that handles any thrown errors.
31
- */
32
- export function thispatch(value, obj, key, handler = logError) {
33
- try {
34
- obj[key](value);
35
- }
36
- catch (thrown) {
37
- handler(thrown);
38
- }
39
- }
40
- /** Safely dispatch an async value to a dispatcher method on an object. */
41
- export function thispatchAsync(value, obj, key, handler = logError) {
42
- void callAsync(thispatch, value, obj, key, handler);
43
- }