@byloth/core 1.5.3 → 2.0.0-rc.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.
- package/dist/core.js +520 -364
- package/dist/core.js.map +1 -1
- package/dist/core.umd.cjs +2 -2
- package/dist/core.umd.cjs.map +1 -1
- package/package.json +10 -8
- package/src/helpers.ts +3 -0
- package/src/index.ts +16 -4
- package/src/models/aggregators/reduced-iterator.ts +2 -2
- package/src/models/exceptions/core.ts +3 -4
- package/src/models/exceptions/index.ts +41 -1
- package/src/models/game-loop.ts +83 -0
- package/src/models/index.ts +10 -4
- package/src/models/iterators/smart-async-iterator.ts +3 -1
- package/src/models/iterators/smart-iterator.ts +3 -1
- package/src/models/json/index.ts +3 -0
- package/src/models/{json-storage.ts → json/json-storage.ts} +39 -26
- package/src/models/json/types.ts +3 -0
- package/src/models/publisher.ts +39 -0
- package/src/models/timers/clock.ts +47 -0
- package/src/models/timers/countdown.ts +79 -0
- package/src/models/timers/index.ts +4 -0
- package/src/models/types.ts +9 -0
- package/src/utils/date.ts +4 -4
- package/src/utils/index.ts +1 -1
- package/src/models/subscribers.ts +0 -35
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ValueException } from "../exceptions/index.js";
|
|
2
2
|
import { SmartIterator } from "../iterators/index.js";
|
|
3
3
|
import type { GeneratorFunction } from "../iterators/types.js";
|
|
4
4
|
|
|
@@ -62,7 +62,7 @@ export default class ReducedIterator<K extends PropertyKey, T>
|
|
|
62
62
|
const firstElement = this._elements.next();
|
|
63
63
|
if (firstElement.done)
|
|
64
64
|
{
|
|
65
|
-
throw new
|
|
65
|
+
throw new ValueException("Cannot reduce an empty iterator without an initial value.");
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
index += 1;
|
|
@@ -48,8 +48,8 @@ export class FatalErrorException extends Exception
|
|
|
48
48
|
{
|
|
49
49
|
if (message === undefined)
|
|
50
50
|
{
|
|
51
|
-
message = "The
|
|
52
|
-
"Please,
|
|
51
|
+
message = "The program has encountered an unrecoverable error and cannot continue as expected. " +
|
|
52
|
+
"Please, try again later. If the problem persists, contact the support team.";
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
super(message, cause, name);
|
|
@@ -57,8 +57,7 @@ export class FatalErrorException extends Exception
|
|
|
57
57
|
|
|
58
58
|
public get [Symbol.toStringTag]() { return "FatalErrorException"; }
|
|
59
59
|
}
|
|
60
|
-
|
|
61
|
-
export class NotImplementedException extends Exception
|
|
60
|
+
export class NotImplementedException extends FatalErrorException
|
|
62
61
|
{
|
|
63
62
|
public constructor(message?: string, cause?: unknown, name = "NotImplementedException")
|
|
64
63
|
{
|
|
@@ -1,6 +1,24 @@
|
|
|
1
1
|
import Exception from "./core.js";
|
|
2
2
|
|
|
3
|
-
export class
|
|
3
|
+
export class FileException extends Exception
|
|
4
|
+
{
|
|
5
|
+
public constructor(message: string, cause?: unknown, name = "FileException")
|
|
6
|
+
{
|
|
7
|
+
super(message, cause, name);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
public get [Symbol.toStringTag]() { return "FileException"; }
|
|
11
|
+
}
|
|
12
|
+
export class FileExistsException extends FileException
|
|
13
|
+
{
|
|
14
|
+
public constructor(message: string, cause?: unknown, name = "FileExistsException")
|
|
15
|
+
{
|
|
16
|
+
super(message, cause, name);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
public get [Symbol.toStringTag]() { return "FileExistsException"; }
|
|
20
|
+
}
|
|
21
|
+
export class FileNotFoundException extends FileException
|
|
4
22
|
{
|
|
5
23
|
public constructor(message: string, cause?: unknown, name = "FileNotFoundException")
|
|
6
24
|
{
|
|
@@ -9,6 +27,7 @@ export class FileNotFoundException extends Exception
|
|
|
9
27
|
|
|
10
28
|
public get [Symbol.toStringTag]() { return "FileNotFoundException"; }
|
|
11
29
|
}
|
|
30
|
+
|
|
12
31
|
export class KeyException extends Exception
|
|
13
32
|
{
|
|
14
33
|
public constructor(message: string, cause?: unknown, name = "KeyException")
|
|
@@ -45,6 +64,7 @@ export class ReferenceException extends Exception
|
|
|
45
64
|
|
|
46
65
|
public get [Symbol.toStringTag]() { return "ReferenceException"; }
|
|
47
66
|
}
|
|
67
|
+
|
|
48
68
|
export class RuntimeException extends Exception
|
|
49
69
|
{
|
|
50
70
|
public constructor(message: string, cause?: unknown, name = "RuntimeException")
|
|
@@ -54,6 +74,16 @@ export class RuntimeException extends Exception
|
|
|
54
74
|
|
|
55
75
|
public get [Symbol.toStringTag]() { return "RuntimeException"; }
|
|
56
76
|
}
|
|
77
|
+
export class EnvironmentException extends RuntimeException
|
|
78
|
+
{
|
|
79
|
+
public constructor(message: string, cause?: unknown, name = "EnvironmentException")
|
|
80
|
+
{
|
|
81
|
+
super(message, cause, name);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
public get [Symbol.toStringTag]() { return "EnvironmentException"; }
|
|
85
|
+
}
|
|
86
|
+
|
|
57
87
|
export class TimeoutException extends Exception
|
|
58
88
|
{
|
|
59
89
|
public constructor(message: string, cause?: unknown, name = "TimeoutException")
|
|
@@ -72,6 +102,7 @@ export class TypeException extends Exception
|
|
|
72
102
|
|
|
73
103
|
public get [Symbol.toStringTag]() { return "TypeException"; }
|
|
74
104
|
}
|
|
105
|
+
|
|
75
106
|
export class ValueException extends Exception
|
|
76
107
|
{
|
|
77
108
|
public constructor(message: string, cause?: unknown, name = "ValueException")
|
|
@@ -81,6 +112,15 @@ export class ValueException extends Exception
|
|
|
81
112
|
|
|
82
113
|
public get [Symbol.toStringTag]() { return "ValueException"; }
|
|
83
114
|
}
|
|
115
|
+
export class RangeException extends ValueException
|
|
116
|
+
{
|
|
117
|
+
public constructor(message: string, cause?: unknown, name = "RangeException")
|
|
118
|
+
{
|
|
119
|
+
super(message, cause, name);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
public get [Symbol.toStringTag]() { return "RangeException"; }
|
|
123
|
+
}
|
|
84
124
|
|
|
85
125
|
export { Exception };
|
|
86
126
|
export { FatalErrorException, NotImplementedException } from "./core.js";
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { isBrowser } from "../helpers.js";
|
|
2
|
+
import { TimeUnit } from "../utils/date.js";
|
|
3
|
+
|
|
4
|
+
import { FatalErrorException, RuntimeException } from "./exceptions/index.js";
|
|
5
|
+
|
|
6
|
+
export default class GameLoop
|
|
7
|
+
{
|
|
8
|
+
protected _handle?: number;
|
|
9
|
+
|
|
10
|
+
protected _startTime: number;
|
|
11
|
+
public get startTime(): number
|
|
12
|
+
{
|
|
13
|
+
return this._startTime;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
protected _isRunning: boolean;
|
|
17
|
+
public get isRunning(): boolean
|
|
18
|
+
{
|
|
19
|
+
return this._isRunning;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
public get elapsedTime(): number
|
|
23
|
+
{
|
|
24
|
+
return performance.now() - this._startTime;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
protected _start: () => void;
|
|
28
|
+
protected _stop: () => void;
|
|
29
|
+
|
|
30
|
+
public constructor(callback: FrameRequestCallback, fpsIfNotBrowser = 30)
|
|
31
|
+
{
|
|
32
|
+
this._startTime = 0;
|
|
33
|
+
this._isRunning = false;
|
|
34
|
+
|
|
35
|
+
if (isBrowser)
|
|
36
|
+
{
|
|
37
|
+
this._start = () =>
|
|
38
|
+
{
|
|
39
|
+
callback(this.elapsedTime);
|
|
40
|
+
|
|
41
|
+
this._handle = window.requestAnimationFrame(this._start);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
this._stop = () => window.cancelAnimationFrame(this._handle!);
|
|
45
|
+
}
|
|
46
|
+
else
|
|
47
|
+
{
|
|
48
|
+
// eslint-disable-next-line no-console
|
|
49
|
+
console.warn(
|
|
50
|
+
"Not a browser environment detected. " +
|
|
51
|
+
`Using setInterval@${fpsIfNotBrowser}fps instead of requestAnimationFrame...`
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
this._start = () =>
|
|
55
|
+
{
|
|
56
|
+
const delay = (TimeUnit.Second / fpsIfNotBrowser);
|
|
57
|
+
|
|
58
|
+
this._handle = (setInterval(() => callback(this.elapsedTime), delay) as unknown) as number;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
this._stop = () => clearInterval(this._handle!);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
public start(elapsedTime = 0): void
|
|
66
|
+
{
|
|
67
|
+
if (this._isRunning) { throw new RuntimeException("The game loop has already been started."); }
|
|
68
|
+
|
|
69
|
+
this._startTime = performance.now() - elapsedTime;
|
|
70
|
+
this._start();
|
|
71
|
+
this._isRunning = true;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
public stop(): void
|
|
75
|
+
{
|
|
76
|
+
if (!(this._isRunning)) { throw new RuntimeException("The game loop hadn't yet started."); }
|
|
77
|
+
if (!(this._handle)) { throw new FatalErrorException(); }
|
|
78
|
+
|
|
79
|
+
this._stop();
|
|
80
|
+
this._handle = undefined;
|
|
81
|
+
this._isRunning = false;
|
|
82
|
+
}
|
|
83
|
+
}
|
package/src/models/index.ts
CHANGED
|
@@ -11,10 +11,13 @@ export {
|
|
|
11
11
|
Exception,
|
|
12
12
|
FatalErrorException,
|
|
13
13
|
NotImplementedException,
|
|
14
|
+
FileException,
|
|
15
|
+
FileExistsException,
|
|
14
16
|
FileNotFoundException,
|
|
15
17
|
KeyException,
|
|
16
18
|
NetworkException,
|
|
17
19
|
PermissionException,
|
|
20
|
+
RangeException,
|
|
18
21
|
ReferenceException,
|
|
19
22
|
RuntimeException,
|
|
20
23
|
TimeoutException,
|
|
@@ -23,11 +26,14 @@ export {
|
|
|
23
26
|
|
|
24
27
|
} from "./exceptions/index.js";
|
|
25
28
|
|
|
29
|
+
import GameLoop from "./game-loop.js";
|
|
30
|
+
|
|
26
31
|
export { SmartIterator, SmartAsyncIterator } from "./iterators/index.js";
|
|
32
|
+
export { JSONStorage } from "./json/index.js";
|
|
33
|
+
export { DeferredPromise, SmartPromise, TimedPromise } from "./promises/index.js";
|
|
27
34
|
|
|
28
|
-
import
|
|
29
|
-
import Subscribers from "./subscribers.js";
|
|
35
|
+
import Publisher from "./publisher.js";
|
|
30
36
|
|
|
31
|
-
export {
|
|
37
|
+
export { Clock, Countdown } from "./timers/index.js";
|
|
32
38
|
|
|
33
|
-
export {
|
|
39
|
+
export { GameLoop, Publisher };
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { ValueException } from "../exceptions/index.js";
|
|
2
|
+
|
|
1
3
|
import type {
|
|
2
4
|
AsyncGeneratorFunction,
|
|
3
5
|
GeneratorFunction,
|
|
@@ -168,7 +170,7 @@ export default class SmartAsyncIterator<T, R = void, N = undefined> implements A
|
|
|
168
170
|
if (accumulator === undefined)
|
|
169
171
|
{
|
|
170
172
|
const result = await this._iterator.next();
|
|
171
|
-
if (result.done) { throw new
|
|
173
|
+
if (result.done) { throw new ValueException("Cannot reduce an empty iterator without an initial value."); }
|
|
172
174
|
|
|
173
175
|
accumulator = (result.value as unknown) as A;
|
|
174
176
|
index += 1;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { ValueException } from "../exceptions/index.js";
|
|
2
|
+
|
|
1
3
|
import type { GeneratorFunction, Iteratee, TypeGuardIteratee, Reducer, IterLike } from "./types.js";
|
|
2
4
|
|
|
3
5
|
export default class SmartIterator<T, R = void, N = undefined> implements Iterator<T, R, N>
|
|
@@ -110,7 +112,7 @@ export default class SmartIterator<T, R = void, N = undefined> implements Iterat
|
|
|
110
112
|
if (accumulator === undefined)
|
|
111
113
|
{
|
|
112
114
|
const result = this._iterator.next();
|
|
113
|
-
if (result.done) { throw new
|
|
115
|
+
if (result.done) { throw new ValueException("Cannot reduce an empty iterator without an initial value."); }
|
|
114
116
|
|
|
115
117
|
accumulator = (result.value as unknown) as A;
|
|
116
118
|
index += 1;
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
/* eslint-disable no-trailing-spaces */
|
|
2
2
|
|
|
3
|
+
import { isBrowser } from "../../helpers.js";
|
|
4
|
+
import { EnvironmentException } from "../exceptions/index.js";
|
|
5
|
+
|
|
6
|
+
import type { JSONValue } from "./types.js";
|
|
7
|
+
|
|
3
8
|
/**
|
|
4
9
|
* A wrapper around the `Storage` API to store and retrieve JSON values.
|
|
5
10
|
*
|
|
6
11
|
* It allows to handle either the `sessionStorage` or the `localStorage`
|
|
7
12
|
* storage at the same time, depending on the required use case.
|
|
8
13
|
*/
|
|
9
|
-
export default class
|
|
14
|
+
export default class JSONStorage
|
|
10
15
|
{
|
|
11
16
|
protected _preferPersistence: boolean;
|
|
12
17
|
|
|
@@ -17,14 +22,21 @@ export default class JsonStorage
|
|
|
17
22
|
{
|
|
18
23
|
this._preferPersistence = preferPersistence;
|
|
19
24
|
|
|
25
|
+
if (!(isBrowser))
|
|
26
|
+
{
|
|
27
|
+
throw new EnvironmentException(
|
|
28
|
+
"The `JSONStorage` class can only be instantiated within a browser environment."
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
20
32
|
this._volatile = window.sessionStorage;
|
|
21
33
|
this._persistent = window.localStorage;
|
|
22
34
|
}
|
|
23
35
|
|
|
24
|
-
protected _get<T>(storage: Storage, propertyName: string): T | undefined;
|
|
25
|
-
protected _get<T>(storage: Storage, propertyName: string, defaultValue: T): T;
|
|
26
|
-
protected _get<T>(storage: Storage, propertyName: string, defaultValue?: T): T | undefined;
|
|
27
|
-
protected _get<T>(storage: Storage, propertyName: string, defaultValue?: T): T | undefined
|
|
36
|
+
protected _get<T extends JSONValue>(storage: Storage, propertyName: string): T | undefined;
|
|
37
|
+
protected _get<T extends JSONValue>(storage: Storage, propertyName: string, defaultValue: T): T;
|
|
38
|
+
protected _get<T extends JSONValue>(storage: Storage, propertyName: string, defaultValue?: T): T | undefined;
|
|
39
|
+
protected _get<T extends JSONValue>(storage: Storage, propertyName: string, defaultValue?: T): T | undefined
|
|
28
40
|
{
|
|
29
41
|
const propertyValue = storage.getItem(propertyName);
|
|
30
42
|
if (propertyValue)
|
|
@@ -46,7 +58,7 @@ export default class JsonStorage
|
|
|
46
58
|
|
|
47
59
|
return defaultValue;
|
|
48
60
|
}
|
|
49
|
-
protected _set<T>(storage: Storage, propertyName: string, newValue?: T): void
|
|
61
|
+
protected _set<T extends JSONValue>(storage: Storage, propertyName: string, newValue?: T): void
|
|
50
62
|
{
|
|
51
63
|
const encodedValue = JSON.stringify(newValue);
|
|
52
64
|
if (encodedValue)
|
|
@@ -68,10 +80,11 @@ export default class JsonStorage
|
|
|
68
80
|
*
|
|
69
81
|
* @returns The value of the property or the default value if the property does not exist.
|
|
70
82
|
*/
|
|
71
|
-
public get<T>(propertyName: string, defaultValue: undefined, persistent?: boolean): T | undefined;
|
|
72
|
-
public get<T>(propertyName: string, defaultValue: T, persistent?: boolean): T ;
|
|
73
|
-
public get<T>(propertyName: string, defaultValue?: T, persistent?: boolean): T | undefined;
|
|
74
|
-
public get<T>(propertyName: string, defaultValue?: T, persistent = this._preferPersistence)
|
|
83
|
+
public get<T extends JSONValue>(propertyName: string, defaultValue: undefined, persistent?: boolean): T | undefined;
|
|
84
|
+
public get<T extends JSONValue>(propertyName: string, defaultValue: T, persistent?: boolean): T ;
|
|
85
|
+
public get<T extends JSONValue>(propertyName: string, defaultValue?: T, persistent?: boolean): T | undefined;
|
|
86
|
+
public get<T extends JSONValue>(propertyName: string, defaultValue?: T, persistent = this._preferPersistence)
|
|
87
|
+
: T | undefined
|
|
75
88
|
{
|
|
76
89
|
const storage = persistent ? this._persistent : this._volatile;
|
|
77
90
|
|
|
@@ -85,10 +98,10 @@ export default class JsonStorage
|
|
|
85
98
|
*
|
|
86
99
|
* @returns The value of the property or the default value if the property does not exist.
|
|
87
100
|
*/
|
|
88
|
-
public recall<T>(propertyName: string): T | undefined;
|
|
89
|
-
public recall<T>(propertyName: string, defaultValue: T): T;
|
|
90
|
-
public recall<T>(propertyName: string, defaultValue?: T): T | undefined;
|
|
91
|
-
public recall<T>(propertyName: string, defaultValue?: T): T | undefined
|
|
101
|
+
public recall<T extends JSONValue>(propertyName: string): T | undefined;
|
|
102
|
+
public recall<T extends JSONValue>(propertyName: string, defaultValue: T): T;
|
|
103
|
+
public recall<T extends JSONValue>(propertyName: string, defaultValue?: T): T | undefined;
|
|
104
|
+
public recall<T extends JSONValue>(propertyName: string, defaultValue?: T): T | undefined
|
|
92
105
|
{
|
|
93
106
|
return this._get<T>(this._volatile, propertyName, defaultValue);
|
|
94
107
|
}
|
|
@@ -101,10 +114,10 @@ export default class JsonStorage
|
|
|
101
114
|
*
|
|
102
115
|
* @returns The value of the property or the default value if the property does not exist.
|
|
103
116
|
*/
|
|
104
|
-
public retrieve<T>(propertyName: string): T | undefined;
|
|
105
|
-
public retrieve<T>(propertyName: string, defaultValue: T): T;
|
|
106
|
-
public retrieve<T>(propertyName: string, defaultValue?: T): T | undefined;
|
|
107
|
-
public retrieve<T>(propertyName: string, defaultValue?: T): T | undefined
|
|
117
|
+
public retrieve<T extends JSONValue>(propertyName: string): T | undefined;
|
|
118
|
+
public retrieve<T extends JSONValue>(propertyName: string, defaultValue: T): T;
|
|
119
|
+
public retrieve<T extends JSONValue>(propertyName: string, defaultValue?: T): T | undefined;
|
|
120
|
+
public retrieve<T extends JSONValue>(propertyName: string, defaultValue?: T): T | undefined
|
|
108
121
|
{
|
|
109
122
|
return this.recall<T>(propertyName) ?? this.read<T>(propertyName, defaultValue);
|
|
110
123
|
}
|
|
@@ -116,10 +129,10 @@ export default class JsonStorage
|
|
|
116
129
|
*
|
|
117
130
|
* @returns The value of the property or the default value if the property does not exist.
|
|
118
131
|
*/
|
|
119
|
-
public read<T>(propertyName: string): T | undefined;
|
|
120
|
-
public read<T>(propertyName: string, defaultValue: T): T;
|
|
121
|
-
public read<T>(propertyName: string, defaultValue?: T): T | undefined;
|
|
122
|
-
public read<T>(propertyName: string, defaultValue?: T): T | undefined
|
|
132
|
+
public read<T extends JSONValue>(propertyName: string): T | undefined;
|
|
133
|
+
public read<T extends JSONValue>(propertyName: string, defaultValue: T): T;
|
|
134
|
+
public read<T extends JSONValue>(propertyName: string, defaultValue?: T): T | undefined;
|
|
135
|
+
public read<T extends JSONValue>(propertyName: string, defaultValue?: T): T | undefined
|
|
123
136
|
{
|
|
124
137
|
return this._get<T>(this._persistent, propertyName, defaultValue);
|
|
125
138
|
}
|
|
@@ -181,7 +194,7 @@ export default class JsonStorage
|
|
|
181
194
|
* @param newValue The new value to set.
|
|
182
195
|
* @param persistent Whether to use the persistent `localStorage` or the volatile `sessionStorage`.
|
|
183
196
|
*/
|
|
184
|
-
public set<T>(propertyName: string, newValue?: T, persistent = this._preferPersistence): void
|
|
197
|
+
public set<T extends JSONValue>(propertyName: string, newValue?: T, persistent = this._preferPersistence): void
|
|
185
198
|
{
|
|
186
199
|
const storage = persistent ? this._persistent : this._volatile;
|
|
187
200
|
|
|
@@ -194,7 +207,7 @@ export default class JsonStorage
|
|
|
194
207
|
* @param propertyName The name of the property to set.
|
|
195
208
|
* @param newValue The new value to set.
|
|
196
209
|
*/
|
|
197
|
-
public remember<T>(propertyName: string, newValue?: T): void
|
|
210
|
+
public remember<T extends JSONValue>(propertyName: string, newValue?: T): void
|
|
198
211
|
{
|
|
199
212
|
this._set<T>(this._volatile, propertyName, newValue);
|
|
200
213
|
}
|
|
@@ -205,7 +218,7 @@ export default class JsonStorage
|
|
|
205
218
|
* @param propertyName The name of the property to set.
|
|
206
219
|
* @param newValue The new value to set.
|
|
207
220
|
*/
|
|
208
|
-
public write<T>(propertyName: string, newValue?: T): void
|
|
221
|
+
public write<T extends JSONValue>(propertyName: string, newValue?: T): void
|
|
209
222
|
{
|
|
210
223
|
this._set<T>(this._persistent, propertyName, newValue);
|
|
211
224
|
}
|
|
@@ -239,5 +252,5 @@ export default class JsonStorage
|
|
|
239
252
|
this._persistent.removeItem(propertyName);
|
|
240
253
|
}
|
|
241
254
|
|
|
242
|
-
public get [Symbol.toStringTag]() { return "
|
|
255
|
+
public get [Symbol.toStringTag]() { return "JSONStorage"; }
|
|
243
256
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { ReferenceException } from "./exceptions/index.js";
|
|
2
|
+
|
|
3
|
+
export type Subscriber<A extends unknown[] = [], R = void> = (...args: A) => R;
|
|
4
|
+
|
|
5
|
+
export default class Publisher<A extends unknown[] = [], R = void>
|
|
6
|
+
{
|
|
7
|
+
protected _subscribers: Subscriber<A, R>[];
|
|
8
|
+
|
|
9
|
+
public constructor()
|
|
10
|
+
{
|
|
11
|
+
this._subscribers = [];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
public subscribe(subscriber: Subscriber<A, R>): () => void
|
|
15
|
+
{
|
|
16
|
+
this._subscribers.push(subscriber);
|
|
17
|
+
|
|
18
|
+
return () =>
|
|
19
|
+
{
|
|
20
|
+
const index = this._subscribers.indexOf(subscriber);
|
|
21
|
+
if (index < 0)
|
|
22
|
+
{
|
|
23
|
+
throw new ReferenceException("Unable to unsubscribe the required subscriber. " +
|
|
24
|
+
"The subscription was already unsubscribed.");
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
this._subscribers.splice(index, 1);
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
public publish(...args: A): R[]
|
|
32
|
+
{
|
|
33
|
+
return this._subscribers
|
|
34
|
+
.slice()
|
|
35
|
+
.map((subscriber) => subscriber(...args));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
public get [Symbol.toStringTag]() { return "Publisher"; }
|
|
39
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { TimeUnit } from "../../utils/date.js";
|
|
2
|
+
|
|
3
|
+
import { RangeException, RuntimeException } from "../exceptions/index.js";
|
|
4
|
+
import GameLoop from "../game-loop.js";
|
|
5
|
+
import Publisher from "../publisher.js";
|
|
6
|
+
|
|
7
|
+
export default class Clock extends GameLoop
|
|
8
|
+
{
|
|
9
|
+
protected _publisher: Publisher<[number], void>;
|
|
10
|
+
|
|
11
|
+
public constructor(fpsIfNotBrowser: number = TimeUnit.Second)
|
|
12
|
+
{
|
|
13
|
+
super((elapsedTime) => this._publisher.publish(elapsedTime), fpsIfNotBrowser);
|
|
14
|
+
|
|
15
|
+
this._publisher = new Publisher();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
public start(elapsedTime = 0): void
|
|
19
|
+
{
|
|
20
|
+
if (this._isRunning) { throw new RuntimeException("The clock has already been started."); }
|
|
21
|
+
|
|
22
|
+
super.start(elapsedTime);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
public stop(): void
|
|
26
|
+
{
|
|
27
|
+
if (!(this._isRunning)) { throw new RuntimeException("The clock hadn't yet started."); }
|
|
28
|
+
|
|
29
|
+
super.stop();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
public onTick(callback: (elapsedTime: number) => void, tickStep = 0): () => void
|
|
33
|
+
{
|
|
34
|
+
if (tickStep < 0) { throw new RangeException("The tick step must be a non-negative number."); }
|
|
35
|
+
if (tickStep === 0) { return this._publisher.subscribe(callback); }
|
|
36
|
+
|
|
37
|
+
let lastTick = 0;
|
|
38
|
+
|
|
39
|
+
return this._publisher.subscribe((elapsedTime: number) =>
|
|
40
|
+
{
|
|
41
|
+
if ((elapsedTime - lastTick) < tickStep) { return; }
|
|
42
|
+
|
|
43
|
+
callback(elapsedTime);
|
|
44
|
+
lastTick = elapsedTime;
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { TimeUnit } from "../../utils/date.js";
|
|
2
|
+
|
|
3
|
+
import { FatalErrorException, RangeException, RuntimeException } from "../exceptions/index.js";
|
|
4
|
+
import { DeferredPromise, SmartPromise } from "../promises/index.js";
|
|
5
|
+
|
|
6
|
+
import GameLoop from "../game-loop.js";
|
|
7
|
+
import Publisher from "../publisher.js";
|
|
8
|
+
|
|
9
|
+
export default class Countdown extends GameLoop
|
|
10
|
+
{
|
|
11
|
+
protected _deferrer?: DeferredPromise<void>;
|
|
12
|
+
protected _publisher: Publisher<[number], void>;
|
|
13
|
+
|
|
14
|
+
protected _duration: number;
|
|
15
|
+
public get duration(): number
|
|
16
|
+
{
|
|
17
|
+
return this._duration;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
public get remainingTime(): number
|
|
21
|
+
{
|
|
22
|
+
return this._duration - this.elapsedTime;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
public constructor(duration: number, fpsIfNotBrowser: number = TimeUnit.Second)
|
|
26
|
+
{
|
|
27
|
+
const callback = () =>
|
|
28
|
+
{
|
|
29
|
+
const remainingTime = this.remainingTime;
|
|
30
|
+
this._publisher.publish(remainingTime);
|
|
31
|
+
|
|
32
|
+
if (remainingTime <= 0) { this.stop(); }
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
super(callback, fpsIfNotBrowser);
|
|
36
|
+
|
|
37
|
+
this._publisher = new Publisher();
|
|
38
|
+
this._duration = duration;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
public start(remainingTime: number = this.duration): SmartPromise<void>
|
|
42
|
+
{
|
|
43
|
+
if (this._isRunning) { throw new RuntimeException("The countdown has already been started."); }
|
|
44
|
+
if (this._deferrer) { throw new FatalErrorException(); }
|
|
45
|
+
|
|
46
|
+
this._deferrer = new DeferredPromise();
|
|
47
|
+
super.start(this.duration - remainingTime);
|
|
48
|
+
|
|
49
|
+
return this._deferrer;
|
|
50
|
+
}
|
|
51
|
+
public stop(reason?: unknown): void
|
|
52
|
+
{
|
|
53
|
+
if (!(this._isRunning)) { throw new RuntimeException("The countdown hadn't yet started."); }
|
|
54
|
+
if (!(this._deferrer)) { throw new FatalErrorException(); }
|
|
55
|
+
|
|
56
|
+
super.stop();
|
|
57
|
+
|
|
58
|
+
if (reason !== undefined) { this._deferrer.reject(reason); }
|
|
59
|
+
else { this._deferrer.resolve(); }
|
|
60
|
+
|
|
61
|
+
this._deferrer = undefined;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
public onTick(callback: (remainingTime: number) => void, tickStep = 0): () => void
|
|
65
|
+
{
|
|
66
|
+
if (tickStep < 0) { throw new RangeException("The tick step must be a non-negative number."); }
|
|
67
|
+
if (tickStep === 0) { return this._publisher.subscribe(callback); }
|
|
68
|
+
|
|
69
|
+
let lastTick = 0;
|
|
70
|
+
|
|
71
|
+
return this._publisher.subscribe((remainingTime: number) =>
|
|
72
|
+
{
|
|
73
|
+
if ((lastTick - remainingTime) < tickStep) { return; }
|
|
74
|
+
|
|
75
|
+
callback(remainingTime);
|
|
76
|
+
lastTick = remainingTime;
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
package/src/models/types.ts
CHANGED
|
@@ -23,6 +23,13 @@ export type {
|
|
|
23
23
|
|
|
24
24
|
} from "./iterators/types.js";
|
|
25
25
|
|
|
26
|
+
export type {
|
|
27
|
+
JSONArray,
|
|
28
|
+
JSONObject,
|
|
29
|
+
JSONValue
|
|
30
|
+
|
|
31
|
+
} from "./json/types.js";
|
|
32
|
+
|
|
26
33
|
export type {
|
|
27
34
|
MaybePromise,
|
|
28
35
|
FulfilledHandler,
|
|
@@ -32,3 +39,5 @@ export type {
|
|
|
32
39
|
PromiseExecutor
|
|
33
40
|
|
|
34
41
|
} from "./promises/types.js";
|
|
42
|
+
|
|
43
|
+
export type { Subscriber } from "./publisher.js";
|
package/src/utils/date.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { SmartIterator } from "../models/index.js";
|
|
2
2
|
|
|
3
|
-
export enum
|
|
3
|
+
export enum TimeUnit
|
|
4
4
|
{
|
|
5
5
|
Millisecond = 1,
|
|
6
6
|
Second = 1000,
|
|
@@ -12,7 +12,7 @@ export enum DateUnit
|
|
|
12
12
|
Year = 365 * Day
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
export function dateDifference(start: string | Date, end: string | Date, unit =
|
|
15
|
+
export function dateDifference(start: string | Date, end: string | Date, unit = TimeUnit.Day): number
|
|
16
16
|
{
|
|
17
17
|
start = new Date(start);
|
|
18
18
|
end = new Date(end);
|
|
@@ -20,7 +20,7 @@ export function dateDifference(start: string | Date, end: string | Date, unit =
|
|
|
20
20
|
return Math.floor((end.getTime() - start.getTime()) / unit);
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
export function dateRange(start: string | Date, end: string | Date, offset =
|
|
23
|
+
export function dateRange(start: string | Date, end: string | Date, offset = TimeUnit.Day): SmartIterator<Date>
|
|
24
24
|
{
|
|
25
25
|
start = new Date(start);
|
|
26
26
|
end = new Date(end);
|
|
@@ -39,7 +39,7 @@ export function dateRange(start: string | Date, end: string | Date, offset = Dat
|
|
|
39
39
|
});
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
export function dateRound(date: string | Date, unit =
|
|
42
|
+
export function dateRound(date: string | Date, unit = TimeUnit.Day): Date
|
|
43
43
|
{
|
|
44
44
|
date = new Date(date);
|
|
45
45
|
|
package/src/utils/index.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import Random from "./random.js";
|
|
2
2
|
|
|
3
3
|
export { delay, nextAnimationFrame } from "./async.js";
|
|
4
|
-
export { dateDifference, dateRange, dateRound,
|
|
4
|
+
export { dateDifference, dateRange, dateRound, TimeUnit } from "./date.js";
|
|
5
5
|
export { loadScript } from "./dom.js";
|
|
6
6
|
export { chain, count, enumerate, range, shuffle, unique, zip } from "./iterator.js";
|
|
7
7
|
export { average, hash, sum } from "./math.js";
|