uneventful 0.0.4 → 0.0.6

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.
@@ -0,0 +1,590 @@
1
+ import { N as swapCtx, O as nullCtx, P as ruleQueue, c as backpressure, Q as defaultQ, I as IsStream, q as current, R as currentRule, S as makeCtx, T as ruleStops, d as defer, D as makeJob, j as isError, h as markHandled, e as detached, g as getJob, w as reject, b as resolve, U as freeCtx, o as callOrWait } from './call-or-wait-DHkBd_bM.mjs';
2
+ import { apply, setMap, CallableObject } from './utils.mjs';
3
+
4
+ class WriteConflict extends Error {
5
+ }
6
+ class CircularDependency extends Error {
7
+ }
8
+ var timestamp = 1;
9
+ const fntrackers = /* @__PURE__ */ new WeakMap();
10
+ const obtrackers = /* @__PURE__ */ new WeakMap();
11
+ const streamtrackers = /* @__PURE__ */ new WeakMap();
12
+ const dirtyStack = [];
13
+ function markDependentsDirty(cell) {
14
+ const latestSource = cell.latestSource = timestamp;
15
+ for (; cell; cell = dirtyStack.pop()) {
16
+ for (let sub = cell.subscribers; sub; sub = sub.nT) {
17
+ const tgt = sub.tgt;
18
+ if (tgt.latestSource >= latestSource)
19
+ continue;
20
+ tgt.latestSource = latestSource;
21
+ tgt.queue?.add(tgt);
22
+ if (tgt.subscribers)
23
+ dirtyStack.push(tgt);
24
+ }
25
+ }
26
+ }
27
+ function returnValue() {
28
+ return this.value;
29
+ }
30
+ function throwValue() {
31
+ throw this.value;
32
+ }
33
+ const notCaughtUp = [];
34
+ var freesubs;
35
+ function mksub(source, target) {
36
+ let sub = freesubs;
37
+ if (sub) {
38
+ freesubs = sub.old;
39
+ sub.src = source;
40
+ sub.nS = void 0;
41
+ sub.pS = target.sources;
42
+ sub.tgt = target;
43
+ sub.nT = sub.pT = void 0;
44
+ sub.ts = source.lastChanged;
45
+ sub.old = source.adding;
46
+ } else
47
+ sub = {
48
+ src: source,
49
+ nS: void 0,
50
+ pS: target.sources,
51
+ tgt: target,
52
+ nT: void 0,
53
+ pT: void 0,
54
+ ts: source.lastChanged,
55
+ old: source.adding
56
+ };
57
+ if (target.sources)
58
+ target.sources.nS = sub;
59
+ target.sources = sub;
60
+ source.adding = sub;
61
+ if (target.subscribers || target.queue)
62
+ source.subscribe(sub);
63
+ }
64
+ function delsub(sub) {
65
+ sub.src.unsubscribe(sub);
66
+ if (sub.nS)
67
+ sub.nS.pS = sub.pS;
68
+ if (sub.pS)
69
+ sub.pS.nS = sub.nS;
70
+ sub.src = sub.tgt = sub.nS = sub.pS = sub.nT = sub.pT = void 0;
71
+ sub.old = freesubs;
72
+ freesubs = sub;
73
+ }
74
+ function subscribeAll(s) {
75
+ if (s)
76
+ while (s.pS)
77
+ s = s.pS;
78
+ else
79
+ return;
80
+ for (; s; s = s.nS)
81
+ s.src.subscribe(s);
82
+ }
83
+ function unsubscribeAll(s) {
84
+ if (s)
85
+ while (s.pS)
86
+ s = s.pS;
87
+ else
88
+ return;
89
+ for (; s; s = s.nS)
90
+ s.src.unsubscribe(s);
91
+ }
92
+ const sentinel = {};
93
+ class Cell {
94
+ constructor() {
95
+ this.value = void 0;
96
+ // the value
97
+ this.validThrough = 0;
98
+ // timestamp of most recent validation or recalculation
99
+ this.lastChanged = 0;
100
+ // timestamp of last value change
101
+ this.latestSource = timestamp;
102
+ // max lastChanged of this cell or any ancestor source
103
+ this.flags = 0;
104
+ this.ctx = void 0;
105
+ /** The subscription being added during the current calculation - used for uniqueness */
106
+ this.adding = void 0;
107
+ /** Linked list of sources */
108
+ this.sources = void 0;
109
+ /** Linked list of targets */
110
+ this.subscribers = void 0;
111
+ this.queue = void 0;
112
+ /**
113
+ * The computation to be performed (if {@link Is.Compute}) or the
114
+ * setup/teardown for {@link Is.Stream}.
115
+ */
116
+ this.compute = returnValue;
117
+ }
118
+ stream(sink, _conn, inlet) {
119
+ let lastValue = sentinel;
120
+ Cell.mkRule(() => {
121
+ const val = this.getValue();
122
+ if (val !== lastValue) {
123
+ const old = swapCtx(nullCtx);
124
+ try {
125
+ sink(lastValue = val);
126
+ } finally {
127
+ swapCtx(old);
128
+ }
129
+ }
130
+ }, inlet ? ruleQueue(backpressure(inlet)) : defaultQ);
131
+ return IsStream;
132
+ }
133
+ getValue() {
134
+ if (arguments.length)
135
+ return apply(this.stream, this, arguments);
136
+ this.catchUp();
137
+ const dep = current.cell;
138
+ if (dep && this.flags & 132 /* Variable */) {
139
+ if (this.flags & 32 /* Running */)
140
+ throw new CircularDependency("Cached function dependency cycle");
141
+ let s = this.adding;
142
+ if (!s || s.tgt !== dep) {
143
+ mksub(this, dep);
144
+ } else {
145
+ if (s.ts === -1) {
146
+ s.ts = this.lastChanged;
147
+ if (s.nS) {
148
+ s.nS.pS = s.pS;
149
+ if (s.pS)
150
+ s.pS.nS = s.nS;
151
+ s.nS = void 0;
152
+ s.pS = dep.sources;
153
+ dep.sources.nS = s;
154
+ dep.sources = s;
155
+ }
156
+ }
157
+ }
158
+ }
159
+ if (this.flags & 16 /* Error */)
160
+ throw this.value;
161
+ return this.value;
162
+ }
163
+ shouldWrite(changed) {
164
+ const cell = current.cell || currentRule;
165
+ if (cell) {
166
+ if (!cell.queue)
167
+ throw new WriteConflict("Side-effects not allowed outside rules");
168
+ if (this.adding && this.adding.tgt === cell)
169
+ throw new CircularDependency("Can't update direct dependency");
170
+ if (this.validThrough === timestamp || this.hasBeenRead())
171
+ throw new WriteConflict("Value already used");
172
+ } else if (changed) {
173
+ if (this.validThrough === timestamp || notCaughtUp.length) {
174
+ ++timestamp;
175
+ notCaughtUp.length = 0;
176
+ }
177
+ } else {
178
+ return false;
179
+ }
180
+ this.lastChanged === timestamp || markDependentsDirty(this);
181
+ if (this.sources)
182
+ this.sources.ts = 0;
183
+ return true;
184
+ }
185
+ setValue(val, isErr) {
186
+ if (this.shouldWrite(val !== this.value || isErr !== !!(this.flags & 16 /* Error */))) {
187
+ this.value = val;
188
+ this.lastChanged = timestamp;
189
+ this.flags = isErr ? this.flags | 16 /* Error */ : this.flags & ~16 /* Error */;
190
+ }
191
+ this.compute = isErr ? throwValue : returnValue;
192
+ }
193
+ setCalc(compute) {
194
+ if (this.shouldWrite(compute !== this.compute)) {
195
+ this.flags |= 4 /* Compute */;
196
+ this.compute = compute;
197
+ this.ctx ||= makeCtx(null, this);
198
+ }
199
+ }
200
+ hasBeenRead() {
201
+ if (notCaughtUp.length) {
202
+ for (const cell of notCaughtUp)
203
+ cell.catchUp();
204
+ notCaughtUp.length = 0;
205
+ }
206
+ return this.validThrough === timestamp;
207
+ }
208
+ catchUp() {
209
+ const { validThrough } = this;
210
+ if (validThrough === timestamp)
211
+ return;
212
+ this.validThrough = timestamp;
213
+ if (!(this.flags & 4 /* Compute */))
214
+ return;
215
+ if (this.sources) {
216
+ for (let sub = this.sources; sub; sub = sub.nS) {
217
+ const s = sub.src;
218
+ if (sub.ts !== s.lastChanged)
219
+ return this.doRecalc();
220
+ if (s.subscribers && s.latestSource <= validThrough)
221
+ continue;
222
+ s.catchUp();
223
+ if (s.lastChanged > validThrough) {
224
+ return this.doRecalc();
225
+ }
226
+ }
227
+ for (let sub = this.sources; sub; sub = sub.nS) {
228
+ if (sub.src.validThrough !== timestamp)
229
+ notCaughtUp.push(sub.src);
230
+ }
231
+ } else
232
+ return this.doRecalc();
233
+ }
234
+ doRecalc() {
235
+ const oldCtx = swapCtx(this.ctx);
236
+ for (let sub = this.sources; sub; sub = sub.nS) {
237
+ sub.ts = -1;
238
+ sub.old = sub.src.adding;
239
+ sub.src.adding = sub;
240
+ this.sources = sub;
241
+ }
242
+ this.flags |= 32 /* Running */;
243
+ try {
244
+ try {
245
+ const future = this.compute();
246
+ if (future !== this.value || !this.lastChanged || this.flags & 16 /* Error */) {
247
+ this.value = future;
248
+ this.lastChanged = timestamp;
249
+ }
250
+ this.flags &= ~16 /* Error */;
251
+ } catch (e) {
252
+ this.flags |= 16 /* Error */;
253
+ this.value = e;
254
+ this.lastChanged = timestamp;
255
+ if (currentRule === this)
256
+ throw e;
257
+ }
258
+ } finally {
259
+ this.flags &= ~32 /* Running */;
260
+ swapCtx(oldCtx);
261
+ let head;
262
+ for (let sub = this.sources; sub; ) {
263
+ const pS = sub.pS;
264
+ sub.src.adding = sub.old;
265
+ sub.old = void 0;
266
+ if (sub.ts === -1)
267
+ delsub(sub);
268
+ else
269
+ head = sub;
270
+ sub = pS;
271
+ }
272
+ this.sources = head;
273
+ if (!head) {
274
+ this.flags &= ~4 /* Compute */;
275
+ }
276
+ }
277
+ }
278
+ stop() {
279
+ this.setQ(null);
280
+ ruleStops.get(this)?.();
281
+ }
282
+ setQ(queue = defaultQ) {
283
+ queue || this.subscribers || unsubscribeAll(this.sources);
284
+ if (this.queue) {
285
+ if (this.queue.has(this))
286
+ queue?.add(this);
287
+ this.queue.delete(this);
288
+ queue || this.ctx.job?.restart();
289
+ } else if (queue) {
290
+ queue.add(this);
291
+ this.subscribers || subscribeAll(this.sources);
292
+ }
293
+ this.queue = queue;
294
+ }
295
+ subscribe(sub) {
296
+ if (!this.subscribers) {
297
+ this.queue || subscribeAll(this.sources);
298
+ if ((this.flags & 320 /* Demand */) === 64 /* Stream */)
299
+ this.updateDemand();
300
+ }
301
+ if (this.subscribers !== sub && !sub.pT) {
302
+ sub.nT = this.subscribers;
303
+ if (this.subscribers)
304
+ this.subscribers.pT = sub;
305
+ this.subscribers = sub;
306
+ }
307
+ }
308
+ unsubscribe(sub) {
309
+ if (sub.nT)
310
+ sub.nT.pT = sub.pT;
311
+ if (sub.pT)
312
+ sub.pT.nT = sub.nT;
313
+ if (this.subscribers === sub)
314
+ this.subscribers = sub.nT;
315
+ sub.nT = sub.pT = void 0;
316
+ if (!this.subscribers) {
317
+ this.queue || unsubscribeAll(this.sources);
318
+ if ((this.flags & 320 /* Demand */) === 64 /* Stream */)
319
+ this.updateDemand();
320
+ }
321
+ }
322
+ static mkValue(val) {
323
+ const cell = new Cell();
324
+ cell.flags = 128 /* Mutable */;
325
+ cell.value = val;
326
+ cell.lastChanged = timestamp;
327
+ return cell;
328
+ }
329
+ updateDemand() {
330
+ this.flags |= 256 /* Demanded */;
331
+ defer(streamtrackers.get(this));
332
+ }
333
+ static mkStream(src, val) {
334
+ const cell = this.mkValue(val);
335
+ cell.flags |= 64 /* Stream */;
336
+ const ctx = makeCtx();
337
+ const write = (v) => {
338
+ cell.setValue(v, false);
339
+ };
340
+ let job;
341
+ streamtrackers.set(cell, () => {
342
+ if (!(cell.flags & 256 /* Demanded */))
343
+ return;
344
+ cell.flags &= ~256 /* Demanded */;
345
+ if (!cell.subscribers) {
346
+ write(val);
347
+ job?.end();
348
+ return;
349
+ }
350
+ if (job)
351
+ return;
352
+ job = ctx.job = makeJob().do((r) => {
353
+ if (isError(r)) {
354
+ cell.setValue(markHandled(r), true);
355
+ } else {
356
+ cell.setValue(val, false);
357
+ }
358
+ ctx.job = job = void 0;
359
+ });
360
+ const old = swapCtx(ctx);
361
+ try {
362
+ src(write, job);
363
+ } catch (e) {
364
+ job.end();
365
+ detached.asyncThrow(e);
366
+ } finally {
367
+ swapCtx(old);
368
+ }
369
+ });
370
+ return cell;
371
+ }
372
+ recalcWhen(fnOrKey, fn) {
373
+ let trackers = fn ? obtrackers.get(fn) || setMap(obtrackers, fn, /* @__PURE__ */ new WeakMap()) : fntrackers;
374
+ let signal = trackers.get(fnOrKey);
375
+ if (!signal) {
376
+ const src = fn ? fn(fnOrKey) : fnOrKey;
377
+ let ct = 0;
378
+ const c = Cell.mkStream((s) => (src(() => s(++ct)), IsStream), ct);
379
+ trackers.set(fnOrKey, signal = c.getValue.bind(c));
380
+ }
381
+ signal();
382
+ }
383
+ static mkCached(compute) {
384
+ const cell = new Cell();
385
+ cell.compute = compute;
386
+ cell.ctx = makeCtx(null, cell);
387
+ cell.flags = 4 /* Compute */;
388
+ return cell;
389
+ }
390
+ static mkRule(fn, q) {
391
+ const cell = new Cell(), outer = getJob(), job = makeJob(), stop = cell.stop.bind(cell);
392
+ outer === detached || ruleStops.set(cell, outer.release(stop));
393
+ cell.compute = () => {
394
+ try {
395
+ job.restart().must(fn(stop));
396
+ cell.lastChanged = timestamp;
397
+ } catch (e) {
398
+ stop();
399
+ throw e;
400
+ }
401
+ };
402
+ cell.ctx = makeCtx(job, cell);
403
+ cell.flags = 4 /* Compute */;
404
+ cell.setQ(q);
405
+ return stop;
406
+ }
407
+ }
408
+
409
+ class RF extends CallableObject {
410
+ constructor(q) {
411
+ super((fn) => Cell.mkRule(fn, q));
412
+ }
413
+ get stop() {
414
+ if (currentRule)
415
+ return currentRule.stop.bind(currentRule);
416
+ throw new Error("No rule active");
417
+ }
418
+ if(condition, action) {
419
+ const cond = Cell.mkCached(() => !!condition());
420
+ return this(() => cond.getValue() ? action() : void 0);
421
+ }
422
+ get method() {
423
+ const self = this;
424
+ return (fn, _ctxOrName, desc) => {
425
+ if (desc)
426
+ return void (desc.value = this.method(desc.value));
427
+ return function(...args) {
428
+ return self(() => apply(fn, this, args));
429
+ };
430
+ };
431
+ }
432
+ factory(scheduleFn) {
433
+ return factories.get(scheduleFn) || setMap(factories, scheduleFn, new RF(ruleQueue(scheduleFn)));
434
+ }
435
+ detached(fn) {
436
+ return detached.run(this, fn);
437
+ }
438
+ setScheduler(scheduleFn) {
439
+ if (currentRule)
440
+ currentRule.setQ(scheduleFn ? ruleQueue(scheduleFn) : defaultQ);
441
+ else
442
+ throw new Error("No rule active");
443
+ }
444
+ }
445
+ const factories = /* @__PURE__ */ new WeakMap();
446
+ const rule = RF.prototype.factory(defer);
447
+ function runRules(scheduleFn) {
448
+ (scheduleFn ? ruleQueue(scheduleFn) : defaultQ).flush();
449
+ }
450
+
451
+ class SignalImpl extends CallableObject {
452
+ constructor(_c) {
453
+ super(_c.getValue.bind(_c));
454
+ this._c = _c;
455
+ }
456
+ get value() {
457
+ return this();
458
+ }
459
+ valueOf() {
460
+ return this();
461
+ }
462
+ toString() {
463
+ return "" + this();
464
+ }
465
+ toJSON() {
466
+ return this();
467
+ }
468
+ peek() {
469
+ return peek(this);
470
+ }
471
+ asReadonly() {
472
+ return this;
473
+ }
474
+ withSet(set) {
475
+ return Object.assign(new WritableImpl(this._c), { set });
476
+ }
477
+ *"uneventful.next"() {
478
+ return yield (r) => {
479
+ let seen = false, res;
480
+ rule((stop) => {
481
+ try {
482
+ res = this();
483
+ } catch (e) {
484
+ stop();
485
+ defer(reject.bind(null, r, e));
486
+ }
487
+ if (seen) {
488
+ stop();
489
+ defer(resolve.bind(null, r, res));
490
+ }
491
+ seen = true;
492
+ });
493
+ };
494
+ }
495
+ *"uneventful.until"() {
496
+ return yield (r) => {
497
+ let res;
498
+ try {
499
+ res = this();
500
+ } catch (e) {
501
+ reject(r, e);
502
+ return;
503
+ }
504
+ if (res)
505
+ return resolve(r, res);
506
+ rule((stop) => {
507
+ try {
508
+ res = this();
509
+ } catch (e) {
510
+ stop();
511
+ defer(reject.bind(null, r, e));
512
+ }
513
+ if (res) {
514
+ stop();
515
+ defer(resolve.bind(null, r, res));
516
+ }
517
+ });
518
+ };
519
+ }
520
+ }
521
+ class WritableImpl extends SignalImpl {
522
+ constructor() {
523
+ super(...arguments);
524
+ this.set = (val) => {
525
+ this._c.setValue(val, false);
526
+ };
527
+ }
528
+ get value() {
529
+ return this();
530
+ }
531
+ set value(val) {
532
+ this.set(val);
533
+ }
534
+ asReadonly() {
535
+ return new SignalImpl(this._c);
536
+ }
537
+ }
538
+ class ConfigurableImpl extends WritableImpl {
539
+ setf(expr) {
540
+ this._c.setCalc(expr);
541
+ return this;
542
+ }
543
+ }
544
+ function value(val) {
545
+ const cell = Cell.mkValue(val);
546
+ return new ConfigurableImpl(cell);
547
+ }
548
+ function cached(compute, initVal) {
549
+ if (compute instanceof SignalImpl)
550
+ return compute;
551
+ return new SignalImpl(
552
+ compute.length ? Cell.mkStream(compute, initVal) : Cell.mkCached(compute)
553
+ );
554
+ }
555
+ function peek(fn, ...args) {
556
+ if (!current.cell)
557
+ return fn(...args);
558
+ const old = swapCtx(makeCtx(current.job));
559
+ try {
560
+ return fn(...args);
561
+ } finally {
562
+ freeCtx(swapCtx(old));
563
+ }
564
+ }
565
+ function action(fn, _ctx, desc) {
566
+ if (desc)
567
+ return { ...desc, value: action(desc.value) };
568
+ return function() {
569
+ if (!current.cell)
570
+ return apply(fn, this, arguments);
571
+ const old = swapCtx(makeCtx(current.job));
572
+ try {
573
+ return apply(fn, this, arguments);
574
+ } finally {
575
+ freeCtx(swapCtx(old));
576
+ }
577
+ };
578
+ }
579
+ function until(source) {
580
+ return callOrWait(source, "uneventful.until", waitTruthy, recache);
581
+ }
582
+ function recache(s) {
583
+ return until(cached(s));
584
+ }
585
+ function waitTruthy(job, v) {
586
+ v && job.return(v);
587
+ }
588
+
589
+ export { CircularDependency, ConfigurableImpl, SignalImpl, WritableImpl, WriteConflict, action, cached, peek, rule, runRules, until, value };
590
+ //# sourceMappingURL=signals.mjs.map