@msb235/blits 0.6.0 → 0.7.1
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/CHANGELOG.md +152 -0
- package/README.md +31 -7
- package/dist/bare.js +7 -1
- package/dist/book.d.ts +7 -0
- package/dist/book.js +35 -4
- package/dist/chain.d.ts +2 -1
- package/dist/chain.js +7 -3
- package/dist/channels.d.ts +12 -19
- package/dist/channels.js +18 -118
- package/dist/clock.d.ts +9 -2
- package/dist/clock.js +10 -2
- package/dist/closed.d.ts +15 -0
- package/dist/closed.js +144 -0
- package/dist/color.d.ts +61 -0
- package/dist/color.js +229 -0
- package/dist/columns.js +2 -1
- package/dist/crowd.d.ts +1 -1
- package/dist/crowd.js +4 -1
- package/dist/cue.d.ts +2 -0
- package/dist/cue.js +80 -27
- package/dist/due.js +3 -0
- package/dist/fade.d.ts +13 -0
- package/dist/fade.js +57 -4
- package/dist/fill.d.ts +1 -1
- package/dist/fill.js +15 -8
- package/dist/fit.d.ts +144 -0
- package/dist/fit.js +181 -0
- package/dist/fold.d.ts +3 -3
- package/dist/fold.js +18 -11
- package/dist/gather.d.ts +6 -0
- package/dist/gather.js +18 -0
- package/dist/gone.d.ts +32 -0
- package/dist/gone.js +134 -0
- package/dist/handle.d.ts +27 -2
- package/dist/handle.js +60 -11
- package/dist/held.js +4 -0
- package/dist/history.d.ts +15 -2
- package/dist/history.js +56 -9
- package/dist/hosts.d.ts +2 -2
- package/dist/hosts.js +20 -6
- package/dist/index.d.ts +8 -2
- package/dist/index.js +5 -1
- package/dist/lane.d.ts +1 -1
- package/dist/lanes.d.ts +32 -9
- package/dist/lanes.js +53 -24
- package/dist/locus.d.ts +3 -3
- package/dist/locus.js +1 -1
- package/dist/marks.d.ts +3 -0
- package/dist/marks.js +80 -12
- package/dist/meet.d.ts +3 -2
- package/dist/meet.js +15 -3
- package/dist/mixer.d.ts +56 -36
- package/dist/mixer.js +158 -87
- package/dist/motion.d.ts +2 -199
- package/dist/motion.js +2 -652
- package/dist/motions.d.ts +266 -0
- package/dist/motions.js +724 -0
- package/dist/move.d.ts +1 -1
- package/dist/move.js +28 -8
- package/dist/owner.d.ts +1 -1
- package/dist/owner.js +3 -3
- package/dist/pace.d.ts +2 -0
- package/dist/pace.js +8 -0
- package/dist/pack.d.ts +31 -0
- package/dist/pack.js +65 -0
- package/dist/paging.d.ts +63 -0
- package/dist/paging.js +110 -0
- package/dist/patch.d.ts +3 -0
- package/dist/patch.js +2 -0
- package/dist/place.js +235 -43
- package/dist/project.d.ts +7 -4
- package/dist/project.js +117 -78
- package/dist/pull.js +4 -3
- package/dist/qualify.js +4 -2
- package/dist/revive.d.ts +58 -0
- package/dist/revive.js +158 -0
- package/dist/sample.d.ts +1 -1
- package/dist/sample.js +6 -1
- package/dist/scored.d.ts +57 -0
- package/dist/scored.js +196 -0
- package/dist/seek.d.ts +3 -0
- package/dist/seek.js +311 -0
- package/dist/spans.d.ts +29 -0
- package/dist/spans.js +165 -0
- package/dist/strays.d.ts +1 -1
- package/dist/strays.js +21 -0
- package/dist/tape.d.ts +21 -0
- package/dist/tape.js +65 -0
- package/dist/ticker.d.ts +28 -3
- package/dist/ticker.js +3 -0
- package/dist/transport.d.ts +125 -0
- package/dist/transport.js +283 -0
- package/dist/types.d.ts +404 -38
- package/dist/unpage.d.ts +8 -0
- package/dist/unpage.js +165 -0
- package/dist/voice.d.ts +75 -7
- package/dist/voice.js +119 -4
- package/dist/weigh.js +2 -2
- package/docs/schema.html +120 -31
- package/package.json +2 -1
package/dist/move.d.ts
CHANGED
|
@@ -6,4 +6,4 @@ export declare function waits<I, O>(mix: Mixer<I, O>): boolean;
|
|
|
6
6
|
/** Moves the mix clock to `now`: voices start, finite loops end, and fades finish. */
|
|
7
7
|
export declare function move<I, O>(mix: Mixer<I, O>, now: number): void;
|
|
8
8
|
/** Lets go of the voices that have left and that history no longer reaches. */
|
|
9
|
-
export declare function forget<I, O>(mix: Mixer<I, O
|
|
9
|
+
export declare function forget<I, O>(mix: Mixer<I, O>): void;
|
package/dist/move.js
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { changed, unindex } from './chain.js';
|
|
2
2
|
import { popDue, schedule } from './due.js';
|
|
3
3
|
import { beginFade, part, retire } from './fade.js';
|
|
4
|
+
import { expireGone, keepGone } from './gone.js';
|
|
4
5
|
import { started } from './held.js';
|
|
5
6
|
import { ownerReading } from './owner.js';
|
|
6
7
|
import { mixAt, place, repin, startOf } from './place.js';
|
|
8
|
+
import { pageVoice } from './revive.js';
|
|
9
|
+
import { scoredLeft, scoredRemove } from './scored.js';
|
|
10
|
+
import { lapse } from './spans.js';
|
|
7
11
|
import { unplay } from './strays.js';
|
|
8
12
|
/** Numbers every mix's frames apart, so a blend's read is never taken for another mix's or frame's. */
|
|
9
13
|
let frames = 0;
|
|
@@ -38,7 +42,7 @@ function moveTo(mix, now) {
|
|
|
38
42
|
place(mix);
|
|
39
43
|
if (mix.announced.length > 0) {
|
|
40
44
|
// Kept while still ahead, or while history reaches it; anchors waiting on one were placed above.
|
|
41
|
-
const reach =
|
|
45
|
+
const reach = mix.opts.history === undefined ? now : mix.transport.keepsFrom();
|
|
42
46
|
mix.announced = mix.announced.filter((a) => mixAt(mix, a.at) >= reach);
|
|
43
47
|
}
|
|
44
48
|
// A live mix visits only the voices due by now; a projection, which copies few, visits all.
|
|
@@ -49,7 +53,7 @@ function moveTo(mix, now) {
|
|
|
49
53
|
if (Number.isNaN(voice.opened))
|
|
50
54
|
voice.opened = now;
|
|
51
55
|
if ((voice.state === 'live' || voice.state === 'frozen') && voice.outAt <= now)
|
|
52
|
-
beginFade(mix, voice, {}, Math.max(startOf(voice), voice.outAt));
|
|
56
|
+
beginFade(mix, voice, { over: Number.isNaN(voice.outOver) ? undefined : voice.outOver }, Math.max(startOf(voice), voice.outAt));
|
|
53
57
|
if (voice.state === 'pending' &&
|
|
54
58
|
(voice.owner === null ? now : ownerReading(voice.owner, now)) >= voice.start) {
|
|
55
59
|
voice.state = 'live';
|
|
@@ -59,7 +63,7 @@ function moveTo(mix, now) {
|
|
|
59
63
|
if (voice.state !== 'pending' && Number.isFinite(voice.span)) {
|
|
60
64
|
const end = voice.span + voice.latest;
|
|
61
65
|
if (voice.elapsedAt(now) >= end) {
|
|
62
|
-
voice.play(true);
|
|
66
|
+
voice.play(true, now);
|
|
63
67
|
// The fade starts when the last pass ended, not at the frame that noticed, so it plays
|
|
64
68
|
// the same at any frame rate and a read at another time can find it.
|
|
65
69
|
if (voice.state === 'live') {
|
|
@@ -77,6 +81,11 @@ function moveTo(mix, now) {
|
|
|
77
81
|
for (const [subject, r] of voice.parts)
|
|
78
82
|
if (now - r.at >= r.over)
|
|
79
83
|
part(mix, voice, subject, r.at + r.over);
|
|
84
|
+
const back = voice.back;
|
|
85
|
+
if (back !== null && now >= back.at + (1 - back.from) * back.over && !mix.projecting) {
|
|
86
|
+
voice.back = null;
|
|
87
|
+
mix.lanes?.refill();
|
|
88
|
+
}
|
|
80
89
|
if (voice.state === 'fading' && voice.out) {
|
|
81
90
|
const { at, over, rest, deadline } = voice.out;
|
|
82
91
|
const spent = now - at;
|
|
@@ -94,10 +103,12 @@ function moveTo(mix, now) {
|
|
|
94
103
|
if (!mix.projecting)
|
|
95
104
|
schedule(mix, voice);
|
|
96
105
|
}
|
|
106
|
+
if (mix.owners !== null)
|
|
107
|
+
lapse(mix, now);
|
|
97
108
|
mix.stirred = false;
|
|
98
109
|
if (mix.retired.length > 0)
|
|
99
110
|
prune(mix);
|
|
100
|
-
forget(mix
|
|
111
|
+
forget(mix);
|
|
101
112
|
}
|
|
102
113
|
/** Past this many voices retiring in one frame, one pass over the list beats a search for each. */
|
|
103
114
|
const SEARCHED = 8;
|
|
@@ -164,8 +175,11 @@ function indexOf(voices, voice) {
|
|
|
164
175
|
}
|
|
165
176
|
/** Lets go of a retired voice's place in the mix; whether it was among the voices naming no subject. */
|
|
166
177
|
function leave(mix, voice) {
|
|
167
|
-
|
|
178
|
+
scoredRemove(mix, [voice]);
|
|
179
|
+
if (mix.opts.history) {
|
|
168
180
|
mix.gone.push(voice);
|
|
181
|
+
scoredLeft(mix, voice);
|
|
182
|
+
}
|
|
169
183
|
else
|
|
170
184
|
mix.parters.delete(voice);
|
|
171
185
|
unplay(mix, voice);
|
|
@@ -181,10 +195,16 @@ function leave(mix, voice) {
|
|
|
181
195
|
return voice.named === null;
|
|
182
196
|
}
|
|
183
197
|
/** Lets go of the voices that have left and that history no longer reaches. */
|
|
184
|
-
export function forget(mix
|
|
198
|
+
export function forget(mix) {
|
|
185
199
|
const history = mix.opts.history;
|
|
186
200
|
if (history && mix.gone.length > 0) {
|
|
187
|
-
|
|
188
|
-
|
|
201
|
+
// With a store a voice that has left pages out where it can, and stays where it cannot.
|
|
202
|
+
if (mix.transport.pager !== null) {
|
|
203
|
+
const reach = mix.now - history.ms;
|
|
204
|
+
keepGone(mix, (v) => v.doneAt >= reach || !pageVoice(mix, v, reach));
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
expireGone(mix, mix.transport.keepsFrom());
|
|
208
|
+
}
|
|
189
209
|
}
|
|
190
210
|
}
|
package/dist/owner.d.ts
CHANGED
|
@@ -24,7 +24,7 @@ export declare function adopt<I, O>(owner: Voice<I, O>, child: Voice<I, O>): voi
|
|
|
24
24
|
/** A child left: its owner, where that was its last child and it should leave too. */
|
|
25
25
|
export declare function orphan<I, O>(child: Voice<I, O>): Voice<I, O> | null;
|
|
26
26
|
/** A child finished its passes: its owner has played once every child it held has. */
|
|
27
|
-
export declare function childPlayed<I, O>(owner: Voice<I, O
|
|
27
|
+
export declare function childPlayed<I, O>(owner: Voice<I, O>, at: number): void;
|
|
28
28
|
/** Every voice an owner holds, its children's children included. */
|
|
29
29
|
export declare function descendants<I, O>(owner: Voice<I, O>, visit: (v: Voice<I, O>) => void): void;
|
|
30
30
|
/** Whether any owner above a voice reads a signal for its weight, which lanes cannot fill. */
|
package/dist/owner.js
CHANGED
|
@@ -48,10 +48,10 @@ export function orphan(child) {
|
|
|
48
48
|
return h.children.length === 0 && owner.state !== 'done' ? owner : null;
|
|
49
49
|
}
|
|
50
50
|
/** A child finished its passes: its owner has played once every child it held has. */
|
|
51
|
-
export function childPlayed(owner) {
|
|
51
|
+
export function childPlayed(owner, at) {
|
|
52
52
|
const h = owner.holding;
|
|
53
53
|
if (++h.played === h.kids)
|
|
54
|
-
owner.play(true);
|
|
54
|
+
owner.play(true, at);
|
|
55
55
|
}
|
|
56
56
|
/** Every voice an owner holds, its children's children included. */
|
|
57
57
|
export function descendants(owner, visit) {
|
|
@@ -86,7 +86,7 @@ export function ownWeight(owner, now, reduced, base) {
|
|
|
86
86
|
const b = typeof owner.spec.weight === 'function' ? base(owner) : owner.weight;
|
|
87
87
|
const start = owner.owner === null ? owner.start : mixTime(owner.owner, owner.start);
|
|
88
88
|
const since = owner.freezesBefore && owner.opened < start ? owner.opened : start;
|
|
89
|
-
return clampWeight(b * envelope(owner.fade.in ?? 0, owner.out, owner.ease, reduced, now, since));
|
|
89
|
+
return clampWeight(b * envelope(owner.fade.in ?? 0, owner.out, owner.ease, reduced, now, since, owner.back));
|
|
90
90
|
}
|
|
91
91
|
/** What every owner above a voice multiplies into its weight. */
|
|
92
92
|
export function ownersWeight(voice, now, reduced, base) {
|
package/dist/pace.d.ts
CHANGED
|
@@ -35,6 +35,8 @@ export declare class Pace {
|
|
|
35
35
|
* time `reach`.
|
|
36
36
|
*/
|
|
37
37
|
change(u: number, rate: number, over: number, reach: number): void;
|
|
38
|
+
/** Lets go of the clocks set after host time `u`, for a seek back to then. */
|
|
39
|
+
cut(u: number): void;
|
|
38
40
|
/** A copy holding the clocks that took effect before host time `u`, for a read back to then. */
|
|
39
41
|
until(u: number): Pace;
|
|
40
42
|
}
|
package/dist/pace.js
CHANGED
|
@@ -102,6 +102,14 @@ export class Pace {
|
|
|
102
102
|
if (drop > 0)
|
|
103
103
|
log.splice(0, drop);
|
|
104
104
|
}
|
|
105
|
+
/** Lets go of the clocks set after host time `u`, for a seek back to then. */
|
|
106
|
+
cut(u) {
|
|
107
|
+
const log = this.log;
|
|
108
|
+
let n = 1;
|
|
109
|
+
while (n < log.length && log[n].anchorNow <= u)
|
|
110
|
+
n++;
|
|
111
|
+
log.length = n;
|
|
112
|
+
}
|
|
105
113
|
/** A copy holding the clocks that took effect before host time `u`, for a read back to then. */
|
|
106
114
|
until(u) {
|
|
107
115
|
const log = this.log;
|
package/dist/pack.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Subject, Voice } from './voice.js';
|
|
2
|
+
/** A subject's record as plain data, for a history store. */
|
|
3
|
+
export interface PackedRecord {
|
|
4
|
+
reaches: boolean;
|
|
5
|
+
delay: number;
|
|
6
|
+
since: number;
|
|
7
|
+
shown: number;
|
|
8
|
+
weight: number;
|
|
9
|
+
rested: boolean;
|
|
10
|
+
bands: number[] | null;
|
|
11
|
+
state: unknown;
|
|
12
|
+
stepped: number;
|
|
13
|
+
ticks: number;
|
|
14
|
+
/**
|
|
15
|
+
* What `setting.keep` held, in the order its owners first kept it. An owner is code, which no
|
|
16
|
+
* store keeps, and a voice revived from a descriptor has new ones; the same code keeps in the same
|
|
17
|
+
* order, so each owner takes the next value on its first keep.
|
|
18
|
+
*/
|
|
19
|
+
kept: unknown[] | null;
|
|
20
|
+
base?: Record<string, unknown>;
|
|
21
|
+
slope?: Record<string, unknown>;
|
|
22
|
+
snaps?: [at: number, held: PackedRecord][];
|
|
23
|
+
inputs?: {
|
|
24
|
+
at: number;
|
|
25
|
+
value: number;
|
|
26
|
+
}[];
|
|
27
|
+
}
|
|
28
|
+
/** A voice's record of one subject as plain data, its patch's `pack` taking its state. */
|
|
29
|
+
export declare function packHeld<I, O>(voice: Voice<I, O>, h: Subject<unknown>): PackedRecord;
|
|
30
|
+
/** A record made again from `packHeld`'s data, for `voice`. */
|
|
31
|
+
export declare function unpackHeld<I, O>(voice: Voice<I, O>, p: PackedRecord): Subject<unknown>;
|
package/dist/pack.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { clone } from './clone.js';
|
|
2
|
+
/** A voice's record of one subject as plain data, its patch's `pack` taking its state. */
|
|
3
|
+
export function packHeld(voice, h) {
|
|
4
|
+
const patch = voice.patch;
|
|
5
|
+
const kept = [...(h.kept?.values() ?? []), ...(h.unkept ?? [])];
|
|
6
|
+
const p = {
|
|
7
|
+
reaches: h.reaches,
|
|
8
|
+
delay: h.delay,
|
|
9
|
+
since: h.since,
|
|
10
|
+
shown: h.shown,
|
|
11
|
+
weight: h.weight,
|
|
12
|
+
rested: h.rested,
|
|
13
|
+
bands: h.bands === null ? null : Array.from(h.bands),
|
|
14
|
+
state: h.state === undefined ? undefined : patch.pack ? patch.pack(h.state) : clone(h.state),
|
|
15
|
+
stepped: h.stepped,
|
|
16
|
+
ticks: h.ticks,
|
|
17
|
+
kept: kept.length === 0 ? null : kept.map(clone),
|
|
18
|
+
};
|
|
19
|
+
if (h.base !== undefined)
|
|
20
|
+
p.base = clone(h.base);
|
|
21
|
+
if (h.slope !== undefined)
|
|
22
|
+
p.slope = clone(h.slope);
|
|
23
|
+
if (h.snaps !== undefined)
|
|
24
|
+
p.snaps = h.snaps.map((s) => [s.at, packHeld(voice, s.held)]);
|
|
25
|
+
if (h.inputs !== undefined)
|
|
26
|
+
p.inputs = h.inputs.map((e) => ({ ...e }));
|
|
27
|
+
return p;
|
|
28
|
+
}
|
|
29
|
+
/** A record made again from `packHeld`'s data, for `voice`. */
|
|
30
|
+
export function unpackHeld(voice, p) {
|
|
31
|
+
const patch = voice.patch;
|
|
32
|
+
const h = {
|
|
33
|
+
reaches: p.reaches,
|
|
34
|
+
delay: p.delay,
|
|
35
|
+
since: p.since,
|
|
36
|
+
shown: p.shown,
|
|
37
|
+
weight: p.weight,
|
|
38
|
+
rested: p.rested,
|
|
39
|
+
bands: p.bands === null ? null : Uint8Array.from(p.bands),
|
|
40
|
+
state: p.state === undefined ? undefined : patch.unpack ? patch.unpack(p.state) : p.state,
|
|
41
|
+
stepped: p.stepped,
|
|
42
|
+
ticks: p.ticks,
|
|
43
|
+
probed: Number.NaN,
|
|
44
|
+
delta: null,
|
|
45
|
+
phase: 0,
|
|
46
|
+
seeks: 0,
|
|
47
|
+
kept: null,
|
|
48
|
+
voice,
|
|
49
|
+
next: null,
|
|
50
|
+
version: Number.NaN,
|
|
51
|
+
loci: null,
|
|
52
|
+
slot: -1,
|
|
53
|
+
};
|
|
54
|
+
if (p.kept !== null)
|
|
55
|
+
h.unkept = p.kept;
|
|
56
|
+
if (p.base !== undefined)
|
|
57
|
+
h.base = p.base;
|
|
58
|
+
if (p.slope !== undefined)
|
|
59
|
+
h.slope = p.slope;
|
|
60
|
+
if (p.snaps !== undefined)
|
|
61
|
+
h.snaps = p.snaps.map(([at, held]) => ({ at, held: unpackHeld(voice, held) }));
|
|
62
|
+
if (p.inputs !== undefined)
|
|
63
|
+
h.inputs = p.inputs;
|
|
64
|
+
return h;
|
|
65
|
+
}
|
package/dist/paging.d.ts
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { Mixer } from './mixer.js';
|
|
2
|
+
import type { Transport } from './transport.js';
|
|
3
|
+
import type { HistoryStore, Paged, PagedStream } from './types.js';
|
|
4
|
+
/**
|
|
5
|
+
* A `seek` or `project` reached past memory into records `prepare` had not loaded. Thrown before
|
|
6
|
+
* anything moves, so a restore is never partial: `await prepare(at)` and ask again.
|
|
7
|
+
*
|
|
8
|
+
* @category mix
|
|
9
|
+
*/
|
|
10
|
+
export declare class HistoryMiss extends Error {
|
|
11
|
+
/** The mix whose records were missing, by `name`. */
|
|
12
|
+
readonly mix: string;
|
|
13
|
+
readonly stream: string;
|
|
14
|
+
/** The mix time asked for. */
|
|
15
|
+
readonly at: number;
|
|
16
|
+
constructor(
|
|
17
|
+
/** The mix whose records were missing, by `name`. */
|
|
18
|
+
mix: string, stream: string,
|
|
19
|
+
/** The mix time asked for. */
|
|
20
|
+
at: number);
|
|
21
|
+
}
|
|
22
|
+
/** A transport's history store: what left memory, and what `prepare` loaded back. */
|
|
23
|
+
export declare class Pager {
|
|
24
|
+
readonly store: HistoryStore;
|
|
25
|
+
/** What the last `prepare` loaded, and every record paged since, until a seek or read takes it. */
|
|
26
|
+
loaded: {
|
|
27
|
+
t: number;
|
|
28
|
+
records: Paged[];
|
|
29
|
+
} | null;
|
|
30
|
+
/** The latest record paged, which a miss names. */
|
|
31
|
+
last: {
|
|
32
|
+
mix: string;
|
|
33
|
+
stream: PagedStream;
|
|
34
|
+
} | null;
|
|
35
|
+
constructor(store: HistoryStore);
|
|
36
|
+
}
|
|
37
|
+
/** A subject's key in history records; undefined for the unit subject. */
|
|
38
|
+
export type Key = string | number | undefined;
|
|
39
|
+
/**
|
|
40
|
+
* Object subjects by key, for a mix with a store, held weakly: a subject the host has let go of
|
|
41
|
+
* can never be probed again, so nothing it kept is put back.
|
|
42
|
+
*/
|
|
43
|
+
export declare class Keys {
|
|
44
|
+
private readonly of;
|
|
45
|
+
private readonly refs;
|
|
46
|
+
private readonly gone;
|
|
47
|
+
constructor(of: ((subject: never) => string | number) | undefined);
|
|
48
|
+
/** The key a subject is paged under; throws for an object subject without `keyOf`. */
|
|
49
|
+
key(subject: unknown): Key;
|
|
50
|
+
/** The subject a key stands for: an object the mix met under it, else the key itself. */
|
|
51
|
+
subject(key: Key): unknown;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Hands records leaving memory to the store, the last of any that share a time, which is the one
|
|
55
|
+
* a read finds. `reach` is how far back memory still reaches.
|
|
56
|
+
*/
|
|
57
|
+
export declare function pageOut<I, O, E extends {
|
|
58
|
+
at: number;
|
|
59
|
+
}>(mix: Mixer<I, O>, stream: PagedStream, voice: number | undefined, subject: Key, out: readonly E[], data: (e: E) => unknown, reach: number): void;
|
|
60
|
+
/** Gives records to the store; memory now reaches back no further than `reach`. */
|
|
61
|
+
export declare function page(transport: Transport, records: Paged[], reach: number): void;
|
|
62
|
+
/** Loads what a seek or read to `t` needs where it reaches past memory. */
|
|
63
|
+
export declare function prepare(transport: Transport, t: number): Promise<void>;
|
package/dist/paging.js
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A `seek` or `project` reached past memory into records `prepare` had not loaded. Thrown before
|
|
3
|
+
* anything moves, so a restore is never partial: `await prepare(at)` and ask again.
|
|
4
|
+
*
|
|
5
|
+
* @category mix
|
|
6
|
+
*/
|
|
7
|
+
export class HistoryMiss extends Error {
|
|
8
|
+
mix;
|
|
9
|
+
stream;
|
|
10
|
+
at;
|
|
11
|
+
constructor(
|
|
12
|
+
/** The mix whose records were missing, by `name`. */
|
|
13
|
+
mix, stream,
|
|
14
|
+
/** The mix time asked for. */
|
|
15
|
+
at) {
|
|
16
|
+
super(`blits: ${at} reaches past memory into ${stream} records of ${mix === '' ? 'the mix' : mix} that were not loaded; await prepare(${at}) first`);
|
|
17
|
+
this.mix = mix;
|
|
18
|
+
this.stream = stream;
|
|
19
|
+
this.at = at;
|
|
20
|
+
this.name = 'HistoryMiss';
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/** A transport's history store: what left memory, and what `prepare` loaded back. */
|
|
24
|
+
export class Pager {
|
|
25
|
+
store;
|
|
26
|
+
/** What the last `prepare` loaded, and every record paged since, until a seek or read takes it. */
|
|
27
|
+
loaded = null;
|
|
28
|
+
/** The latest record paged, which a miss names. */
|
|
29
|
+
last = null;
|
|
30
|
+
constructor(store) {
|
|
31
|
+
this.store = store;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Object subjects by key, for a mix with a store, held weakly: a subject the host has let go of
|
|
36
|
+
* can never be probed again, so nothing it kept is put back.
|
|
37
|
+
*/
|
|
38
|
+
export class Keys {
|
|
39
|
+
of;
|
|
40
|
+
refs = new Map();
|
|
41
|
+
gone = new FinalizationRegistry((key) => {
|
|
42
|
+
const ref = this.refs.get(key);
|
|
43
|
+
if (ref !== undefined && ref.deref() === undefined)
|
|
44
|
+
this.refs.delete(key);
|
|
45
|
+
});
|
|
46
|
+
constructor(of) {
|
|
47
|
+
this.of = of;
|
|
48
|
+
}
|
|
49
|
+
/** The key a subject is paged under; throws for an object subject without `keyOf`. */
|
|
50
|
+
key(subject) {
|
|
51
|
+
if (subject === undefined || typeof subject === 'string' || typeof subject === 'number')
|
|
52
|
+
return subject;
|
|
53
|
+
const of = this.of;
|
|
54
|
+
if (of === undefined || typeof subject !== 'object' || subject === null)
|
|
55
|
+
throw new Error('blits: a mix whose history has a store pages its subjects by key; give it keyOf for subjects that are not strings or numbers');
|
|
56
|
+
const key = of(subject);
|
|
57
|
+
if (this.refs.get(key)?.deref() !== subject) {
|
|
58
|
+
this.refs.set(key, new WeakRef(subject));
|
|
59
|
+
this.gone.register(subject, key);
|
|
60
|
+
}
|
|
61
|
+
return key;
|
|
62
|
+
}
|
|
63
|
+
/** The subject a key stands for: an object the mix met under it, else the key itself. */
|
|
64
|
+
subject(key) {
|
|
65
|
+
if (key === undefined)
|
|
66
|
+
return undefined;
|
|
67
|
+
return this.refs.get(key)?.deref() ?? key;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Hands records leaving memory to the store, the last of any that share a time, which is the one
|
|
72
|
+
* a read finds. `reach` is how far back memory still reaches.
|
|
73
|
+
*/
|
|
74
|
+
export function pageOut(mix, stream, voice, subject, out, data, reach) {
|
|
75
|
+
const pager = mix.transport.pager;
|
|
76
|
+
if (pager === null || out.length === 0)
|
|
77
|
+
return;
|
|
78
|
+
const name = mix.name ?? '';
|
|
79
|
+
const records = [];
|
|
80
|
+
for (let i = 0; i < out.length; i++) {
|
|
81
|
+
const e = out[i];
|
|
82
|
+
if (i + 1 < out.length && out[i + 1].at === e.at)
|
|
83
|
+
continue;
|
|
84
|
+
const r = { mix: name, stream, at: e.at, data: data(e) };
|
|
85
|
+
if (voice !== undefined)
|
|
86
|
+
r.voice = voice;
|
|
87
|
+
if (subject !== undefined)
|
|
88
|
+
r.subject = subject;
|
|
89
|
+
records.push(r);
|
|
90
|
+
}
|
|
91
|
+
page(mix.transport, records, reach);
|
|
92
|
+
}
|
|
93
|
+
/** Gives records to the store; memory now reaches back no further than `reach`. */
|
|
94
|
+
export function page(transport, records, reach) {
|
|
95
|
+
const pager = transport.pager;
|
|
96
|
+
pager.store.page(records);
|
|
97
|
+
pager.loaded?.records.push(...records);
|
|
98
|
+
const last = records[records.length - 1];
|
|
99
|
+
pager.last = { mix: last.mix, stream: last.stream };
|
|
100
|
+
if (reach > transport.floor)
|
|
101
|
+
transport.floor = reach;
|
|
102
|
+
}
|
|
103
|
+
/** Loads what a seek or read to `t` needs where it reaches past memory. */
|
|
104
|
+
export async function prepare(transport, t) {
|
|
105
|
+
const pager = transport.pager;
|
|
106
|
+
if (pager === null || transport.reaches(t))
|
|
107
|
+
return;
|
|
108
|
+
const records = await pager.store.load(t);
|
|
109
|
+
pager.loaded = { t, records: [...records] };
|
|
110
|
+
}
|
package/dist/patch.d.ts
CHANGED
|
@@ -15,6 +15,9 @@ export interface PatchOptions<I, O, S, H = unknown> {
|
|
|
15
15
|
reads?: readonly string[];
|
|
16
16
|
state?(subject: I): S;
|
|
17
17
|
step?(state: S, dt: number, subject: I, setting: Setting<S, H>): void;
|
|
18
|
+
/** `state` as plain data and back, for a history store: see `Patch.pack`. */
|
|
19
|
+
pack?(state: S): unknown;
|
|
20
|
+
unpack?(data: unknown): S;
|
|
18
21
|
}
|
|
19
22
|
/**
|
|
20
23
|
* The procedural form. Flicker, roving, a hash walk, a starter strike.
|