uneventful 0.0.9 → 0.0.11
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/README.md +2 -2
- package/dist/call-or-wait-EzXJX5Dq.mjs +111 -0
- package/dist/ext.d.ts +260 -0
- package/dist/ext.mjs +112 -0
- package/dist/{call-or-wait-DpJBh_pS.mjs → jobutils-Dvu-e99o.mjs} +64 -167
- package/dist/mod.d.ts +65 -5
- package/dist/mod.mjs +11 -9
- package/dist/shared.d.ts +109 -0
- package/dist/shared.mjs +52 -0
- package/dist/signals.d.ts +26 -4
- package/dist/signals.mjs +70 -47
- package/dist/{utils-P0JEpWXG.mjs → utils-cyEhnyp7.mjs} +16 -7
- package/dist/utils.d.ts +77 -26
- package/dist/utils.mjs +1 -2
- package/package.json +27 -11
- package/dist/call-or-wait-DpJBh_pS.mjs.map +0 -1
- package/dist/mod.mjs.map +0 -1
- package/dist/signals.mjs.map +0 -1
- package/dist/utils-P0JEpWXG.mjs.map +0 -1
- package/dist/utils.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -67,12 +67,12 @@ function supportDragDrop(parentNode: HTMLElement) {
|
|
|
67
67
|
return start(function*(job) {
|
|
68
68
|
const mouseDown = fromDomEvent(parentNode, "mousedown");
|
|
69
69
|
for (const {item: event, next} of yield *each(mouseDown)) {
|
|
70
|
-
if (event.target.matches(".drag-handle") {
|
|
70
|
+
if (event.target.matches(".drag-handle")) {
|
|
71
71
|
const dropTarget = yield *drag(event.target.closest(".draggable"));
|
|
72
72
|
// do something with the dropTarget here
|
|
73
73
|
}
|
|
74
74
|
yield next; // wait for next mousedown
|
|
75
|
-
}
|
|
75
|
+
}
|
|
76
76
|
});
|
|
77
77
|
}
|
|
78
78
|
```
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { g as getJob, H as currentJob, I as pulls, s as start, l as isValue, f as isError, e as markHandled } from './jobutils-Dvu-e99o.mjs';
|
|
2
|
+
import { i as isFunction } from './utils-cyEhnyp7.mjs';
|
|
3
|
+
|
|
4
|
+
function backpressure(inlet = defaultInlet) {
|
|
5
|
+
const job = getJob();
|
|
6
|
+
return (cb) => {
|
|
7
|
+
if (!job.result() && inlet.isOpen()) {
|
|
8
|
+
if (cb)
|
|
9
|
+
inlet.onReady(cb, job);
|
|
10
|
+
return inlet.isReady();
|
|
11
|
+
}
|
|
12
|
+
return false;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
const IsStream = "uneventful/is-stream";
|
|
16
|
+
function connect(src, sink, inlet) {
|
|
17
|
+
return getJob().connect(src, sink, inlet);
|
|
18
|
+
}
|
|
19
|
+
function throttle(job = currentJob) {
|
|
20
|
+
return new _Throttle(job);
|
|
21
|
+
}
|
|
22
|
+
class _Throttle {
|
|
23
|
+
/** @internal */
|
|
24
|
+
constructor(_job) {
|
|
25
|
+
this._job = _job;
|
|
26
|
+
/** @internal */
|
|
27
|
+
this._callbacks = void 0;
|
|
28
|
+
this._isReady = true;
|
|
29
|
+
this._isPulling = false;
|
|
30
|
+
}
|
|
31
|
+
isOpen() {
|
|
32
|
+
return !this._job?.result();
|
|
33
|
+
}
|
|
34
|
+
/** Is the connection ready to receive data? */
|
|
35
|
+
isReady() {
|
|
36
|
+
return this.isOpen() && this._isReady;
|
|
37
|
+
}
|
|
38
|
+
onReady(cb, job) {
|
|
39
|
+
if (!this.isOpen())
|
|
40
|
+
return this;
|
|
41
|
+
const _callbacks = this._callbacks ||= /* @__PURE__ */ new Map();
|
|
42
|
+
const unlink = job.release(() => _callbacks.delete(cb));
|
|
43
|
+
if (this.isReady() && this && !_callbacks.size) {
|
|
44
|
+
pulls.add(this);
|
|
45
|
+
}
|
|
46
|
+
_callbacks.set(cb, unlink);
|
|
47
|
+
return this;
|
|
48
|
+
}
|
|
49
|
+
pause() {
|
|
50
|
+
this._isReady = false;
|
|
51
|
+
return this;
|
|
52
|
+
}
|
|
53
|
+
doPull() {
|
|
54
|
+
if (this._isPulling)
|
|
55
|
+
return;
|
|
56
|
+
const { _callbacks } = this;
|
|
57
|
+
if (!_callbacks?.size)
|
|
58
|
+
return;
|
|
59
|
+
this._isPulling = true;
|
|
60
|
+
try {
|
|
61
|
+
for (let [cb, unlink] of _callbacks) {
|
|
62
|
+
if (!this.isReady())
|
|
63
|
+
break;
|
|
64
|
+
unlink();
|
|
65
|
+
_callbacks.delete(cb);
|
|
66
|
+
cb();
|
|
67
|
+
}
|
|
68
|
+
} finally {
|
|
69
|
+
this._isPulling = false;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
resume() {
|
|
73
|
+
if (this.isOpen()) {
|
|
74
|
+
this._isReady = true;
|
|
75
|
+
this.doPull();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
const defaultInlet = throttle();
|
|
80
|
+
function pipe() {
|
|
81
|
+
var v = arguments[0];
|
|
82
|
+
for (var i = 1; i < arguments.length; i++)
|
|
83
|
+
v = arguments[i](v);
|
|
84
|
+
return v;
|
|
85
|
+
}
|
|
86
|
+
function compose(...fns) {
|
|
87
|
+
return (val) => pipe(val, ...fns);
|
|
88
|
+
}
|
|
89
|
+
function into(...args) {
|
|
90
|
+
return (src) => src(...args);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function callOrWait(source, method, handler, noArgs) {
|
|
94
|
+
if (source && isFunction(source[method]))
|
|
95
|
+
return source[method]();
|
|
96
|
+
if (!isFunction(source))
|
|
97
|
+
mustBeSourceOrSignal();
|
|
98
|
+
return (source.length === 0 ? noArgs(source) : false) || start((job) => {
|
|
99
|
+
connect(source, (v) => handler(job, v)).do((r) => {
|
|
100
|
+
if (isValue(r))
|
|
101
|
+
job.throw(new Error("Stream ended"));
|
|
102
|
+
else if (isError(r))
|
|
103
|
+
job.throw(markHandled(r));
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
function mustBeSourceOrSignal() {
|
|
108
|
+
throw new TypeError("not a source or signal");
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export { IsStream as I, callOrWait as a, backpressure as b, connect as c, compose as d, into as i, mustBeSourceOrSignal as m, pipe as p, throttle as t };
|
package/dist/ext.d.ts
ADDED
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import { P as PlainFunction } from './types-N2ua11te.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* This module provides helpers for creating *extensions*: a way of extending
|
|
5
|
+
* objects with additional (possibly private) data or methods via a WeakMap.
|
|
6
|
+
*
|
|
7
|
+
* An extension property links a type of object (known as the "target") with a
|
|
8
|
+
* specific type of extension, via a factory function or class. When you use an
|
|
9
|
+
* extension property for a specific target, the extension is automatically
|
|
10
|
+
* created (via the factory) if it doesn't already exist.
|
|
11
|
+
*
|
|
12
|
+
* Extension properties and methods are created either by inheriting from the
|
|
13
|
+
* {@link Ext} class, or by wrapping factory functions with {@link ext}() or
|
|
14
|
+
* {@link method}().
|
|
15
|
+
*
|
|
16
|
+
* @module uneventful/ext
|
|
17
|
+
*
|
|
18
|
+
* @experimental
|
|
19
|
+
*
|
|
20
|
+
* @summary Tools for extending objects with extra state and behavior, without
|
|
21
|
+
* directly modifying them.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Create an extension accessor function that returns a memoized
|
|
26
|
+
* value for a given target object or function.
|
|
27
|
+
*
|
|
28
|
+
* (Memoization is done via WeakMap, so the cached extensions will be freed
|
|
29
|
+
* automatically once the target is garbage-collected.)
|
|
30
|
+
*
|
|
31
|
+
* @template Target The type of target object that will be extended. Both the
|
|
32
|
+
* passed-in factory function and the returned accessor function will take a
|
|
33
|
+
* parameter of this type, which must be an object or function type. (So it's
|
|
34
|
+
* suitable for weak referencing.)
|
|
35
|
+
*
|
|
36
|
+
* @template ExtType The type of extension that will be cached. Both the
|
|
37
|
+
* passed-in factory function and the returned accessor will return a value of
|
|
38
|
+
* this type.
|
|
39
|
+
*
|
|
40
|
+
* @param factory Function called with a target object or function and a weakmap
|
|
41
|
+
* to create a new extension for that target. The result is cached in the
|
|
42
|
+
* weakmap so the factory is called at most once per target (unless you alter
|
|
43
|
+
* the weakmap contents directly).
|
|
44
|
+
*
|
|
45
|
+
* @param map Optional: the weakmap to use to store the extensions. This allows
|
|
46
|
+
* you to manipulate the map contents (e.g. remove items or clear it) from
|
|
47
|
+
* outside the factory function. If no map is provided, one is created
|
|
48
|
+
* automatically, but then it's only accessible via the factory function's
|
|
49
|
+
* second parameter.
|
|
50
|
+
*
|
|
51
|
+
* @returns A function that always returns the same extension for a given target
|
|
52
|
+
* (assuming you don't alter the WeakMap), calling the factory function if an
|
|
53
|
+
* extension doesn't exist yet for that target.
|
|
54
|
+
*/
|
|
55
|
+
declare function ext<Target extends WeakKey, ExtType extends Object>(factory: (tgt: Target, map: WeakMap<Target, ExtType>) => ExtType, map?: WeakMap<Target, ExtType>): (tgt: Target) => ExtType;
|
|
56
|
+
/**
|
|
57
|
+
* Create an extension *method*: a function that invokes a memoized closure for
|
|
58
|
+
* a given target object or function.
|
|
59
|
+
*
|
|
60
|
+
* This function is almost identical to {@link ext}(), except that instead of an
|
|
61
|
+
* accessor function, this returns a function that will *call* the extension
|
|
62
|
+
* (method closure) corresponding to the target, passing along any extra
|
|
63
|
+
* arguments.
|
|
64
|
+
*
|
|
65
|
+
* This is useful when you want to create an extension type that only has one
|
|
66
|
+
* public method, and you'd rather not create a whole class for it: just set up
|
|
67
|
+
* its state as variables in your factory function and return a closure. Then,
|
|
68
|
+
* you can simply call `myMethod(target, ...args)`, which will look up the
|
|
69
|
+
* (possibly cached) closure for `target` and call it with `(...args)`. (You
|
|
70
|
+
* could do the same thing with an {@link ext}() accessor, but then the API
|
|
71
|
+
* would be `myMethod(target)(...args)`.)
|
|
72
|
+
*
|
|
73
|
+
* @template Target The type of target object that will be extended. Both the
|
|
74
|
+
* passed-in factory function and the returned accessor function will take a
|
|
75
|
+
* parameter of this type, which must be an object or function type. (So it's
|
|
76
|
+
* suitable for weak referencing.)
|
|
77
|
+
*
|
|
78
|
+
* @template Method The type of the closure that will be cached. The passed-in
|
|
79
|
+
* factory function must return a value of this type, and the resulting wrapper
|
|
80
|
+
* function will be of the same type but with an added initial `target`
|
|
81
|
+
* parameter. (Note: if this type has more than one call signature, only the
|
|
82
|
+
* *last* overload will be used in the resulting method signature, due to
|
|
83
|
+
* TypeScript compiler limitations.)
|
|
84
|
+
*
|
|
85
|
+
* @param factory Function called with a target object or function and a weakmap
|
|
86
|
+
* to create a method closure for that target. The result is cached in the
|
|
87
|
+
* weakmap so the factory is called at most once per target (unless you alter
|
|
88
|
+
* the weakmap contents directly).
|
|
89
|
+
*
|
|
90
|
+
* @param map Optional: the weakmap to use to store the method closures. This
|
|
91
|
+
* allows you to manipulate the map contents (e.g. remove items or clear it)
|
|
92
|
+
* from outside the factory function. If no map is provided, one is created
|
|
93
|
+
* automatically, but then it's only accessible via the factory function's
|
|
94
|
+
* second parameter.
|
|
95
|
+
*
|
|
96
|
+
* @returns A wrapper function that always invokes the same closure for a given
|
|
97
|
+
* target (assuming you don't alter the WeakMap), calling the factory function
|
|
98
|
+
* if a method closure doesn't exist yet for that target. When called, the
|
|
99
|
+
* wrapper function returns the result of calling the closure with the same
|
|
100
|
+
* arguments (minus an initial `target` argument).
|
|
101
|
+
*/
|
|
102
|
+
declare function method<Target extends object, Method extends PlainFunction>(factory: (tgt: Target, map: WeakMap<Target, Method>) => Method, map?: WeakMap<Target, Method>): (tgt: Target, ...args: Parameters<Method>) => ReturnType<Method>;
|
|
103
|
+
/** Helper types for working with {@link Ext} Subclasses */
|
|
104
|
+
declare namespace Ext {
|
|
105
|
+
/** Get the target type of an {@link Ext} subclass constructor */
|
|
106
|
+
type Target<T extends Ext.Class> = InstanceType<T>["of"];
|
|
107
|
+
/**
|
|
108
|
+
* Get the type of extension that will be returned by the static API.
|
|
109
|
+
*
|
|
110
|
+
* Defaults to the subclass instance type, but can be overridden by `declare
|
|
111
|
+
* readonly __type__: OtherType` in a subclass, so long as the
|
|
112
|
+
* {@link Ext.__new__ `__new__()`} method is also overridden to return that
|
|
113
|
+
* type.
|
|
114
|
+
*/
|
|
115
|
+
type Type<T extends Ext.Class> = InstanceType<T> extends {
|
|
116
|
+
__type__: infer R;
|
|
117
|
+
} ? (unknown extends R ? InstanceType<T> : R) : InstanceType<T>;
|
|
118
|
+
/** The type of weakmap passed to {@link Ext.__new__ `__new__()`} */
|
|
119
|
+
type Map<Class extends Ext.Class> = WeakMap<Ext.Target<Class>, any>;
|
|
120
|
+
/**
|
|
121
|
+
* The type constraint for static generics in the API; you probably won't use this directly.
|
|
122
|
+
*/
|
|
123
|
+
type Class = typeof Ext<WeakKey>;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* A base class for more complex extension types, providing static accessor and
|
|
127
|
+
* management APIs. (e.g. `MyExt.for(aTarget)`, `MyExt.delete(aTarget)`, etc.)
|
|
128
|
+
*
|
|
129
|
+
* To create an extension class, just subclass Ext with an appropriate target
|
|
130
|
+
* type, e.g.:
|
|
131
|
+
*
|
|
132
|
+
* ```ts
|
|
133
|
+
* class MyExt extends Ext<MyTargetType> {
|
|
134
|
+
* // ...
|
|
135
|
+
* }
|
|
136
|
+
* ```
|
|
137
|
+
* You can then use `MyExt.for()`, `.delete()`, `.has()`, etc. on instances of
|
|
138
|
+
* `MyTargetType`, to manage the `MyExt` instances attached to them.
|
|
139
|
+
*
|
|
140
|
+
* @template Target The type of target this extension will extend.
|
|
141
|
+
*
|
|
142
|
+
* @categoryDescription Extension Management
|
|
143
|
+
*
|
|
144
|
+
* Static methods for working with extensions of the subclass type, e.g.
|
|
145
|
+
* `MyExt.for(someTarget)`.
|
|
146
|
+
*
|
|
147
|
+
* @categoryDescription Lifecycle Hooks
|
|
148
|
+
*
|
|
149
|
+
* Instance and static members you can override to customize extension creation,
|
|
150
|
+
* deletion, target and return types.
|
|
151
|
+
*/
|
|
152
|
+
declare abstract class Ext<Target extends WeakKey = WeakKey> {
|
|
153
|
+
/**
|
|
154
|
+
* The target the extension was created for (set automatically by the base
|
|
155
|
+
* class constructor). You can narrow the target type either by extending
|
|
156
|
+
* `Ext<SomeType>` directly, or by using `declare readonly of: SomeType` in
|
|
157
|
+
* your subclass.
|
|
158
|
+
*
|
|
159
|
+
* @category Lifecycle Hooks
|
|
160
|
+
*/
|
|
161
|
+
readonly of: Target;
|
|
162
|
+
/**
|
|
163
|
+
* A "virtual" property you can override in subclasses to change the static
|
|
164
|
+
* interface's return type. For example, if a subclass does `declare readonly __type__:
|
|
165
|
+
* Promise<this>`, and overrides `__new__`() to return a promise, then the
|
|
166
|
+
* static APIs for the subclass (like `.for()`) will return promises instead
|
|
167
|
+
* of instances. (See the {@link __new__ `__new__`} method for more
|
|
168
|
+
* details.)
|
|
169
|
+
*
|
|
170
|
+
* Note: this property is not actually set by any code, so you can't do anything
|
|
171
|
+
* other than declare it. It's just a hack to work around TypeScript's limited
|
|
172
|
+
* type parameterization for static generics.
|
|
173
|
+
*
|
|
174
|
+
* @category Lifecycle Hooks
|
|
175
|
+
*/
|
|
176
|
+
readonly __type__: unknown;
|
|
177
|
+
/**
|
|
178
|
+
* @deprecated Use .for() instead!
|
|
179
|
+
*
|
|
180
|
+
* Never directly call the constructor of an Ext subclass except from the
|
|
181
|
+
* {@link __new__ `__new__()`} method - otherwise you run the risk of having
|
|
182
|
+
* multiple instances for the same target. (And it may accept invalid
|
|
183
|
+
* parameter values if you've redefined the type of the {@link Ext.of `of`}
|
|
184
|
+
* property in a sub-subclass.)
|
|
185
|
+
*/
|
|
186
|
+
constructor(of: Target);
|
|
187
|
+
/**
|
|
188
|
+
* Get or create an extension instance for the given target.
|
|
189
|
+
* @category Extension Management
|
|
190
|
+
*/
|
|
191
|
+
static for<Class extends Ext.Class>(this: Class, tgt: Ext.Target<Class>): Ext.Type<Class>;
|
|
192
|
+
/**
|
|
193
|
+
* Get the current extension instance for the given target, or `undefined`
|
|
194
|
+
* if there isn't one.
|
|
195
|
+
*
|
|
196
|
+
* @category Extension Management
|
|
197
|
+
*/
|
|
198
|
+
static get<Class extends Ext.Class>(this: Class, tgt: Ext.Target<Class>): Ext.Type<Class> | undefined;
|
|
199
|
+
/**
|
|
200
|
+
* Does an extension currently exist for the given target?
|
|
201
|
+
*
|
|
202
|
+
* @category Extension Management
|
|
203
|
+
*/
|
|
204
|
+
static has<Class extends Ext.Class>(this: Class, tgt: Ext.Target<Class>): boolean;
|
|
205
|
+
/**
|
|
206
|
+
* Delete the current extension for the given target (if one exists), after calling the
|
|
207
|
+
* {@link __del__ `__del__()`} method on it.
|
|
208
|
+
*
|
|
209
|
+
* @category Extension Management
|
|
210
|
+
*/
|
|
211
|
+
static delete<Class extends Ext.Class>(this: Class, tgt: Ext.Target<Class>): void;
|
|
212
|
+
/**
|
|
213
|
+
* This method is called by {@link for}() to create the extension instance
|
|
214
|
+
* for a target. You can override this method to customize instance creation
|
|
215
|
+
* behavior, e.g. to execute the constructor within a job, or create a
|
|
216
|
+
* promise for an extension instance to be asynchronously initiaized, etc.
|
|
217
|
+
*
|
|
218
|
+
* If you will be creating something other than an instance of the subclass,
|
|
219
|
+
* you must also redeclare the type of the {@link __type__ `__type__`}
|
|
220
|
+
* property. For example:
|
|
221
|
+
*
|
|
222
|
+
* ```ts
|
|
223
|
+
* class AsyncExt extends Ext {
|
|
224
|
+
* declare readonly __type__: Job<this>
|
|
225
|
+
*
|
|
226
|
+
* // simulate slow initialization
|
|
227
|
+
* *setup() { yield *sleep(100); return this; }
|
|
228
|
+
*
|
|
229
|
+
* static __new__<Class extends typeof AsyncExt>(
|
|
230
|
+
* tgt: Ext.Target<Class>, map: Ext.Map<Class>
|
|
231
|
+
* ) {
|
|
232
|
+
* const ext = new this(tgt);
|
|
233
|
+
* map.set(tgt, root.start(ext.setup()) as Ext.Type<Class>);
|
|
234
|
+
* }
|
|
235
|
+
* }
|
|
236
|
+
* ```
|
|
237
|
+
*
|
|
238
|
+
* Now, `AsyncExt.for(someTarget)` will create and cache a Job yielding an
|
|
239
|
+
* extension whose `setup()` has finished. (And subclasses of `AsyncExt`
|
|
240
|
+
* will share the same behavior, while being subtyped appropriately.)
|
|
241
|
+
*
|
|
242
|
+
* (Note: your `__new__` method *must* store what it created in the supplied
|
|
243
|
+
* map for the given target, and the value it sets must conform to the
|
|
244
|
+
* declared `__type__`, which is *not* checked for you by TypeScript!)
|
|
245
|
+
*
|
|
246
|
+
* @category Lifecycle Hooks
|
|
247
|
+
*/
|
|
248
|
+
static __new__<Class extends Ext.Class>(this: Class, tgt: Ext.Target<Class>, map: Ext.Map<Class>): void;
|
|
249
|
+
/**
|
|
250
|
+
* This method is called by {@link delete}() if it finds an existing
|
|
251
|
+
* extension for the target. You can override it in a subclass to do any
|
|
252
|
+
* necessary cleanup on the extension. (If you need to retrieve the
|
|
253
|
+
* extension, you can call .get() on the target.)
|
|
254
|
+
*
|
|
255
|
+
* @category Lifecycle Hooks
|
|
256
|
+
*/
|
|
257
|
+
static __del__<Class extends Ext.Class>(this: Class, _target: Ext.Target<Class>): void;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
export { Ext, ext, method };
|
package/dist/ext.mjs
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { s as setMap } from './utils-cyEhnyp7.mjs';
|
|
2
|
+
|
|
3
|
+
function ext(factory, map = /* @__PURE__ */ new WeakMap()) {
|
|
4
|
+
return (tgt) => map.has(tgt) ? map.get(tgt) : setMap(map, tgt, factory(tgt, map));
|
|
5
|
+
}
|
|
6
|
+
function method(factory, map = /* @__PURE__ */ new WeakMap()) {
|
|
7
|
+
return (tgt, ...args) => (map.get(tgt) ?? setMap(map, tgt, factory(tgt, map)))(...args);
|
|
8
|
+
}
|
|
9
|
+
const classMap = /* @__PURE__ */ ext((cls) => /* @__PURE__ */ new WeakMap());
|
|
10
|
+
class Ext {
|
|
11
|
+
/**
|
|
12
|
+
* @deprecated Use .for() instead!
|
|
13
|
+
*
|
|
14
|
+
* Never directly call the constructor of an Ext subclass except from the
|
|
15
|
+
* {@link __new__ `__new__()`} method - otherwise you run the risk of having
|
|
16
|
+
* multiple instances for the same target. (And it may accept invalid
|
|
17
|
+
* parameter values if you've redefined the type of the {@link Ext.of `of`}
|
|
18
|
+
* property in a sub-subclass.)
|
|
19
|
+
*/
|
|
20
|
+
constructor(of) {
|
|
21
|
+
this.of = of;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Get or create an extension instance for the given target.
|
|
25
|
+
* @category Extension Management
|
|
26
|
+
*/
|
|
27
|
+
static for(tgt) {
|
|
28
|
+
const map = classMap(this);
|
|
29
|
+
return map.get(tgt) ?? (this.__new__(tgt, map), map.get(tgt));
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Get the current extension instance for the given target, or `undefined`
|
|
33
|
+
* if there isn't one.
|
|
34
|
+
*
|
|
35
|
+
* @category Extension Management
|
|
36
|
+
*/
|
|
37
|
+
static get(tgt) {
|
|
38
|
+
return classMap(this).get(tgt);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Does an extension currently exist for the given target?
|
|
42
|
+
*
|
|
43
|
+
* @category Extension Management
|
|
44
|
+
*/
|
|
45
|
+
static has(tgt) {
|
|
46
|
+
return classMap(this).has(tgt);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Delete the current extension for the given target (if one exists), after calling the
|
|
50
|
+
* {@link __del__ `__del__()`} method on it.
|
|
51
|
+
*
|
|
52
|
+
* @category Extension Management
|
|
53
|
+
*/
|
|
54
|
+
static delete(tgt) {
|
|
55
|
+
const map = classMap(this);
|
|
56
|
+
if (map.has(tgt)) {
|
|
57
|
+
this.__del__(tgt);
|
|
58
|
+
map.delete(tgt);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* This method is called by {@link for}() to create the extension instance
|
|
63
|
+
* for a target. You can override this method to customize instance creation
|
|
64
|
+
* behavior, e.g. to execute the constructor within a job, or create a
|
|
65
|
+
* promise for an extension instance to be asynchronously initiaized, etc.
|
|
66
|
+
*
|
|
67
|
+
* If you will be creating something other than an instance of the subclass,
|
|
68
|
+
* you must also redeclare the type of the {@link __type__ `__type__`}
|
|
69
|
+
* property. For example:
|
|
70
|
+
*
|
|
71
|
+
* ```ts
|
|
72
|
+
* class AsyncExt extends Ext {
|
|
73
|
+
* declare readonly __type__: Job<this>
|
|
74
|
+
*
|
|
75
|
+
* // simulate slow initialization
|
|
76
|
+
* *setup() { yield *sleep(100); return this; }
|
|
77
|
+
*
|
|
78
|
+
* static __new__<Class extends typeof AsyncExt>(
|
|
79
|
+
* tgt: Ext.Target<Class>, map: Ext.Map<Class>
|
|
80
|
+
* ) {
|
|
81
|
+
* const ext = new this(tgt);
|
|
82
|
+
* map.set(tgt, root.start(ext.setup()) as Ext.Type<Class>);
|
|
83
|
+
* }
|
|
84
|
+
* }
|
|
85
|
+
* ```
|
|
86
|
+
*
|
|
87
|
+
* Now, `AsyncExt.for(someTarget)` will create and cache a Job yielding an
|
|
88
|
+
* extension whose `setup()` has finished. (And subclasses of `AsyncExt`
|
|
89
|
+
* will share the same behavior, while being subtyped appropriately.)
|
|
90
|
+
*
|
|
91
|
+
* (Note: your `__new__` method *must* store what it created in the supplied
|
|
92
|
+
* map for the given target, and the value it sets must conform to the
|
|
93
|
+
* declared `__type__`, which is *not* checked for you by TypeScript!)
|
|
94
|
+
*
|
|
95
|
+
* @category Lifecycle Hooks
|
|
96
|
+
*/
|
|
97
|
+
static __new__(tgt, map) {
|
|
98
|
+
map.set(tgt, new this(tgt, map));
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* This method is called by {@link delete}() if it finds an existing
|
|
102
|
+
* extension for the target. You can override it in a subclass to do any
|
|
103
|
+
* necessary cleanup on the extension. (If you need to retrieve the
|
|
104
|
+
* extension, you can call .get() on the target.)
|
|
105
|
+
*
|
|
106
|
+
* @category Lifecycle Hooks
|
|
107
|
+
*/
|
|
108
|
+
static __del__(_target) {
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export { Ext, ext, method };
|