@davidsouther/jiffies 1.0.0 → 1.1.0
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/build/assert.d.ts +23 -23
- package/build/assert.js +33 -33
- package/build/case.d.ts +1 -1
- package/build/case.js +5 -5
- package/build/components/button_bar.d.ts +8 -8
- package/build/components/button_bar.js +27 -27
- package/build/components/inline_edit.d.ts +12 -12
- package/build/components/inline_edit.js +48 -48
- package/build/components/logger.d.ts +6 -7
- package/build/components/logger.js +22 -22
- package/build/components/select.d.ts +13 -10
- package/build/components/select.js +3 -3
- package/build/components/test.d.ts +1 -1
- package/build/components/test.js +2 -2
- package/build/components/virtual_scroll.d.ts +40 -41
- package/build/components/virtual_scroll.js +94 -94
- package/build/components/virtual_scroll.test.d.ts +1 -1
- package/build/components/virtual_scroll.test.js +21 -21
- package/build/context.d.ts +15 -15
- package/build/context.js +43 -43
- package/build/context.test.d.ts +1 -1
- package/build/context.test.js +46 -46
- package/build/debounce.d.ts +1 -1
- package/build/debounce.js +7 -7
- package/build/display.d.ts +5 -5
- package/build/display.js +11 -11
- package/build/dom/css/border.d.ts +11 -11
- package/build/dom/css/border.js +27 -27
- package/build/dom/css/constants.d.ts +31 -31
- package/build/dom/css/constants.js +28 -28
- package/build/dom/css/core.d.ts +5 -5
- package/build/dom/css/core.js +24 -24
- package/build/dom/css/fstyle.d.ts +5 -5
- package/build/dom/css/fstyle.js +32 -32
- package/build/dom/css/sizing.d.ts +5 -5
- package/build/dom/css/sizing.js +10 -10
- package/build/dom/dom.d.ts +26 -27
- package/build/dom/dom.js +95 -95
- package/build/dom/fc.d.ts +14 -14
- package/build/dom/fc.js +36 -35
- package/build/dom/fc.test.d.ts +1 -1
- package/build/dom/fc.test.js +21 -21
- package/build/dom/form/form.app.d.ts +1 -1
- package/build/dom/form/form.app.js +23 -23
- package/build/dom/form/form.d.ts +26 -26
- package/build/dom/form/form.js +34 -34
- package/build/dom/form/form.test.js +1 -1
- package/build/dom/html.d.ts +113 -117
- package/build/dom/html.js +114 -114
- package/build/dom/html.test.d.ts +1 -1
- package/build/dom/html.test.js +58 -58
- package/build/dom/provide.d.ts +3 -3
- package/build/dom/provide.js +7 -7
- package/build/dom/router/link.d.ts +6 -6
- package/build/dom/router/link.js +3 -3
- package/build/dom/router/router.d.ts +12 -12
- package/build/dom/router/router.js +49 -49
- package/build/dom/svg.d.ts +64 -64
- package/build/dom/svg.js +65 -65
- package/build/dom/test.d.ts +1 -1
- package/build/dom/test.js +2 -2
- package/build/dom/types/css.d.ts +6612 -6612
- package/build/dom/types/css.js +23 -23
- package/build/dom/types/dom.js +1 -1
- package/build/dom/types/html.d.ts +616 -616
- package/build/dom/types/html.js +1 -1
- package/build/dom/xml.d.ts +1 -1
- package/build/dom/xml.js +4 -5
- package/build/equal.d.ts +5 -5
- package/build/equal.js +37 -37
- package/build/equal.test.d.ts +1 -1
- package/build/equal.test.js +20 -20
- package/build/flags.d.ts +7 -7
- package/build/flags.js +48 -48
- package/build/flags.test.d.ts +1 -1
- package/build/flags.test.js +35 -35
- package/build/fs.d.ts +48 -48
- package/build/fs.js +144 -144
- package/build/fs.test.d.ts +1 -1
- package/build/fs.test.js +43 -43
- package/build/generator.d.ts +1 -1
- package/build/generator.js +10 -10
- package/build/generator.test.d.ts +1 -1
- package/build/generator.test.js +24 -24
- package/build/is_browser.d.ts +1 -1
- package/build/is_browser.js +1 -1
- package/build/loader.d.mts +22 -22
- package/build/loader.mjs +35 -35
- package/build/lock.d.ts +1 -1
- package/build/lock.js +23 -23
- package/build/lock.test.d.ts +1 -1
- package/build/lock.test.js +16 -16
- package/build/log.d.ts +26 -26
- package/build/log.js +46 -46
- package/build/observable/observable.d.ts +83 -0
- package/build/observable/observable.js +148 -0
- package/build/observable/observable.test.d.ts +1 -0
- package/build/observable/observable.test.js +21 -0
- package/build/range.d.ts +1 -1
- package/build/range.js +7 -7
- package/build/result.d.ts +31 -31
- package/build/result.js +65 -65
- package/build/result.test.d.ts +1 -1
- package/build/result.test.js +71 -71
- package/build/safe.d.ts +1 -1
- package/build/safe.js +10 -10
- package/build/scope/describe.d.ts +18 -14
- package/build/scope/describe.js +61 -52
- package/build/scope/display/console.d.ts +2 -2
- package/build/scope/display/console.js +21 -21
- package/build/scope/display/dom.d.ts +3 -3
- package/build/scope/display/dom.js +26 -26
- package/build/scope/display/junit.d.ts +2 -2
- package/build/scope/display/junit.js +17 -17
- package/build/scope/execute.d.ts +12 -12
- package/build/scope/execute.js +85 -85
- package/build/scope/expect.d.ts +23 -23
- package/build/scope/expect.js +108 -108
- package/build/scope/fix.d.ts +4 -4
- package/build/scope/fix.js +22 -22
- package/build/scope/index.d.ts +3 -3
- package/build/scope/index.js +3 -3
- package/build/scope/scope.d.ts +17 -17
- package/build/scope/scope.js +1 -1
- package/build/server/http/apps.d.ts +5 -5
- package/build/server/http/apps.js +23 -23
- package/build/server/http/css.d.ts +5 -5
- package/build/server/http/css.js +50 -47
- package/build/server/http/index.d.ts +21 -21
- package/build/server/http/index.js +73 -73
- package/build/server/http/response.d.ts +4 -4
- package/build/server/http/response.js +40 -40
- package/build/server/http/sitemap.d.ts +2 -2
- package/build/server/http/sitemap.js +42 -42
- package/build/server/http/static.d.ts +2 -2
- package/build/server/http/static.js +21 -21
- package/build/server/http/typescript.d.ts +5 -5
- package/build/server/http/typescript.js +40 -40
- package/build/server/main.d.ts +2 -2
- package/build/server/main.js +9 -9
- package/build/test.d.mts +2 -2
- package/build/test.mjs +23 -23
- package/build/test_all.d.ts +7 -7
- package/build/test_all.js +18 -18
- package/build/transpile.d.mts +3 -3
- package/build/transpile.mjs +18 -18
- package/package.json +3 -3
- package/src/components/logger.ts +2 -3
- package/src/components/virtual_scroll.test.ts +4 -4
- package/src/components/virtual_scroll.ts +8 -8
- package/src/diff.test.ts +48 -0
- package/src/diff.ts +84 -0
- package/src/dom/dom.ts +73 -61
- package/src/dom/fc.ts +10 -9
- package/src/dom/html.test.ts +5 -5
- package/src/dom/html.ts +7 -10
- package/src/dom/observable.test.ts +43 -0
- package/src/dom/observable.ts +11 -0
- package/src/dom/router/router.ts +4 -4
- package/src/dom/test.ts +5 -1
- package/src/dom/xml.ts +1 -2
- package/src/index.html +6 -3
- package/src/observable/_notes +21 -8
- package/src/observable/event.ts +93 -0
- package/src/observable/observable.test.ts +73 -0
- package/src/observable/observable.ts +403 -0
- package/src/scope/describe.ts +14 -1
- package/src/scope/display/dom.ts +2 -2
- package/src/scope/execute.ts +2 -5
- package/src/server/http/css.ts +3 -1
- package/src/test_all.ts +10 -8
- package/src/observable/observable._js +0 -175
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
import { DEFAULT_LOGGER, Logger } from "../log.js";
|
|
2
|
+
|
|
3
|
+
export interface FullSubscriber<T, E> {
|
|
4
|
+
// (t: T): void | Promise<undefined>;
|
|
5
|
+
next?: (t: T) => void | Promise<undefined>;
|
|
6
|
+
error?: (e: E) => void | Promise<undefined>;
|
|
7
|
+
complete?: () => void | Promise<undefined>;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export type Subscriber<T, E> =
|
|
11
|
+
| FullSubscriber<T, E>
|
|
12
|
+
// | EventSubscriber<T, E>
|
|
13
|
+
| ((t: T) => void | Promise<undefined>);
|
|
14
|
+
|
|
15
|
+
export interface Subscription {
|
|
16
|
+
// (): void;
|
|
17
|
+
unsubscribe(): void;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface Observable<T, E = unknown> {
|
|
21
|
+
// (subscriber: Subscriber<T, E>): Subscription;
|
|
22
|
+
subscribe(subscriber: Subscriber<T, E>): Subscription;
|
|
23
|
+
pipe<T1>(o1: Subscriber<T, E> & Observable<T1, E>): Observable<T1, E>;
|
|
24
|
+
pipe<T1, T2>(
|
|
25
|
+
o1: Subscriber<T, E> & Observable<T1, E>,
|
|
26
|
+
o2: Subscriber<T1, E> & Observable<T2, E>
|
|
27
|
+
): Observable<T2, E>;
|
|
28
|
+
filter(fn: (t: T) => boolean): Observable<T, E>;
|
|
29
|
+
map<U>(fn: (t: T) => U): Observable<U, E>;
|
|
30
|
+
reduce<A>(fn: (acc: A, t: T) => A, init: A): Observable<A, E>;
|
|
31
|
+
replay(n: number): Observable<T, E>;
|
|
32
|
+
tap(s: Subscriber<T, E>): Observable<T, E>;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const Observable = {
|
|
36
|
+
of<T, E>(...ts: T[]): Observable<T, E> {
|
|
37
|
+
const subject = new Subject<T, E>();
|
|
38
|
+
(async function next() {
|
|
39
|
+
if (subject.cold) {
|
|
40
|
+
subject.onWarm(next);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (ts.length === 0) {
|
|
44
|
+
subject.complete();
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const t = ts.shift()!;
|
|
48
|
+
await subject.next(t);
|
|
49
|
+
next();
|
|
50
|
+
})();
|
|
51
|
+
return subject;
|
|
52
|
+
},
|
|
53
|
+
combineLatest<T1, T2, E>(
|
|
54
|
+
o1: Observable<T1, E>,
|
|
55
|
+
o2: Observable<T2, E>
|
|
56
|
+
): Observable<[T1, T2], E> {
|
|
57
|
+
let latestSubject = new Subject<[T1, T2], E>();
|
|
58
|
+
let o1Latest: T1;
|
|
59
|
+
let o2Latest: T2;
|
|
60
|
+
|
|
61
|
+
function next() {
|
|
62
|
+
if (o1Latest && o2Latest) {
|
|
63
|
+
latestSubject.next([o1Latest, o2Latest]);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function error(e: E) {
|
|
68
|
+
latestSubject.error(e);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function complete() {
|
|
72
|
+
latestSubject.complete();
|
|
73
|
+
o1sub.unsubscribe();
|
|
74
|
+
o2sub.unsubscribe();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
let o1sub = o1.subscribe({
|
|
78
|
+
next(t: T1) {
|
|
79
|
+
o1Latest = t;
|
|
80
|
+
next();
|
|
81
|
+
},
|
|
82
|
+
error,
|
|
83
|
+
complete,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
let o2sub = o2.subscribe({
|
|
87
|
+
next(t: T2) {
|
|
88
|
+
o2Latest = t;
|
|
89
|
+
next();
|
|
90
|
+
},
|
|
91
|
+
error,
|
|
92
|
+
complete,
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
return latestSubject;
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
export class Subject<T, E = unknown, T2 = T>
|
|
100
|
+
implements FullSubscriber<T, E>, Observable<T, E>
|
|
101
|
+
{
|
|
102
|
+
#coldWaiters = new Set<Function>();
|
|
103
|
+
#subscribers = new Set<FullSubscriber<T, E>>();
|
|
104
|
+
#complete = false;
|
|
105
|
+
|
|
106
|
+
get hot(): boolean {
|
|
107
|
+
return this.#subscribers.size > 0;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
get cold(): boolean {
|
|
111
|
+
return !this.hot;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
onWarm(fn: Function) {
|
|
115
|
+
if (this.cold) this.#coldWaiters.add(fn);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
next(t: T | T2): void | Promise<undefined> {
|
|
119
|
+
if (this.#complete)
|
|
120
|
+
throw new Error("Cannot call next on a completed subject");
|
|
121
|
+
return Promise.all(
|
|
122
|
+
[...this.#subscribers].map((s) => s.next?.(t as T))
|
|
123
|
+
).then(() => undefined);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
error(e: E): void | Promise<undefined> {
|
|
127
|
+
if (this.#complete)
|
|
128
|
+
throw new Error("Cannot call error on a completed subject");
|
|
129
|
+
return Promise.all([...this.#subscribers].map((s) => s.error?.(e))).then(
|
|
130
|
+
() => undefined
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
complete(): void | Promise<undefined> {
|
|
135
|
+
if (this.#complete)
|
|
136
|
+
throw new Error("Cannot call complete on a completed subject");
|
|
137
|
+
this.#complete = true;
|
|
138
|
+
const finished = Promise.all(
|
|
139
|
+
[...this.#subscribers].map((s) => s.complete?.())
|
|
140
|
+
);
|
|
141
|
+
this.#subscribers.clear(); // Free subscribers for garbage collection
|
|
142
|
+
return finished.then(() => undefined);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
subscribe(subscriber: Subscriber<T, E>): Subscription {
|
|
146
|
+
if (this.#complete)
|
|
147
|
+
throw new Error("Cannot call subscribe on a completed subject");
|
|
148
|
+
|
|
149
|
+
if (subscriber instanceof Function) {
|
|
150
|
+
subscriber = { next: subscriber };
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
this.#subscribers.add(subscriber);
|
|
154
|
+
|
|
155
|
+
[...this.#coldWaiters].forEach((w) => w());
|
|
156
|
+
this.#coldWaiters.clear();
|
|
157
|
+
|
|
158
|
+
return {
|
|
159
|
+
unsubscribe: () =>
|
|
160
|
+
this.#subscribers.delete(subscriber as FullSubscriber<T, E>),
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
pipe<T1>(o1: Subscriber<T, E> & Observable<T1, E>): Observable<T1, E>;
|
|
165
|
+
pipe<T1, T2>(
|
|
166
|
+
o1: Subscriber<T, E> & Observable<T1, E>,
|
|
167
|
+
o2: Subscriber<T1, E> & Observable<T2, E>
|
|
168
|
+
): Observable<T2, E>;
|
|
169
|
+
pipe(
|
|
170
|
+
...os: (Subscriber<unknown, unknown> & Observable<unknown, unknown>)[]
|
|
171
|
+
): Observable<unknown, E> {
|
|
172
|
+
this.subscribe(os[0]);
|
|
173
|
+
for (let i = 1; i < os.length; i++) {
|
|
174
|
+
os[i - 1].subscribe(os[i]);
|
|
175
|
+
}
|
|
176
|
+
return os[os.length - 1];
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
filter(fn: (t: T) => boolean): Observable<T, E> {
|
|
180
|
+
return this.pipe(operator.filter(fn));
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
map<U>(fn: (t: T) => U): Observable<U, E> {
|
|
184
|
+
return this.pipe(operator.map(fn));
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
reduce<A>(fn: (acc: A, t: T) => A, init: A): Observable<A, E> {
|
|
188
|
+
return this.pipe(operator.reduce(fn, init));
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
replay(n: number): Observable<T, E> {
|
|
192
|
+
return this.pipe(operator.replay(n));
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
tap(s: Subscriber<T, E>): Observable<T, E> {
|
|
196
|
+
return this.pipe(operator.tap(s));
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export class BehaviorSubject<T, E = unknown, T2 = T> extends Subject<T, E, T2> {
|
|
201
|
+
#current: T;
|
|
202
|
+
|
|
203
|
+
constructor(t: T) {
|
|
204
|
+
super();
|
|
205
|
+
this.#current = t;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
next(t: T | T2) {
|
|
209
|
+
this.#current = t as T;
|
|
210
|
+
return super.next(t);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
subscribe(subscriber: Subscriber<T, E>): Subscription {
|
|
214
|
+
if (subscriber instanceof Function) {
|
|
215
|
+
subscriber = { next: subscriber };
|
|
216
|
+
}
|
|
217
|
+
subscriber.next?.(this.#current);
|
|
218
|
+
return super.subscribe(subscriber);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
get current(): T {
|
|
222
|
+
return this.#current;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
export class ReplaySubject<T, E = unknown> extends Subject<T, E> {
|
|
227
|
+
#history: T[] = [];
|
|
228
|
+
|
|
229
|
+
constructor(private readonly n: number) {
|
|
230
|
+
super();
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
next(t: T) {
|
|
234
|
+
this.#history.push(t);
|
|
235
|
+
if (this.#history.length > this.n) {
|
|
236
|
+
this.#history.shift();
|
|
237
|
+
}
|
|
238
|
+
return super.next(t);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
subscribe(subscriber: Subscriber<T, E>): Subscription {
|
|
242
|
+
if (subscriber instanceof Function) {
|
|
243
|
+
subscriber = { next: subscriber };
|
|
244
|
+
}
|
|
245
|
+
const history = [...this.#history];
|
|
246
|
+
(function send() {
|
|
247
|
+
if (history.length == 0) return;
|
|
248
|
+
const t = history.shift()!;
|
|
249
|
+
subscriber.next?.(t);
|
|
250
|
+
new Promise(send);
|
|
251
|
+
})();
|
|
252
|
+
return super.subscribe(subscriber);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
export function eventListener<E extends Event>() {
|
|
257
|
+
const observable = new Subject<E, unknown>();
|
|
258
|
+
function listener(e: E) {
|
|
259
|
+
e.preventDefault();
|
|
260
|
+
observable.next(e);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return [observable, listener];
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
export class EventHandler<E extends Event> extends Subject<E> {
|
|
267
|
+
constructor(private readonly eventFn: (e: E) => void | Promise<undefined>) {
|
|
268
|
+
super();
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
next(e: E) {
|
|
272
|
+
e.preventDefault();
|
|
273
|
+
super.next(e);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
export const watch =
|
|
278
|
+
<T, E>(logger: Logger = DEFAULT_LOGGER) =>
|
|
279
|
+
(observable: Observable<T, E>) => {
|
|
280
|
+
observable.tap({
|
|
281
|
+
next(t: T) {
|
|
282
|
+
logger.info(t);
|
|
283
|
+
},
|
|
284
|
+
complete() {
|
|
285
|
+
logger.info("Observable completed");
|
|
286
|
+
},
|
|
287
|
+
error(e: E) {
|
|
288
|
+
logger.warn(e);
|
|
289
|
+
},
|
|
290
|
+
});
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
class MapOperator<T, U, E>
|
|
294
|
+
extends Subject<U, E, T>
|
|
295
|
+
implements FullSubscriber<T, E>, Observable<U, E>
|
|
296
|
+
{
|
|
297
|
+
constructor(private readonly mapFn: (t: T) => U) {
|
|
298
|
+
super();
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
next(t: T): void | Promise<undefined> {
|
|
302
|
+
return super.next(this.mapFn(t));
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
class FilterOperator<T, E>
|
|
307
|
+
extends Subject<T, E>
|
|
308
|
+
implements FullSubscriber<T, E>, Observable<T, E>
|
|
309
|
+
{
|
|
310
|
+
constructor(private readonly filterFn: (t: T) => boolean) {
|
|
311
|
+
super();
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
next(t: T): void | Promise<undefined> {
|
|
315
|
+
return this.filterFn(t) ? super.next(t) : undefined;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
class ReduceOperator<A, T, E> extends BehaviorSubject<A, E, T> {
|
|
320
|
+
constructor(private readonly fn: (acc: A, t: T) => A, init: A) {
|
|
321
|
+
super(init);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
next(t: T) {
|
|
325
|
+
return super.next(this.fn(this.current, t));
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
export class TakeUntilOperator<T, E> extends Subject<T, E> {
|
|
330
|
+
constructor(o: Observable<unknown, unknown>) {
|
|
331
|
+
super();
|
|
332
|
+
o.subscribe(() => this.complete());
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
export class TapOperator<T, E> extends Subject<T, E> {
|
|
336
|
+
private readonly subscriber: FullSubscriber<T, E>;
|
|
337
|
+
constructor(fn: Subscriber<T, E>) {
|
|
338
|
+
super();
|
|
339
|
+
this.subscriber = fn instanceof Function ? { next: fn } : fn;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
next(t: T) {
|
|
343
|
+
this.subscriber.next?.(t);
|
|
344
|
+
return super.next(t);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
error(e: E) {
|
|
348
|
+
this.subscriber.error?.(e);
|
|
349
|
+
return super.error(e);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
complete() {
|
|
353
|
+
this.subscriber.complete?.();
|
|
354
|
+
return super.complete();
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
class FirstOperator<T, E> extends Subject<T, E> {
|
|
359
|
+
next(t: T): void | Promise<undefined> {
|
|
360
|
+
const next = super.next(t);
|
|
361
|
+
this.complete();
|
|
362
|
+
return next;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
class LastOperator<T, E = Error> extends Subject<T, E> {
|
|
367
|
+
#latest?: T;
|
|
368
|
+
|
|
369
|
+
next(t: T) {
|
|
370
|
+
this.#latest = t;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
complete(): void | Promise<undefined> {
|
|
374
|
+
if (this.#latest !== undefined) {
|
|
375
|
+
super.next(this.#latest);
|
|
376
|
+
}
|
|
377
|
+
return super.complete();
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
export const filter = <T, E>(fn: (t: T) => boolean) =>
|
|
382
|
+
new FilterOperator<T, E>(fn);
|
|
383
|
+
export const first = <T, E>() => new FirstOperator<T, E>();
|
|
384
|
+
export const last = <T, E>() => new LastOperator<T, E>();
|
|
385
|
+
export const map = <T1, T2, E>(fn: (t: T1) => T2) =>
|
|
386
|
+
new MapOperator<T1, T2, E>(fn);
|
|
387
|
+
export const replay = <T, E>(n: number) => new ReplaySubject<T, E>(n);
|
|
388
|
+
export const reduce = <A, T, E>(fn: (acc: A, t: T) => A, init: A) =>
|
|
389
|
+
new ReduceOperator<A, T, E>(fn, init);
|
|
390
|
+
export const takeUntil = <T, E>(o: Observable<unknown, unknown>) =>
|
|
391
|
+
new TakeUntilOperator<T, E>(o);
|
|
392
|
+
export const tap = <T, E>(fn: Subscriber<T, E>) => new TapOperator<T, E>(fn);
|
|
393
|
+
|
|
394
|
+
export const operator = {
|
|
395
|
+
filter,
|
|
396
|
+
first,
|
|
397
|
+
last,
|
|
398
|
+
map,
|
|
399
|
+
replay,
|
|
400
|
+
reduce,
|
|
401
|
+
takeUntil,
|
|
402
|
+
tap,
|
|
403
|
+
};
|
package/src/scope/describe.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { assert } from "../assert.js";
|
|
1
2
|
import { getLogger } from "../log.js";
|
|
2
3
|
import { TestCase } from "./scope.js";
|
|
3
4
|
|
|
@@ -11,9 +12,10 @@ const logger = getLogger("scope");
|
|
|
11
12
|
const CASES: TestCase = {};
|
|
12
13
|
let cases = [CASES];
|
|
13
14
|
let totalCases = 0;
|
|
15
|
+
let skippedCases = 0;
|
|
14
16
|
|
|
15
17
|
function push(title: string) {
|
|
16
|
-
const next = (cases[0][title] = {});
|
|
18
|
+
const next = (cases[0][title] = cases[0][title] ?? {}) as TestCase;
|
|
17
19
|
cases.unshift(next);
|
|
18
20
|
}
|
|
19
21
|
|
|
@@ -29,6 +31,10 @@ export function getTotalCases() {
|
|
|
29
31
|
return totalCases;
|
|
30
32
|
}
|
|
31
33
|
|
|
34
|
+
export function getSkippedCases() {
|
|
35
|
+
return skippedCases;
|
|
36
|
+
}
|
|
37
|
+
|
|
32
38
|
export function describe(title: string, block: Function) {
|
|
33
39
|
logger.debug(`describe(${title})`);
|
|
34
40
|
push(title);
|
|
@@ -38,10 +44,17 @@ export function describe(title: string, block: Function) {
|
|
|
38
44
|
|
|
39
45
|
export function it(title: string, block: Function) {
|
|
40
46
|
logger.debug(`it(${title})`);
|
|
47
|
+
assert(cases[0][title] == undefined, `Block already has test ${title}`);
|
|
41
48
|
totalCases += 1;
|
|
42
49
|
cases[0][title] = block;
|
|
43
50
|
}
|
|
44
51
|
|
|
52
|
+
it.skip = (title: string, _block: Function) => {
|
|
53
|
+
logger.debug(`it.skip(${title})`);
|
|
54
|
+
totalCases += 1;
|
|
55
|
+
skippedCases += 1;
|
|
56
|
+
};
|
|
57
|
+
|
|
45
58
|
export function beforeEach(fn: () => void) {
|
|
46
59
|
cases[0][beforeeach] = fn;
|
|
47
60
|
}
|
package/src/scope/display/dom.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { isHTMLLogger, makeHTMLLogger } from "../../components/logger.js";
|
|
2
|
-
import { DOMElement
|
|
2
|
+
import { DOMElement } from "../../dom/dom.js";
|
|
3
3
|
import { DEFAULT_LOGGER, LEVEL } from "../../log.js";
|
|
4
4
|
import { getTotalCases } from "../describe.js";
|
|
5
5
|
import { flattenResults } from "../execute.js";
|
|
@@ -7,7 +7,7 @@ import { TestResult } from "../scope.js";
|
|
|
7
7
|
|
|
8
8
|
export function displayStatistics(
|
|
9
9
|
results: TestResult,
|
|
10
|
-
root:
|
|
10
|
+
root: DOMElement = document.body as DOMElement
|
|
11
11
|
) {
|
|
12
12
|
const { executed, failed } = results;
|
|
13
13
|
const logger = (() => {
|
package/src/scope/execute.ts
CHANGED
|
@@ -7,10 +7,7 @@ import {
|
|
|
7
7
|
} from "./describe.js";
|
|
8
8
|
import { TestFailed, TestPassed, TestResult, TestSummary } from "./scope.js";
|
|
9
9
|
|
|
10
|
-
export async function execute(
|
|
11
|
-
prefix = "",
|
|
12
|
-
cases = rootCases()
|
|
13
|
-
): Promise<TestResult> {
|
|
10
|
+
export async function execute(cases = rootCases()): Promise<TestResult> {
|
|
14
11
|
const beforeallfn = cases[beforeall] ?? (() => {});
|
|
15
12
|
const beforeeachfn = cases[beforeeach] ?? (() => {});
|
|
16
13
|
const afterallfn = cases[afterall] ?? (() => {});
|
|
@@ -42,7 +39,7 @@ export async function execute(
|
|
|
42
39
|
result[title] = { error: /** @type Error */ e };
|
|
43
40
|
}
|
|
44
41
|
} else if (block) {
|
|
45
|
-
const run = await execute(
|
|
42
|
+
const run = await execute(block);
|
|
46
43
|
result.executed += run.executed;
|
|
47
44
|
result.passed += run.passed;
|
|
48
45
|
result.failed += run.failed;
|
package/src/server/http/css.ts
CHANGED
|
@@ -2,8 +2,8 @@ import * as fs from "fs/promises";
|
|
|
2
2
|
import * as path from "path";
|
|
3
3
|
import { contentResponse } from "./response.js";
|
|
4
4
|
import { MiddlewareFactory } from "./index.js";
|
|
5
|
+
// @ts-ignore
|
|
5
6
|
import sass from "sass";
|
|
6
|
-
import { error, info } from "../../log.js";
|
|
7
7
|
const { compileStringAsync } = sass;
|
|
8
8
|
|
|
9
9
|
function render(source: string) {
|
|
@@ -20,6 +20,7 @@ async function compile(
|
|
|
20
20
|
vars: string
|
|
21
21
|
): Promise<string> {
|
|
22
22
|
vars = vars.substring(1).replaceAll("=", ":");
|
|
23
|
+
filename = filename.replaceAll("\\", "/"); // Normalize for dart-sass
|
|
23
24
|
const sassString = `// Using variables: ${vars}\n${vars};\n@import "${filename}";`;
|
|
24
25
|
return (await compileStringAsync(sassString, { loadPaths: [root] })).css;
|
|
25
26
|
}
|
|
@@ -36,6 +37,7 @@ export const cssFileServer: MiddlewareFactory =
|
|
|
36
37
|
Url.pathname.startsWith(`/${s}`)
|
|
37
38
|
);
|
|
38
39
|
// Expand url with found scope
|
|
40
|
+
Url.protocol = "file";
|
|
39
41
|
let url = scope ? Url.pathname.replace(scope[0], scope[1]) : Url.pathname;
|
|
40
42
|
let filename = path.join(root, url);
|
|
41
43
|
try {
|
package/src/test_all.ts
CHANGED
|
@@ -2,14 +2,6 @@
|
|
|
2
2
|
// discarded during transpilation.
|
|
3
3
|
import { describe, expect, it } from "./scope/index.js";
|
|
4
4
|
|
|
5
|
-
import "./context.test.js";
|
|
6
|
-
import "./equal.test.js";
|
|
7
|
-
import "./flags.test.js";
|
|
8
|
-
import "./fs.test.js";
|
|
9
|
-
import "./generator.test.js";
|
|
10
|
-
import "./lock.test.js";
|
|
11
|
-
import "./result.test.js";
|
|
12
|
-
|
|
13
5
|
describe("Test executor", () => {
|
|
14
6
|
it("matches equality", () => {
|
|
15
7
|
expect(1).toBe(1);
|
|
@@ -19,3 +11,13 @@ describe("Test executor", () => {
|
|
|
19
11
|
expect(() => expect(1).toBe(2)).toThrow();
|
|
20
12
|
});
|
|
21
13
|
});
|
|
14
|
+
|
|
15
|
+
import "./context.test.js";
|
|
16
|
+
import "./diff.test.js";
|
|
17
|
+
import "./equal.test.js";
|
|
18
|
+
import "./flags.test.js";
|
|
19
|
+
import "./fs.test.js";
|
|
20
|
+
import "./generator.test.js";
|
|
21
|
+
import "./lock.test.js";
|
|
22
|
+
import "./result.test.js";
|
|
23
|
+
import "./observable/observable.test.js";
|
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
// import { Observable, ReplaySubject } from "rxjs";
|
|
2
|
-
// import { tap } from "rxjs/operators";
|
|
3
|
-
import { display } from "../display.js";
|
|
4
|
-
import { DEFAULT_LOGGER, Logger } from "../log.js";
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* @template T
|
|
8
|
-
* @typedef Next
|
|
9
|
-
* @property {T} value
|
|
10
|
-
* @property {false} completed
|
|
11
|
-
* @property {false} failed
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* @template E
|
|
16
|
-
* @typedef Error
|
|
17
|
-
* @property {E} error
|
|
18
|
-
* @property {false} completed
|
|
19
|
-
* @property {true} failed
|
|
20
|
-
*/
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* @typedef Completed
|
|
24
|
-
* @property {true} completed
|
|
25
|
-
* @property {false} failed
|
|
26
|
-
*/
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* @typedef Failed
|
|
30
|
-
* @property {true} completed
|
|
31
|
-
* @property {true} failed
|
|
32
|
-
*/
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* @template T
|
|
36
|
-
* @template E
|
|
37
|
-
* @typedef {Next<T> | Error<E> | Completed | Failed} Event
|
|
38
|
-
*/
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* @template T
|
|
42
|
-
* @param {T} value
|
|
43
|
-
* @returns {Next<T>}
|
|
44
|
-
*/
|
|
45
|
-
export const next = (value) => ({ value, completed: false, failed: false });
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* @template E
|
|
49
|
-
* @param {E} e
|
|
50
|
-
* @returns {Error<E>}
|
|
51
|
-
*/
|
|
52
|
-
export const error = (e) => ({ error: e, completed: false, failed: true });
|
|
53
|
-
|
|
54
|
-
/** @returns {Completed} */
|
|
55
|
-
export const completed = () => ({ completed: true, failed: false });
|
|
56
|
-
|
|
57
|
-
/** @returns {Failed} */
|
|
58
|
-
export const failed = () => ({ completed: true, failed: true });
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* @template T
|
|
62
|
-
* @param {Event<T, unknown>} event
|
|
63
|
-
* @returns {event is Next<T>}
|
|
64
|
-
*/
|
|
65
|
-
export const isNext = (event) =>
|
|
66
|
-
!event.completed && !event.failed && event.value !== undefined;
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* @template E
|
|
70
|
-
* @param {Event<unknown, E>} event
|
|
71
|
-
* @returns {event is Error<E>}
|
|
72
|
-
*/
|
|
73
|
-
export const isError = (event) => event.failed && !event.completed;
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* @param {Event<unknown, unknown>} event
|
|
77
|
-
* @returns {event is Completed}
|
|
78
|
-
*/
|
|
79
|
-
export const isCompleted = (event) => event.completed && !event.failed;
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* @param {Event<unknown, unknown>} event
|
|
83
|
-
* @returns {event is Failed}
|
|
84
|
-
*/
|
|
85
|
-
export const isFailed = (event) => event.completed && event.failed;
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* @template T
|
|
89
|
-
* @param {T|Event<T, unknown>} t
|
|
90
|
-
* @returns {t is Event<T, unknown>}
|
|
91
|
-
*/
|
|
92
|
-
export const isEvent = (t) => {
|
|
93
|
-
/* @type unknown */
|
|
94
|
-
const b = t;
|
|
95
|
-
return isNext(b) || isError(b) || isCompleted(b);
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* @template T
|
|
100
|
-
* @param {(T|Event<T, unknown>)[]} a
|
|
101
|
-
* @returns {(Event<T, unknown>)[]}
|
|
102
|
-
*/
|
|
103
|
-
export const asEvents = (a) => a.map((e) => (isEvent(e) ? e : next(e)));
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* @template T
|
|
107
|
-
* @param {Event<T, unknown>} event
|
|
108
|
-
* @returns {string}
|
|
109
|
-
*/
|
|
110
|
-
const marble = (event) =>
|
|
111
|
-
isError(event)
|
|
112
|
-
? "X"
|
|
113
|
-
: isFailed(event)
|
|
114
|
-
? "!"
|
|
115
|
-
: isCompleted(event)
|
|
116
|
-
? "|"
|
|
117
|
-
: `(${display(event.value)})`;
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* @template T
|
|
121
|
-
* @param {(Event<T, unknown>)[]} events
|
|
122
|
-
* @returns string
|
|
123
|
-
*/
|
|
124
|
-
export const marbles = (events) => `:${events.map(marble).join("-")}`;
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* @template T
|
|
128
|
-
* @template E
|
|
129
|
-
* @param {Observable<T>} input$
|
|
130
|
-
* @returns {(Event<T, E>)[]}
|
|
131
|
-
*/
|
|
132
|
-
export const collect = (input$) => {
|
|
133
|
-
/** @type {(Event<T, E>)[]} */
|
|
134
|
-
const collected = [];
|
|
135
|
-
|
|
136
|
-
const subscription = input$.subscribe({
|
|
137
|
-
next: (/* @type T */ x) => collected.push(next(x)),
|
|
138
|
-
error: (/* @type E */ e) => collected.push(error(e)),
|
|
139
|
-
complete: () => collected.push(completed()),
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
subscription.unsubscribe();
|
|
143
|
-
|
|
144
|
-
return collected;
|
|
145
|
-
};
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* @template T
|
|
149
|
-
* @param {Logger} logger
|
|
150
|
-
* @returns {(o: Observable<T>) => Observable<T>}
|
|
151
|
-
*/
|
|
152
|
-
export const watch =
|
|
153
|
-
(logger = DEFAULT_LOGGER) =>
|
|
154
|
-
/**
|
|
155
|
-
* @template E
|
|
156
|
-
* @param {Observable<T>} observable
|
|
157
|
-
* @returns {Observable<T>}
|
|
158
|
-
*/
|
|
159
|
-
(observable) => {
|
|
160
|
-
observable.pipe(
|
|
161
|
-
tap({
|
|
162
|
-
next(/* @type T */ t) {
|
|
163
|
-
logger.info(t);
|
|
164
|
-
},
|
|
165
|
-
complete() {
|
|
166
|
-
logger.info("Observable completed");
|
|
167
|
-
},
|
|
168
|
-
error(/* @type E */ e) {
|
|
169
|
-
logger.warn(e);
|
|
170
|
-
},
|
|
171
|
-
})
|
|
172
|
-
);
|
|
173
|
-
|
|
174
|
-
return observable;
|
|
175
|
-
};
|