bireactive 0.2.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/LICENSE +21 -0
- package/README.md +81 -0
- package/dist/animation/anim.d.ts +57 -0
- package/dist/animation/anim.js +318 -0
- package/dist/animation/combinators.d.ts +39 -0
- package/dist/animation/combinators.js +113 -0
- package/dist/animation/easings.d.ts +5 -0
- package/dist/animation/easings.js +5 -0
- package/dist/animation/index.d.ts +3 -0
- package/dist/animation/index.js +3 -0
- package/dist/assert/algebra.d.ts +20 -0
- package/dist/assert/algebra.js +79 -0
- package/dist/assert/claim.d.ts +40 -0
- package/dist/assert/claim.js +129 -0
- package/dist/assert/index.d.ts +7 -0
- package/dist/assert/index.js +19 -0
- package/dist/assert/predicates.d.ts +18 -0
- package/dist/assert/predicates.js +43 -0
- package/dist/assert/record.d.ts +20 -0
- package/dist/assert/record.js +78 -0
- package/dist/assert/scope.d.ts +42 -0
- package/dist/assert/scope.js +233 -0
- package/dist/assert/span.d.ts +37 -0
- package/dist/assert/span.js +68 -0
- package/dist/assert/tree.d.ts +22 -0
- package/dist/assert/tree.js +65 -0
- package/dist/code/code.d.ts +70 -0
- package/dist/code/code.js +361 -0
- package/dist/code/index.d.ts +2 -0
- package/dist/code/index.js +9 -0
- package/dist/code/morph.d.ts +5 -0
- package/dist/code/morph.js +194 -0
- package/dist/code/tokenize.d.ts +8 -0
- package/dist/code/tokenize.js +51 -0
- package/dist/constraints/cluster.d.ts +83 -0
- package/dist/constraints/cluster.js +213 -0
- package/dist/constraints/drivers.d.ts +15 -0
- package/dist/constraints/drivers.js +40 -0
- package/dist/constraints/factories.d.ts +73 -0
- package/dist/constraints/factories.js +248 -0
- package/dist/constraints/index.d.ts +11 -0
- package/dist/constraints/index.js +39 -0
- package/dist/constraints/interaction.d.ts +21 -0
- package/dist/constraints/interaction.js +148 -0
- package/dist/constraints/linalg.d.ts +18 -0
- package/dist/constraints/linalg.js +141 -0
- package/dist/constraints/phases.d.ts +21 -0
- package/dist/constraints/phases.js +60 -0
- package/dist/constraints/physics.d.ts +34 -0
- package/dist/constraints/physics.js +128 -0
- package/dist/constraints/rigid.d.ts +210 -0
- package/dist/constraints/rigid.js +835 -0
- package/dist/constraints/solver.d.ts +107 -0
- package/dist/constraints/solver.js +510 -0
- package/dist/constraints/term.d.ts +50 -0
- package/dist/constraints/term.js +80 -0
- package/dist/constraints/terms.d.ts +80 -0
- package/dist/constraints/terms.js +302 -0
- package/dist/constraints/world.d.ts +31 -0
- package/dist/constraints/world.js +245 -0
- package/dist/core/aggregates.d.ts +64 -0
- package/dist/core/aggregates.js +198 -0
- package/dist/core/anim.d.ts +84 -0
- package/dist/core/anim.js +301 -0
- package/dist/core/index.d.ts +38 -0
- package/dist/core/index.js +38 -0
- package/dist/core/introspect.d.ts +5 -0
- package/dist/core/introspect.js +31 -0
- package/dist/core/lenses/closed-form-policies.d.ts +64 -0
- package/dist/core/lenses/closed-form-policies.js +452 -0
- package/dist/core/lenses/domain-aggregates.d.ts +54 -0
- package/dist/core/lenses/domain-aggregates.js +259 -0
- package/dist/core/lenses/factor-lens.d.ts +42 -0
- package/dist/core/lenses/factor-lens.js +419 -0
- package/dist/core/lenses/index.d.ts +5 -0
- package/dist/core/lenses/index.js +16 -0
- package/dist/core/lenses/memory.d.ts +47 -0
- package/dist/core/lenses/memory.js +102 -0
- package/dist/core/lenses/typed-factor.d.ts +45 -0
- package/dist/core/lenses/typed-factor.js +376 -0
- package/dist/core/network-utils.d.ts +14 -0
- package/dist/core/network-utils.js +62 -0
- package/dist/core/new-primitives.d.ts +33 -0
- package/dist/core/new-primitives.js +113 -0
- package/dist/core/signal.d.ts +254 -0
- package/dist/core/signal.js +1349 -0
- package/dist/core/traits.d.ts +61 -0
- package/dist/core/traits.js +56 -0
- package/dist/core/tree.d.ts +23 -0
- package/dist/core/tree.js +62 -0
- package/dist/core/values/anchor.d.ts +23 -0
- package/dist/core/values/anchor.js +23 -0
- package/dist/core/values/audio.d.ts +33 -0
- package/dist/core/values/audio.js +107 -0
- package/dist/core/values/bool.d.ts +37 -0
- package/dist/core/values/bool.js +75 -0
- package/dist/core/values/box.d.ts +77 -0
- package/dist/core/values/box.js +211 -0
- package/dist/core/values/canvas.d.ts +71 -0
- package/dist/core/values/canvas.js +495 -0
- package/dist/core/values/color.d.ts +49 -0
- package/dist/core/values/color.js +106 -0
- package/dist/core/values/flags.d.ts +18 -0
- package/dist/core/values/flags.js +50 -0
- package/dist/core/values/gpu.d.ts +74 -0
- package/dist/core/values/gpu.js +426 -0
- package/dist/core/values/matrix.d.ts +53 -0
- package/dist/core/values/matrix.js +140 -0
- package/dist/core/values/num.d.ts +62 -0
- package/dist/core/values/num.js +166 -0
- package/dist/core/values/pose.d.ts +31 -0
- package/dist/core/values/pose.js +83 -0
- package/dist/core/values/range.d.ts +83 -0
- package/dist/core/values/range.js +167 -0
- package/dist/core/values/str.d.ts +76 -0
- package/dist/core/values/str.js +346 -0
- package/dist/core/values/template.d.ts +49 -0
- package/dist/core/values/template.js +148 -0
- package/dist/core/values/transform.d.ts +49 -0
- package/dist/core/values/transform.js +115 -0
- package/dist/core/values/tri.d.ts +31 -0
- package/dist/core/values/tri.js +95 -0
- package/dist/core/values/vec.d.ts +72 -0
- package/dist/core/values/vec.js +219 -0
- package/dist/core/writable.d.ts +15 -0
- package/dist/core/writable.js +29 -0
- package/dist/ext/events.d.ts +10 -0
- package/dist/ext/events.js +31 -0
- package/dist/ext/index.d.ts +4 -0
- package/dist/ext/index.js +4 -0
- package/dist/ext/snapshot.d.ts +8 -0
- package/dist/ext/snapshot.js +29 -0
- package/dist/ext/timeline.d.ts +56 -0
- package/dist/ext/timeline.js +94 -0
- package/dist/ext/waapi.d.ts +25 -0
- package/dist/ext/waapi.js +198 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +10 -0
- package/dist/propagators/index.d.ts +6 -0
- package/dist/propagators/index.js +6 -0
- package/dist/propagators/layout.d.ts +68 -0
- package/dist/propagators/layout.js +336 -0
- package/dist/propagators/network.d.ts +52 -0
- package/dist/propagators/network.js +185 -0
- package/dist/propagators/propagator.d.ts +12 -0
- package/dist/propagators/propagator.js +16 -0
- package/dist/propagators/range.d.ts +45 -0
- package/dist/propagators/range.js +147 -0
- package/dist/propagators/relations.d.ts +60 -0
- package/dist/propagators/relations.js +343 -0
- package/dist/shapes/annular-sector.d.ts +15 -0
- package/dist/shapes/annular-sector.js +64 -0
- package/dist/shapes/button.d.ts +14 -0
- package/dist/shapes/button.js +31 -0
- package/dist/shapes/choreographers.d.ts +22 -0
- package/dist/shapes/choreographers.js +69 -0
- package/dist/shapes/circle.d.ts +17 -0
- package/dist/shapes/circle.js +57 -0
- package/dist/shapes/clip.d.ts +5 -0
- package/dist/shapes/clip.js +31 -0
- package/dist/shapes/connect.d.ts +16 -0
- package/dist/shapes/connect.js +70 -0
- package/dist/shapes/curve.d.ts +60 -0
- package/dist/shapes/curve.js +285 -0
- package/dist/shapes/dashed.d.ts +16 -0
- package/dist/shapes/dashed.js +142 -0
- package/dist/shapes/debug.d.ts +43 -0
- package/dist/shapes/debug.js +97 -0
- package/dist/shapes/group.d.ts +5 -0
- package/dist/shapes/group.js +10 -0
- package/dist/shapes/handle.d.ts +32 -0
- package/dist/shapes/handle.js +88 -0
- package/dist/shapes/index.d.ts +23 -0
- package/dist/shapes/index.js +23 -0
- package/dist/shapes/interaction.d.ts +32 -0
- package/dist/shapes/interaction.js +187 -0
- package/dist/shapes/label.d.ts +20 -0
- package/dist/shapes/label.js +42 -0
- package/dist/shapes/layout.d.ts +29 -0
- package/dist/shapes/layout.js +74 -0
- package/dist/shapes/line.d.ts +21 -0
- package/dist/shapes/line.js +79 -0
- package/dist/shapes/list.d.ts +18 -0
- package/dist/shapes/list.js +51 -0
- package/dist/shapes/mount.d.ts +7 -0
- package/dist/shapes/mount.js +10 -0
- package/dist/shapes/path.d.ts +77 -0
- package/dist/shapes/path.js +227 -0
- package/dist/shapes/rect.d.ts +30 -0
- package/dist/shapes/rect.js +131 -0
- package/dist/shapes/shape.d.ts +132 -0
- package/dist/shapes/shape.js +306 -0
- package/dist/shapes/text.d.ts +24 -0
- package/dist/shapes/text.js +53 -0
- package/dist/shapes/tokens.d.ts +28 -0
- package/dist/shapes/tokens.js +27 -0
- package/dist/shapes/transitions.d.ts +23 -0
- package/dist/shapes/transitions.js +62 -0
- package/dist/tex/decorations.d.ts +26 -0
- package/dist/tex/decorations.js +116 -0
- package/dist/tex/index.d.ts +5 -0
- package/dist/tex/index.js +5 -0
- package/dist/tex/marker.d.ts +17 -0
- package/dist/tex/marker.js +63 -0
- package/dist/tex/motion.d.ts +43 -0
- package/dist/tex/motion.js +290 -0
- package/dist/tex/parts.d.ts +65 -0
- package/dist/tex/parts.js +149 -0
- package/dist/tex/tex.d.ts +45 -0
- package/dist/tex/tex.js +244 -0
- package/dist/web/attr.d.ts +16 -0
- package/dist/web/attr.js +98 -0
- package/dist/web/diagram.d.ts +49 -0
- package/dist/web/diagram.js +260 -0
- package/dist/web/index.d.ts +6 -0
- package/dist/web/index.js +6 -0
- package/dist/web/md-marker.d.ts +6 -0
- package/dist/web/md-marker.js +39 -0
- package/dist/web/md-tex.d.ts +6 -0
- package/dist/web/md-tex.js +61 -0
- package/dist/web/raf.d.ts +6 -0
- package/dist/web/raf.js +24 -0
- package/dist/web/viewport.d.ts +7 -0
- package/dist/web/viewport.js +13 -0
- package/package.json +87 -0
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
// scope() — wrap a factory so its invocations carry identity.
|
|
2
|
+
//
|
|
3
|
+
// The wrapped factory returns an Animator-shaped wrapper that:
|
|
4
|
+
// - captures parent at construction (call-site `currentSpan`).
|
|
5
|
+
// - opens the span on first `.next()` (start aligns to engine clock).
|
|
6
|
+
// - pushes `currentSpan = self` around every `.next()`/`.return()`/
|
|
7
|
+
// `.throw()`, so attribution flows through `yield`, `yield*`, and
|
|
8
|
+
// nesting (the parent wrapper is on the stack while its body runs).
|
|
9
|
+
// - closes on done, cancellation, or error.
|
|
10
|
+
//
|
|
11
|
+
// Per-factory queries (`alive`, `last`, `runs`, `duration`, `touched`,
|
|
12
|
+
// `touchedDeep`) hang off the wrapper as lazy signal getters.
|
|
13
|
+
import { cell, derive } from "../core/index.js";
|
|
14
|
+
import { closeSpan, currentSpan, notifySpanOpen, openSpan, withSpan } from "./span.js";
|
|
15
|
+
/** Bumped by record() on each open/close so derived signals refresh. */
|
|
16
|
+
const traceVersion = cell(0);
|
|
17
|
+
/** Bump so `alive` / `last` / `runs` etc. recompute. Called by record(). */
|
|
18
|
+
export function bumpTraceVersion() {
|
|
19
|
+
traceVersion.value++;
|
|
20
|
+
}
|
|
21
|
+
/** Version signal driving the lazy per-factory stat signals. */
|
|
22
|
+
export function traceVersionSignal() {
|
|
23
|
+
return traceVersion;
|
|
24
|
+
}
|
|
25
|
+
/** Per-factory live history; survives `stop()` so post-stop queries work. */
|
|
26
|
+
const spansByFactory = new WeakMap();
|
|
27
|
+
export function recordFactorySpan(s) {
|
|
28
|
+
let arr = spansByFactory.get(s.fn);
|
|
29
|
+
if (!arr) {
|
|
30
|
+
arr = [];
|
|
31
|
+
spansByFactory.set(s.fn, arr);
|
|
32
|
+
}
|
|
33
|
+
arr.push(s);
|
|
34
|
+
}
|
|
35
|
+
export function spansOf(fn) {
|
|
36
|
+
return spansByFactory.get(fn) ?? [];
|
|
37
|
+
}
|
|
38
|
+
export function scope(...args) {
|
|
39
|
+
const [name, fn] = typeof args[0] === "string" ? args : [undefined, args[0]];
|
|
40
|
+
const tagged = name ?? fn.name ?? "anon";
|
|
41
|
+
const factory = ((...args) => {
|
|
42
|
+
const parent = currentSpan;
|
|
43
|
+
const inner = fn(...args);
|
|
44
|
+
return makeWrapper(fn, tagged, args, parent, inner);
|
|
45
|
+
});
|
|
46
|
+
Object.defineProperty(factory, "name", {
|
|
47
|
+
value: tagged,
|
|
48
|
+
configurable: true,
|
|
49
|
+
});
|
|
50
|
+
// Lazy stat signals: allocate the Computed on first access, memoize
|
|
51
|
+
// after. Each reads `traceVersion` directly so it dirties on every
|
|
52
|
+
// open/close — an `allSpans` intermediate fails because spansOf()
|
|
53
|
+
// returns a stable array ref that signal equality treats as unchanged.
|
|
54
|
+
const lazy = (make) => {
|
|
55
|
+
let cached;
|
|
56
|
+
return {
|
|
57
|
+
get() {
|
|
58
|
+
if (!cached)
|
|
59
|
+
cached = make();
|
|
60
|
+
return cached;
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
const lastSpan = lazy(() => derive(() => {
|
|
65
|
+
traceVersion.value;
|
|
66
|
+
const list = spansOf(fn);
|
|
67
|
+
return list.length === 0 ? undefined : list[list.length - 1];
|
|
68
|
+
}));
|
|
69
|
+
Object.defineProperty(factory, "alive", {
|
|
70
|
+
get: lazy(() => derive(() => {
|
|
71
|
+
traceVersion.value;
|
|
72
|
+
const list = spansOf(fn);
|
|
73
|
+
for (const s of list)
|
|
74
|
+
if (s.status === "open")
|
|
75
|
+
return true;
|
|
76
|
+
return false;
|
|
77
|
+
})).get,
|
|
78
|
+
});
|
|
79
|
+
Object.defineProperty(factory, "last", { get: () => lastSpan.get() });
|
|
80
|
+
Object.defineProperty(factory, "runs", {
|
|
81
|
+
get: lazy(() => derive(() => {
|
|
82
|
+
traceVersion.value;
|
|
83
|
+
return spansOf(fn).length;
|
|
84
|
+
})).get,
|
|
85
|
+
});
|
|
86
|
+
Object.defineProperty(factory, "duration", {
|
|
87
|
+
get: lazy(() => derive(() => {
|
|
88
|
+
traceVersion.value;
|
|
89
|
+
const list = spansOf(fn);
|
|
90
|
+
let total = 0;
|
|
91
|
+
for (const s of list) {
|
|
92
|
+
const end = s.end ?? s.start;
|
|
93
|
+
total += Math.max(0, end - s.start);
|
|
94
|
+
}
|
|
95
|
+
return total;
|
|
96
|
+
})).get,
|
|
97
|
+
});
|
|
98
|
+
Object.defineProperty(factory, "touched", {
|
|
99
|
+
get: lazy(() => derive(() => {
|
|
100
|
+
traceVersion.value;
|
|
101
|
+
const list = spansOf(fn);
|
|
102
|
+
if (list.length === 0)
|
|
103
|
+
return [];
|
|
104
|
+
return Array.from(list[list.length - 1].touched);
|
|
105
|
+
})).get,
|
|
106
|
+
});
|
|
107
|
+
Object.defineProperty(factory, "touchedDeep", {
|
|
108
|
+
get: lazy(() => derive(() => {
|
|
109
|
+
traceVersion.value;
|
|
110
|
+
const list = spansOf(fn);
|
|
111
|
+
if (list.length === 0)
|
|
112
|
+
return [];
|
|
113
|
+
return collectTouchedDeep(list[list.length - 1]);
|
|
114
|
+
})).get,
|
|
115
|
+
});
|
|
116
|
+
return factory;
|
|
117
|
+
}
|
|
118
|
+
/** Union of `touched` over `root` and its descendants (via `parent`
|
|
119
|
+
* back-links). */
|
|
120
|
+
function collectTouchedDeep(root) {
|
|
121
|
+
const out = new Set(root.touched);
|
|
122
|
+
// `parent` is a back-link, so walk every span and test ancestry.
|
|
123
|
+
// `descends(s, root)` is O(depth); traces are small in practice.
|
|
124
|
+
for (const arr of allFactoryLists()) {
|
|
125
|
+
for (const s of arr) {
|
|
126
|
+
if (s === root)
|
|
127
|
+
continue;
|
|
128
|
+
if (descends(s, root)) {
|
|
129
|
+
for (const sig of s.touched)
|
|
130
|
+
out.add(sig);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return Array.from(out);
|
|
135
|
+
}
|
|
136
|
+
function descends(s, ancestor) {
|
|
137
|
+
let cur = s.parent;
|
|
138
|
+
while (cur) {
|
|
139
|
+
if (cur === ancestor)
|
|
140
|
+
return true;
|
|
141
|
+
cur = cur.parent;
|
|
142
|
+
}
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
/** Parallel list of seen factories — WeakMap isn't iterable. */
|
|
146
|
+
const knownFactories = [];
|
|
147
|
+
export function rememberFactory(fn) {
|
|
148
|
+
if (!spansByFactory.has(fn))
|
|
149
|
+
knownFactories.push(fn);
|
|
150
|
+
}
|
|
151
|
+
function* allFactoryLists() {
|
|
152
|
+
for (const fn of knownFactories)
|
|
153
|
+
yield spansOf(fn);
|
|
154
|
+
}
|
|
155
|
+
/** Animator-shaped wrapper around `inner`: push/pop the span on each
|
|
156
|
+
* gen entry, observe lifecycle from inside. */
|
|
157
|
+
function makeWrapper(fn, name, args, parent, inner) {
|
|
158
|
+
let span;
|
|
159
|
+
const ensureOpen = () => {
|
|
160
|
+
if (!span) {
|
|
161
|
+
rememberFactory(fn);
|
|
162
|
+
span = openSpan(fn, name, args, parent);
|
|
163
|
+
// Record BEFORE notifying so downstream computeds see the span
|
|
164
|
+
// when they re-evaluate during the listener-driven flush.
|
|
165
|
+
recordFactorySpan(span);
|
|
166
|
+
notifySpanOpen(span);
|
|
167
|
+
}
|
|
168
|
+
return span;
|
|
169
|
+
};
|
|
170
|
+
return {
|
|
171
|
+
next(t) {
|
|
172
|
+
const s = ensureOpen();
|
|
173
|
+
// Close outside withSpan so the close event's writes aren't
|
|
174
|
+
// attributed to this span's `touched` set.
|
|
175
|
+
let r;
|
|
176
|
+
try {
|
|
177
|
+
r = withSpan(s, () => inner.next(t));
|
|
178
|
+
}
|
|
179
|
+
catch (e) {
|
|
180
|
+
if (s.status === "open")
|
|
181
|
+
closeSpan(s, "errored");
|
|
182
|
+
throw e;
|
|
183
|
+
}
|
|
184
|
+
if (r.done && s.status === "open")
|
|
185
|
+
closeSpan(s, "settled");
|
|
186
|
+
return r;
|
|
187
|
+
},
|
|
188
|
+
return(v) {
|
|
189
|
+
// Engine may `.return()` on cancel before any `.next()`; an
|
|
190
|
+
// un-resumed span never really ran, so skip it.
|
|
191
|
+
if (!span)
|
|
192
|
+
return inner.return(v);
|
|
193
|
+
let r;
|
|
194
|
+
try {
|
|
195
|
+
r = withSpan(span, () => inner.return(v));
|
|
196
|
+
}
|
|
197
|
+
catch (e) {
|
|
198
|
+
if (span.status === "open")
|
|
199
|
+
closeSpan(span, "errored");
|
|
200
|
+
throw e;
|
|
201
|
+
}
|
|
202
|
+
if (span.status === "open")
|
|
203
|
+
closeSpan(span, "cancelled");
|
|
204
|
+
return r;
|
|
205
|
+
},
|
|
206
|
+
throw(e) {
|
|
207
|
+
const s = ensureOpen();
|
|
208
|
+
let r;
|
|
209
|
+
try {
|
|
210
|
+
r = withSpan(s, () => inner.throw(e));
|
|
211
|
+
}
|
|
212
|
+
catch (err) {
|
|
213
|
+
if (s.status === "open")
|
|
214
|
+
closeSpan(s, "errored");
|
|
215
|
+
throw err;
|
|
216
|
+
}
|
|
217
|
+
if (r.done && s.status === "open")
|
|
218
|
+
closeSpan(s, "settled");
|
|
219
|
+
return r;
|
|
220
|
+
},
|
|
221
|
+
[Symbol.iterator]() {
|
|
222
|
+
return this;
|
|
223
|
+
},
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
/** Batch-scope a record of factories; each key becomes the `name`. */
|
|
227
|
+
export function scopeAll(o) {
|
|
228
|
+
const out = {};
|
|
229
|
+
for (const k of Object.keys(o)) {
|
|
230
|
+
out[k] = scope(k, o[k]);
|
|
231
|
+
}
|
|
232
|
+
return out;
|
|
233
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { Cell } from "../core/index.js";
|
|
2
|
+
export type SpanStatus = "open" | "settled" | "cancelled" | "errored";
|
|
3
|
+
/** One factory invocation, possibly nested. */
|
|
4
|
+
export interface Span {
|
|
5
|
+
readonly id: number;
|
|
6
|
+
/** Factory reference; the canonical identity. */
|
|
7
|
+
readonly fn: Function;
|
|
8
|
+
/** Display name from `scope()`'s arg or `fn.name`. Kept separate so
|
|
9
|
+
* bundler renames of named function expressions don't leak in. */
|
|
10
|
+
readonly name: string;
|
|
11
|
+
readonly args: readonly unknown[];
|
|
12
|
+
readonly parent?: Span;
|
|
13
|
+
/** Set by the recorder on open. `0` outside a `record()` session. */
|
|
14
|
+
start: number;
|
|
15
|
+
/** Set by the recorder on close. */
|
|
16
|
+
end?: number;
|
|
17
|
+
status: SpanStatus;
|
|
18
|
+
/** Signals written while this span (not its descendants) was on top
|
|
19
|
+
* of the stack. Populated by the recorder; empty otherwise. */
|
|
20
|
+
readonly touched: Set<Cell<unknown>>;
|
|
21
|
+
}
|
|
22
|
+
/** Top-of-stack span; read by `scope` (parent capture) and `record`
|
|
23
|
+
* (write attribution). */
|
|
24
|
+
export declare let currentSpan: Span | undefined;
|
|
25
|
+
/** Run `fn` with `currentSpan = s`, restoring on exit. Keeps the stack
|
|
26
|
+
* consistent through `yield`, `yield*`, and re-entrant calls. */
|
|
27
|
+
export declare function withSpan<T>(s: Span | undefined, fn: () => T): T;
|
|
28
|
+
/** Register span-lifecycle listeners. Returns disposer. */
|
|
29
|
+
export declare function addSpanListener(open: (s: Span) => void, close: (s: Span) => void): () => void;
|
|
30
|
+
/** Create a span (`start` = 0; recorder stamps it). Does NOT notify —
|
|
31
|
+
* finish bookkeeping, then call `notifySpanOpen(s)`. */
|
|
32
|
+
export declare function openSpan(fn: Function, name: string, args: readonly unknown[], parent: Span | undefined): Span;
|
|
33
|
+
/** Notify recorders that `s` opened. Call AFTER per-factory bookkeeping
|
|
34
|
+
* downstream observers may read. */
|
|
35
|
+
export declare function notifySpanOpen(s: Span): void;
|
|
36
|
+
/** End `s` with `status` (no-op if already closed); fires close listeners. */
|
|
37
|
+
export declare function closeSpan(s: Span, status: Exclude<SpanStatus, "open">): void;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// Span — one factory invocation. The assert package's single nominal
|
|
2
|
+
// data type; everything else is a function over Spans and Signals.
|
|
3
|
+
//
|
|
4
|
+
// Identity is `fn` (the factory reference); a Span is one particular
|
|
5
|
+
// call, possibly nested. `scope()`'s wrapper opens it on first `.next()`,
|
|
6
|
+
// captures parent at construction, closes on settle/cancel/error.
|
|
7
|
+
//
|
|
8
|
+
// This module never stamps `start` / `end` — the recorder does, on the
|
|
9
|
+
// open/close listener, keeping the engine assert-free. `currentSpan` is
|
|
10
|
+
// a single module slot; `withSpan` push/pop is correct around any
|
|
11
|
+
// synchronous gen body (used by `scope` and `record`).
|
|
12
|
+
/** Top-of-stack span; read by `scope` (parent capture) and `record`
|
|
13
|
+
* (write attribution). */
|
|
14
|
+
export let currentSpan;
|
|
15
|
+
/** Run `fn` with `currentSpan = s`, restoring on exit. Keeps the stack
|
|
16
|
+
* consistent through `yield`, `yield*`, and re-entrant calls. */
|
|
17
|
+
export function withSpan(s, fn) {
|
|
18
|
+
const prev = currentSpan;
|
|
19
|
+
currentSpan = s;
|
|
20
|
+
try {
|
|
21
|
+
return fn();
|
|
22
|
+
}
|
|
23
|
+
finally {
|
|
24
|
+
currentSpan = prev;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/** Lifecycle listeners; each `record()` adds a pair. Multiple recorders
|
|
28
|
+
* coexist. They stamp `s.start` / `s.end`; this module is engine-agnostic. */
|
|
29
|
+
const openListeners = new Set();
|
|
30
|
+
const closeListeners = new Set();
|
|
31
|
+
/** Register span-lifecycle listeners. Returns disposer. */
|
|
32
|
+
export function addSpanListener(open, close) {
|
|
33
|
+
openListeners.add(open);
|
|
34
|
+
closeListeners.add(close);
|
|
35
|
+
return () => {
|
|
36
|
+
openListeners.delete(open);
|
|
37
|
+
closeListeners.delete(close);
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
let nextId = 1;
|
|
41
|
+
/** Create a span (`start` = 0; recorder stamps it). Does NOT notify —
|
|
42
|
+
* finish bookkeeping, then call `notifySpanOpen(s)`. */
|
|
43
|
+
export function openSpan(fn, name, args, parent) {
|
|
44
|
+
return {
|
|
45
|
+
id: nextId++,
|
|
46
|
+
fn,
|
|
47
|
+
name,
|
|
48
|
+
args,
|
|
49
|
+
parent,
|
|
50
|
+
start: 0,
|
|
51
|
+
status: "open",
|
|
52
|
+
touched: new Set(),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/** Notify recorders that `s` opened. Call AFTER per-factory bookkeeping
|
|
56
|
+
* downstream observers may read. */
|
|
57
|
+
export function notifySpanOpen(s) {
|
|
58
|
+
for (const cb of openListeners)
|
|
59
|
+
cb(s);
|
|
60
|
+
}
|
|
61
|
+
/** End `s` with `status` (no-op if already closed); fires close listeners. */
|
|
62
|
+
export function closeSpan(s, status) {
|
|
63
|
+
if (s.status !== "open")
|
|
64
|
+
return;
|
|
65
|
+
s.status = status;
|
|
66
|
+
for (const cb of closeListeners)
|
|
67
|
+
cb(s);
|
|
68
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Span } from "./span.js";
|
|
2
|
+
export interface TraceBatch {
|
|
3
|
+
readonly start: number;
|
|
4
|
+
readonly members: readonly TraceNode[];
|
|
5
|
+
}
|
|
6
|
+
export interface TraceNode {
|
|
7
|
+
readonly span: Span;
|
|
8
|
+
readonly parent?: TraceNode;
|
|
9
|
+
readonly depth: number;
|
|
10
|
+
readonly batches: readonly TraceBatch[];
|
|
11
|
+
readonly children: readonly TraceNode[];
|
|
12
|
+
}
|
|
13
|
+
export interface TraceTree {
|
|
14
|
+
readonly roots: readonly TraceNode[];
|
|
15
|
+
readonly byId: ReadonlyMap<number, TraceNode>;
|
|
16
|
+
readonly size: number;
|
|
17
|
+
/** Pre-order DFS: parent first, then batches in start-time order,
|
|
18
|
+
* then siblings within a batch in start order. */
|
|
19
|
+
dfs(visit: (node: TraceNode, depth: number) => void): void;
|
|
20
|
+
}
|
|
21
|
+
/** Build a `TraceTree`. Spans must be in start-time order (recorder is). */
|
|
22
|
+
export declare function traceTree(spans: readonly Span[]): TraceTree;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// Structural view over `Span[]`. Pure; wrap in `derive()` to stay reactive.
|
|
2
|
+
//
|
|
3
|
+
// `parent` is a back-link on Span; this module produces forward links
|
|
4
|
+
// (children) and groups siblings into `batches` — same-`start` siblings
|
|
5
|
+
// (spawned together via `yield [a, b, c]`) share a batch.
|
|
6
|
+
/** Build a `TraceTree`. Spans must be in start-time order (recorder is). */
|
|
7
|
+
export function traceTree(spans) {
|
|
8
|
+
const byId = new Map();
|
|
9
|
+
for (const s of spans) {
|
|
10
|
+
byId.set(s.id, { span: s, depth: 0, batches: [], children: [] });
|
|
11
|
+
}
|
|
12
|
+
const childrenOf = new Map();
|
|
13
|
+
const roots = [];
|
|
14
|
+
for (const s of spans) {
|
|
15
|
+
const node = byId.get(s.id);
|
|
16
|
+
const parentId = s.parent?.id;
|
|
17
|
+
if (parentId === undefined) {
|
|
18
|
+
roots.push(node);
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
const parent = byId.get(parentId);
|
|
22
|
+
if (!parent) {
|
|
23
|
+
// Parent outside this list (trace started mid-run): treat as root.
|
|
24
|
+
roots.push(node);
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
node.parent = parent;
|
|
28
|
+
node.depth = parent.depth + 1;
|
|
29
|
+
const arr = childrenOf.get(parentId);
|
|
30
|
+
if (arr)
|
|
31
|
+
arr.push(node);
|
|
32
|
+
else
|
|
33
|
+
childrenOf.set(parentId, [node]);
|
|
34
|
+
}
|
|
35
|
+
for (const [parentId, kids] of childrenOf) {
|
|
36
|
+
const parent = byId.get(parentId);
|
|
37
|
+
parent.children = kids;
|
|
38
|
+
let i = 0;
|
|
39
|
+
while (i < kids.length) {
|
|
40
|
+
const t = kids[i].span.start;
|
|
41
|
+
const members = [];
|
|
42
|
+
while (i < kids.length && kids[i].span.start === t) {
|
|
43
|
+
members.push(kids[i]);
|
|
44
|
+
i++;
|
|
45
|
+
}
|
|
46
|
+
parent.batches.push({ start: t, members });
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
const rootsRO = roots;
|
|
50
|
+
const byIdRO = byId;
|
|
51
|
+
return {
|
|
52
|
+
roots: rootsRO,
|
|
53
|
+
byId: byIdRO,
|
|
54
|
+
size: spans.length,
|
|
55
|
+
dfs(visit) {
|
|
56
|
+
const walk = (n) => {
|
|
57
|
+
visit(n, n.depth);
|
|
58
|
+
for (const c of n.children)
|
|
59
|
+
walk(c);
|
|
60
|
+
};
|
|
61
|
+
for (const r of rootsRO)
|
|
62
|
+
walk(r);
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { Animator, Easing } from "../animation/index.js";
|
|
2
|
+
import { type Cell, type Num as NumSignal, type Val, type Vec, type Writable } from "../core/index.js";
|
|
3
|
+
import { Shape, type ShapeOpts } from "../shapes/index.js";
|
|
4
|
+
export interface CodeOpts extends ShapeOpts {
|
|
5
|
+
/** Font size in user units. Default 14. */
|
|
6
|
+
size?: number;
|
|
7
|
+
/** Monospace font stack. */
|
|
8
|
+
font?: string;
|
|
9
|
+
/** Prism language id. Default `"typescript"`. */
|
|
10
|
+
language?: string;
|
|
11
|
+
}
|
|
12
|
+
/** A single-line span placed absolutely; `position` / `opacity` /
|
|
13
|
+
* `rotation` are animatable signals. */
|
|
14
|
+
export declare class Part {
|
|
15
|
+
#private;
|
|
16
|
+
readonly el: HTMLSpanElement;
|
|
17
|
+
/** Current text. Use `setText` to update (instant). */
|
|
18
|
+
text: string;
|
|
19
|
+
/** Top-left in user units. */
|
|
20
|
+
readonly position: Writable<Vec>;
|
|
21
|
+
/** [0..1]. */
|
|
22
|
+
readonly opacity: Writable<NumSignal>;
|
|
23
|
+
/** Radians around the part's centre. */
|
|
24
|
+
readonly rotation: Writable<NumSignal>;
|
|
25
|
+
/** Optional identity tag; shared keys form a `c.group(key)`. */
|
|
26
|
+
key?: string;
|
|
27
|
+
constructor(text: string, x: number, y: number, key?: string);
|
|
28
|
+
/** Instant text update (text itself doesn't tween — animate around it). */
|
|
29
|
+
setText(t: string): void;
|
|
30
|
+
dispose(): void;
|
|
31
|
+
}
|
|
32
|
+
/** A Shape rendering monospace source code as a list of `Part`s. */
|
|
33
|
+
export declare class CodeShape extends Shape {
|
|
34
|
+
#private;
|
|
35
|
+
readonly source: Writable<Cell<string>>;
|
|
36
|
+
readonly width: Writable<Cell<number>>;
|
|
37
|
+
readonly height: Writable<Cell<number>>;
|
|
38
|
+
readonly language: string;
|
|
39
|
+
/** Host wrapper (`position: relative`) for the absolute parts. */
|
|
40
|
+
readonly wrapper: HTMLDivElement;
|
|
41
|
+
/** Flat parts list; morph re-sorts by (row, col) on completion. */
|
|
42
|
+
readonly parts: Part[];
|
|
43
|
+
/** Monospace char width and line height in CSS pixels. */
|
|
44
|
+
readonly charW: number;
|
|
45
|
+
readonly lineH: number;
|
|
46
|
+
constructor(initial: Val<string>, opts?: CodeOpts);
|
|
47
|
+
/** Paint syntax highlights: tokenise each row's joined text, route
|
|
48
|
+
* each typed token to a Range in its containing part. Re-entrant
|
|
49
|
+
* (clears prior syntax Ranges; leaves other buckets untouched). */
|
|
50
|
+
paint(): void;
|
|
51
|
+
/** Split `part` at char offsets into N+1 same-row sub-parts (0 and
|
|
52
|
+
* `text.length` implicit). Sub-parts inherit `part.key`; returned
|
|
53
|
+
* left-to-right. */
|
|
54
|
+
cut(part: Part, offsets: readonly number[]): Part[];
|
|
55
|
+
/** Merge same-row contiguous `parts` into one (inherits the leftmost's
|
|
56
|
+
* key). Single part is a no-op; empty throws. */
|
|
57
|
+
uncut(parts: readonly Part[]): Part;
|
|
58
|
+
/** All parts sharing `key`. Returns a fresh array. */
|
|
59
|
+
group(key: string): Part[];
|
|
60
|
+
/** Animate from current source to `target`. See `morph.ts`. */
|
|
61
|
+
morphTo(target: string, dur: number, ease?: Easing): Animator<void>;
|
|
62
|
+
/** @internal Morph's on-completion commit: set `source` (rebuild
|
|
63
|
+
* suppressed), re-sort parts row/col, refresh size + highlights. */
|
|
64
|
+
_finalize(src: string): void;
|
|
65
|
+
}
|
|
66
|
+
/** Factory: `code("source", { language: "typescript", size: 14 })`. */
|
|
67
|
+
export declare const code: (source: Val<string>, opts?: CodeOpts) => CodeShape;
|
|
68
|
+
/** Prism token-class colours via CSS Custom Highlights. Drop into a
|
|
69
|
+
* `Diagram.styles` block so the rules reach the shadow root. */
|
|
70
|
+
export declare const codeStyles = "\n ::highlight(keyword),\n ::highlight(rule) { color: var(--prettylights-keyword, #cf222e); }\n ::highlight(string),\n ::highlight(attr-value) { color: var(--prettylights-string, #0a3069); }\n ::highlight(comment),\n ::highlight(prolog),\n ::highlight(doctype),\n ::highlight(cdata) { color: var(--prettylights-comment, #59636e); }\n ::highlight(function),\n ::highlight(class-name),\n ::highlight(entity),\n ::highlight(selector) { color: var(--prettylights-entity, #6639ba); }\n ::highlight(tag),\n ::highlight(boolean),\n ::highlight(property),\n ::highlight(symbol) { color: var(--prettylights-entity-tag, #0550ae); }\n ::highlight(constant),\n ::highlight(attr-name),\n ::highlight(builtin),\n ::highlight(char),\n ::highlight(operator) { color: var(--prettylights-constant, #0550ae); }\n ::highlight(variable) { color: var(--prettylights-variable, #953800); }\n ::highlight(regex) { color: var(--prettylights-string-regexp, #116329); }\n";
|