@px-petals/roku.motion-web 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.
Files changed (77) hide show
  1. package/bands/bands.js +3 -1
  2. package/bundle/bundle.js +56 -0
  3. package/list/list.js +12 -1
  4. package/lottie/lottie.layers.js +64 -11
  5. package/lottie/lottie.paint.js +30 -0
  6. package/lottie/lottie.paths.js +19 -4
  7. package/lottie/lottie.props.js +18 -8
  8. package/lottie/lottie.shapes.js +2 -1
  9. package/mixer/mixer.js +31 -8
  10. package/mixer/mixer.mix.js +17 -0
  11. package/motion/motion.convert.js +32 -1
  12. package/motion/motion.flow.js +20 -2
  13. package/motion/motion.machine.js +28 -13
  14. package/motion/motion.values.js +13 -9
  15. package/motion.js +196 -20
  16. package/package.json +10 -16
  17. package/player/player.js +25 -5
  18. package/readme.md +78 -3
  19. package/script/luau/lib/base/base.js +202 -0
  20. package/script/luau/lib/base/base.table.js +189 -0
  21. package/script/luau/lib/lib.args.js +152 -0
  22. package/script/luau/lib/lib.js +23 -0
  23. package/script/luau/lib/math/math.bit32.js +144 -0
  24. package/script/luau/lib/math/math.js +288 -0
  25. package/script/luau/lib/math/math.vector.js +125 -0
  26. package/script/luau/lib/string/string.format.js +143 -0
  27. package/script/luau/lib/string/string.js +160 -0
  28. package/script/luau/lib/string/string.pattern.js +505 -0
  29. package/script/luau/load/load.js +362 -0
  30. package/script/luau/load/load.ops.js +79 -0
  31. package/script/luau/luau.convert.js +259 -0
  32. package/script/luau/luau.js +132 -0
  33. package/script/luau/luau.md +111 -0
  34. package/script/luau/vm/vm.js +167 -0
  35. package/script/luau/vm/vm.meta.js +401 -0
  36. package/script/luau/vm/vm.num.js +236 -0
  37. package/script/luau/vm/vm.run.js +842 -0
  38. package/script/luau/vm/vm.value.js +406 -0
  39. package/script/play/play.js +199 -0
  40. package/script/play/play.objects.js +295 -0
  41. package/script/play/play.scope.js +91 -0
  42. package/script/rive/api/api.statics.js +31 -0
  43. package/script/rive/api/api.type.js +188 -0
  44. package/script/rive/api/artboard/artboard.js +89 -0
  45. package/script/rive/api/assets/assets.js +72 -0
  46. package/script/rive/api/data/data.value.js +99 -0
  47. package/script/rive/api/draw/draw.contours.js +163 -0
  48. package/script/rive/api/draw/draw.measure.js +196 -0
  49. package/script/rive/api/draw/draw.paint.js +235 -0
  50. package/script/rive/api/draw/draw.path.js +153 -0
  51. package/script/rive/api/draw/draw.renderer.js +117 -0
  52. package/script/rive/api/math/math.color.js +96 -0
  53. package/script/rive/api/math/math.mat2d.js +180 -0
  54. package/script/rive/api/math/math.vector.js +121 -0
  55. package/script/rive/api/media/media.audio.js +113 -0
  56. package/script/rive/api/media/media.draw.js +73 -0
  57. package/script/rive/api/media/media.image.js +61 -0
  58. package/script/rive/api/media/media.mesh.js +50 -0
  59. package/script/rive/api/pointer/pointer.event.js +64 -0
  60. package/script/rive/api/pointer/pointer.route.js +69 -0
  61. package/script/rive/api/text/text.font.js +129 -0
  62. package/script/rive/api/text/text.js +278 -0
  63. package/script/rive/api/text/text.layout.js +259 -0
  64. package/script/rive/context/context.js +76 -0
  65. package/script/rive/context/context.memory.js +196 -0
  66. package/script/rive/context/context.viewmodel.js +302 -0
  67. package/script/rive/context/lists/lists.js +160 -0
  68. package/script/rive/context/lists/lists.values.js +281 -0
  69. package/script/rive/host/host.globals.js +44 -0
  70. package/script/rive/host/host.js +533 -0
  71. package/script/rive/host/host.node.js +45 -0
  72. package/script/rive/rive.js +23 -0
  73. package/script/rive/rive.md +180 -0
  74. package/script/script.js +11 -0
  75. package/skin/skin.deform.js +514 -0
  76. package/skin/skin.ik.js +260 -0
  77. package/skin/skin.js +297 -0
@@ -0,0 +1,406 @@
1
+ /**
2
+ * Lua values. They are plain JS values wherever one fits:
3
+ *
4
+ * | Lua | JS |
5
+ * | -------- | -------------------------------------------- |
6
+ * | nil | `undefined` |
7
+ * | boolean | `true`, `false` |
8
+ * | number | a double |
9
+ * | string | a byte string: one char per byte, 0 to 255 |
10
+ * | vector | `Vector` (immutable, three components) |
11
+ * | table | `LuaTable` |
12
+ * | function | `LuaClosure` (bytecode) or `NativeFunction` |
13
+ *
14
+ * A JS array is never a Lua value, so a native function can return one to
15
+ * mean several results.
16
+ */
17
+
18
+ // Values
19
+ // ■■■■■■
20
+
21
+ /** Where the next object id comes from; ids key tables by identity and name them in `tostring` */
22
+ let lastId = 0
23
+
24
+ /** @returns {number} */
25
+ export function newId() {
26
+ return ++lastId
27
+ }
28
+
29
+ const f32 = new Float32Array(1)
30
+
31
+ /**
32
+ * A number rounded to single precision, which is what a vector component holds.
33
+ * (`Math.fround` is newer than the oldest browsers this runs in.)
34
+ *
35
+ * @param {number} x
36
+ * @returns {number}
37
+ */
38
+ export function fround(x) {
39
+ f32[0] = x
40
+ return f32[0]
41
+ }
42
+
43
+ /** A three-component vector value, single precision per component; it compares and keys by components */
44
+ export class Vector {
45
+ /**
46
+ * @param {number} x
47
+ * @param {number} y
48
+ * @param {number} z
49
+ */
50
+ constructor(x, y, z) {
51
+ this.x = fround(x)
52
+ this.y = fround(y)
53
+ this.z = fround(z)
54
+ }
55
+ }
56
+
57
+ /** The results of a function that returns nothing. Never mutate it. */
58
+ export const EMPTY = /** @type {any[]} */ ([])
59
+
60
+ /**
61
+ * A host function. It gets the VM and its arguments as an array and returns
62
+ * an array of results, or any other value for exactly one result.
63
+ *
64
+ * @typedef {(vm: any, args: any[]) => any} NativeCall
65
+ */
66
+
67
+ export class NativeFunction {
68
+ /**
69
+ * @param {string} name
70
+ * @param {NativeCall} fn
71
+ */
72
+ constructor(name, fn) {
73
+ this.name = name
74
+ this.fn = fn
75
+ this.id = 0
76
+ }
77
+ }
78
+
79
+ /** A variable a closure captured: on the VM's stack while its function runs, in `v` once that returns */
80
+ export class Upvalue {
81
+ /**
82
+ * @param {any[]} stack
83
+ * @param {number} index
84
+ */
85
+ constructor(stack, index) {
86
+ /** @type {any[] | null} */
87
+ this.s = stack
88
+ this.i = index
89
+ this.v = undefined
90
+ }
91
+ }
92
+
93
+ /** A function made from a proto and the upvalues it captured */
94
+ export class LuaClosure {
95
+ /**
96
+ * @param {import('../load/load.js').Proto} proto
97
+ * @param {Upvalue[]} upvals
98
+ * @param {LuaTable} env the globals
99
+ */
100
+ constructor(proto, upvals, env) {
101
+ this.proto = proto
102
+ this.upvals = upvals
103
+ this.env = env
104
+ this.id = 0
105
+ }
106
+ }
107
+
108
+ /** A thrown Lua error: the error value, and how many Lua frames up its position is taken from (0: none) */
109
+ export class LuaThrow {
110
+ /**
111
+ * @param {any} value
112
+ * @param {number} [level]
113
+ */
114
+ constructor(value, level) {
115
+ this.value = value
116
+ this.level = level === undefined ? 0 : level
117
+ /** The source line the error was raised at, 0 when unknown */
118
+ this.line = 0
119
+ /** The chunk that line is in */
120
+ this.chunk = ''
121
+ }
122
+ }
123
+
124
+ /**
125
+ * An error to throw from a native function or the VM: a string error gets the
126
+ * position of the Lua code that was running, `level` frames up.
127
+ *
128
+ * @param {any} value
129
+ * @param {number} [level]
130
+ * @returns {LuaThrow}
131
+ */
132
+ export function fail(value, level) {
133
+ return new LuaThrow(value, level === undefined ? 1 : level)
134
+ }
135
+
136
+ /**
137
+ * @param {any} v
138
+ * @returns {string} Lua's `type` of v
139
+ */
140
+ export function typeName(v) {
141
+ switch (typeof v) {
142
+ case 'undefined': return 'nil'
143
+ case 'boolean': return 'boolean'
144
+ case 'number': return 'number'
145
+ case 'string': return 'string'
146
+ default: break
147
+ }
148
+ if (v instanceof LuaTable) return 'table'
149
+ if (v instanceof Vector) return 'vector'
150
+ return 'function'
151
+ }
152
+
153
+ // Tables
154
+ // ■■■■■■
155
+
156
+ /**
157
+ * A Lua table. Keys 1..n are the `arr` array (kept free of trailing nils, so
158
+ * `arr.length` is a border); every other key is in one of three dictionaries
159
+ * (strings, numbers, everything else) that map a key to a slot in the `hk`
160
+ * and `hv` arrays. Slots keep their insertion order, which is the traversal
161
+ * order, and a removed key keeps its slot with a nil value until the next
162
+ * rebuild, so assigning nil during a traversal is safe.
163
+ */
164
+ export class LuaTable {
165
+ constructor() {
166
+ /** @type {any[]} */
167
+ this.arr = []
168
+ /** @type {Record<string, number> | null} */
169
+ this.hs = null
170
+ /** @type {Record<string, number> | null} */
171
+ this.hn = null
172
+ /** @type {Record<string, number> | null} */
173
+ this.ho = null
174
+ /** @type {any[]} */
175
+ this.hk = []
176
+ /** @type {any[]} */
177
+ this.hv = []
178
+ this.dead = 0
179
+ /** the key and value `advance` found */
180
+ this.ck = undefined
181
+ this.cv = undefined
182
+ /** @type {LuaTable | undefined} */
183
+ this.meta = undefined
184
+ this.frozen = false
185
+ this.id = 0
186
+ }
187
+
188
+ /**
189
+ * @param {string} key
190
+ * @returns {any}
191
+ */
192
+ getStr(key) {
193
+ const hs = this.hs
194
+ if (hs === null) return undefined
195
+ const slot = hs[key]
196
+ return slot === undefined ? undefined : this.hv[slot]
197
+ }
198
+
199
+ /**
200
+ * @param {any} key
201
+ * @returns {any}
202
+ */
203
+ get(key) {
204
+ if (typeof key === 'string') return this.getStr(key)
205
+ if (typeof key === 'number') {
206
+ const arr = this.arr
207
+ if (key >= 1 && key <= arr.length && Math.floor(key) === key) return arr[key - 1]
208
+ const hn = this.hn
209
+ if (hn === null) return undefined
210
+ const slot = hn[key]
211
+ return slot === undefined ? undefined : this.hv[slot]
212
+ }
213
+ const slot = this.slotOf(key)
214
+ return slot === undefined ? undefined : this.hv[slot]
215
+ }
216
+
217
+ /**
218
+ * The slot a key holds in the hash part, or undefined
219
+ *
220
+ * @param {any} key
221
+ * @returns {number | undefined}
222
+ */
223
+ slotOf(key) {
224
+ if (typeof key === 'string') return this.hs === null ? undefined : this.hs[key]
225
+ if (typeof key === 'number') return this.hn === null ? undefined : this.hn[key]
226
+ return this.ho === null ? undefined : this.ho[objectKey(key)]
227
+ }
228
+
229
+ /**
230
+ * Sets a key without looking at the metatable. The key must not be nil or NaN.
231
+ *
232
+ * @param {any} key
233
+ * @param {any} value
234
+ */
235
+ set(key, value) {
236
+ if (typeof key === 'number') {
237
+ const arr = this.arr
238
+ const n = arr.length
239
+ if (key >= 1 && key <= n + 1 && Math.floor(key) === key) {
240
+ if (key <= n) {
241
+ arr[key - 1] = value
242
+ if (value === undefined && key === n) {
243
+ let m = n - 1
244
+ while (m > 0 && arr[m - 1] === undefined) m--
245
+ arr.length = m
246
+ }
247
+ } else if (value !== undefined) {
248
+ arr.push(value)
249
+ if (this.hn !== null) this.migrate()
250
+ }
251
+ return
252
+ }
253
+ }
254
+ let slot = this.slotOf(key)
255
+ if (slot !== undefined) {
256
+ const old = this.hv[slot]
257
+ this.hv[slot] = value
258
+ if (value === undefined && old !== undefined) this.dead++
259
+ else if (old === undefined && value !== undefined) this.dead--
260
+ return
261
+ }
262
+ if (value === undefined) return
263
+ if (this.dead > 16 && this.dead * 2 > this.hk.length) this.rebuild()
264
+ slot = this.hk.length
265
+ this.hk.push(key)
266
+ this.hv.push(value)
267
+ if (typeof key === 'string') {
268
+ const hs = this.hs === null ? (this.hs = Object.create(null)) : this.hs
269
+ hs[key] = slot
270
+ } else if (typeof key === 'number') {
271
+ const hn = this.hn === null ? (this.hn = Object.create(null)) : this.hn
272
+ hn[key] = slot
273
+ } else {
274
+ const ho = this.ho === null ? (this.ho = Object.create(null)) : this.ho
275
+ ho[objectKey(key)] = slot
276
+ }
277
+ }
278
+
279
+ /** After the array part grew, moves the keys that now continue it out of the hash part */
280
+ migrate() {
281
+ const hn = /** @type {Record<string, number>} */ (this.hn)
282
+ for (;;) {
283
+ const slot = hn[this.arr.length + 1]
284
+ if (slot === undefined || this.hv[slot] === undefined) return
285
+ this.arr.push(this.hv[slot])
286
+ this.hv[slot] = undefined
287
+ this.dead++
288
+ }
289
+ }
290
+
291
+ /** Drops the slots whose values are nil */
292
+ rebuild() {
293
+ const keys = this.hk
294
+ const values = this.hv
295
+ this.hk = []
296
+ this.hv = []
297
+ this.hs = null
298
+ this.hn = null
299
+ this.ho = null
300
+ this.dead = 0
301
+ for (let i = 0; i < keys.length; i++) if (values[i] !== undefined) this.set(keys[i], values[i])
302
+ }
303
+
304
+ /** @returns {number} a border: the length `#` gives */
305
+ length() {
306
+ return this.arr.length
307
+ }
308
+
309
+ /**
310
+ * One step of a traversal. A position is an array index (0 and up), or -1
311
+ * and down for hash slot 0 and up; it survives assigning nil to entries.
312
+ * Finds the entry at or after `pos`, leaves its key and value in `ck` and
313
+ * `cv`, and returns the position after it; null when there are no more.
314
+ *
315
+ * @param {number} pos where to start: 0 for the beginning
316
+ * @returns {number | null}
317
+ */
318
+ advance(pos) {
319
+ const arr = this.arr
320
+ let i = pos
321
+ if (i >= 0) {
322
+ for (; i < arr.length; i++) {
323
+ if (arr[i] !== undefined) {
324
+ this.ck = i + 1
325
+ this.cv = arr[i]
326
+ return i + 1
327
+ }
328
+ }
329
+ i = -1
330
+ }
331
+ const hv = this.hv
332
+ for (let s = -i - 1; s < hv.length; s++) {
333
+ if (hv[s] !== undefined) {
334
+ this.ck = this.hk[s]
335
+ this.cv = hv[s]
336
+ return -s - 2
337
+ }
338
+ }
339
+ return null
340
+ }
341
+
342
+ /**
343
+ * Stores values at keys `start`, `start + 1`, ... in the array part, nils
344
+ * included, as a table constructor does (so `{ nil, nil, 3 }` has length 3).
345
+ *
346
+ * @param {number} start the first key, 1 or more
347
+ * @param {any[]} source
348
+ * @param {number} from the first index in source
349
+ * @param {number} count
350
+ */
351
+ setRange(start, source, from, count) {
352
+ const arr = this.arr
353
+ while (arr.length < start - 1) arr.push(undefined)
354
+ for (let i = 0; i < count; i++) {
355
+ const v = source[from + i]
356
+ if (start - 1 + i < arr.length) arr[start - 1 + i] = v
357
+ else arr.push(v)
358
+ if (this.hn !== null) {
359
+ const slot = this.hn[start + i]
360
+ if (slot !== undefined && this.hv[slot] !== undefined) {
361
+ this.hv[slot] = undefined
362
+ this.dead++
363
+ }
364
+ }
365
+ }
366
+ let m = arr.length
367
+ while (m > 0 && arr[m - 1] === undefined) m--
368
+ arr.length = m
369
+ if (this.hn !== null) this.migrate()
370
+ }
371
+
372
+ /**
373
+ * The position after a key, for `next`; undefined when the key is not in the table.
374
+ *
375
+ * @param {any} key
376
+ * @returns {number | undefined}
377
+ */
378
+ after(key) {
379
+ if (key === undefined) return 0
380
+ if (typeof key === 'number' && key >= 1 && key <= this.arr.length && Math.floor(key) === key) return key
381
+ const slot = this.slotOf(key)
382
+ return slot === undefined ? undefined : -slot - 2
383
+ }
384
+
385
+ /** @returns {LuaTable} a copy without the metatable's identity: same entries, same metatable */
386
+ clone() {
387
+ const t = new LuaTable()
388
+ t.arr = this.arr.slice()
389
+ for (let s = 0; s < this.hk.length; s++) if (this.hv[s] !== undefined) t.set(this.hk[s], this.hv[s])
390
+ t.meta = this.meta
391
+ return t
392
+ }
393
+ }
394
+
395
+ /**
396
+ * The dictionary key for a key that is neither a string nor a number.
397
+ *
398
+ * @param {any} key
399
+ * @returns {string}
400
+ */
401
+ function objectKey(key) {
402
+ if (typeof key === 'boolean') return key ? 'b1' : 'b0'
403
+ if (key instanceof Vector) return 'v' + key.x + ',' + key.y + ',' + key.z
404
+ if (key.id === 0) key.id = newId()
405
+ return 'o' + key.id
406
+ }
@@ -0,0 +1,199 @@
1
+ /**
2
+ * The scripts a bundle ships, run while it plays: scripted converters on a
3
+ * slot's source, transition conditions on a machine's guards, and listener
4
+ * actions on its interactions.
5
+ *
6
+ * This file imports the Luau VM and Rive host, so the player's main bundle
7
+ * does not: the page gives `script/script.js` (which exports `createScripts`)
8
+ * to `loadScripts` (bundle/bundle.js), and only for a bundle that lists scripts
9
+ * (`manifest.scripts`). A bundle without them downloads and parses none of it,
10
+ * and the legacy build's main file holds none of it either (the host is a file
11
+ * of its own there).
12
+ *
13
+ * Scripted nodes, drawables and path effects (`objects`) run every frame beside
14
+ * these, from `play.objects.js`.
15
+ *
16
+ * The scripts see the player's live values as their view model: a property is
17
+ * a key of the values by its dotted path, and a write changes the value (a
18
+ * trigger sets the event). A converter script that reads none of it is run
19
+ * once per distinct input; one that reads it is run on each use.
20
+ */
21
+
22
+ import { createScriptHost } from '../rive/rive.js'
23
+ import { createObjects } from './play.objects.js'
24
+
25
+ /** Tells the runtimes of several Motions apart, which may share a manifest's objects */
26
+ let nextRuntime = 1
27
+
28
+ /** The interactions a pointer event is made of, by the name a script sees */
29
+ const POINTER_TYPES = /** @type {Record<string, string>} */ ({
30
+ Click: 'click',
31
+ PointerDown: 'down',
32
+ PointerUp: 'up',
33
+ PointerEnter: 'enter',
34
+ PointerExit: 'exit',
35
+ PointerMove: 'move',
36
+ })
37
+
38
+ /**
39
+ * @typedef {object} Scripts
40
+ * @property {(descriptor: any, value: any, isReverse?: boolean, kind?: string, values?: Record<string, any>) => any} convert a converter script's result for a value; the value itself when the script gives none
41
+ * @property {(guard: any, values: Record<string, any>) => boolean} evaluate whether a transition condition script lets its transition pass
42
+ * @property {(action: any, type: string, values: Record<string, any>, events: Record<string, boolean>) => boolean} perform runs a listener action script; whether it changed a value or fired a trigger
43
+ * @property {(values: Record<string, any>) => void} changed the values changed: scripts listening to a property hear of it
44
+ * @property {(entries: import('./play.objects.js').ObjectEntry[], env: import('./play.objects.js').ObjectsEnv) => import('./play.objects.js').Objects} objects starts the bundle's scripted nodes, drawables and path effects, which run each frame (`play.objects.js`)
45
+ * @property {number} count the scripts started so far
46
+ */
47
+
48
+ /**
49
+ * @param {any} manifest
50
+ * @param {Record<string, Uint8Array>} bytes each script's bytecode, by the entry a descriptor names it by
51
+ * @param {(message: string) => void} report
52
+ * @param {Function} [make] makes a script host; default the Rive host (a test passes one that counts)
53
+ * @returns {Scripts}
54
+ */
55
+ export function createScripts(manifest, bytes, report, make = createScriptHost) {
56
+ const runtime = nextRuntime++
57
+ /** @type {Array<{ host: any, pure: boolean, hasIn: boolean, lastIn: any, lastOut: any }>} */
58
+ const entries = []
59
+ /** @type {Array<{ path: string, fn: (value: any) => void, last: any }>} */
60
+ const subscriptions = []
61
+ /** @type {Record<string, any>} */
62
+ let values = {}
63
+ /** @type {Record<string, boolean>} */
64
+ let events = {}
65
+ let dirty = false
66
+ /** values a script read, to tell whether a result may be reused */
67
+ let reads = 0
68
+ /** @type {Record<string, string>} */
69
+ const kinds = {}
70
+ const properties = (manifest.data && manifest.data.properties) || []
71
+ for (let n = 0; n < properties.length; n++) kinds[properties[n].name] = properties[n].type
72
+ /** the pointer event of the latest listener action, reused */
73
+ const pointer = { type: 'click' }
74
+
75
+ /** The player's values, as the view model a script reaches */
76
+ const adapter = {
77
+ get: (/** @type {string} */ path) => {
78
+ reads++
79
+ return values[path]
80
+ },
81
+ kind: (/** @type {string} */ path) => {
82
+ const kind = kinds[path]
83
+ if (kind) return kind
84
+ const value = values[path]
85
+ return typeof value === 'number' ? 'number' : typeof value === 'string' ? 'string' : typeof value === 'boolean' ? 'boolean' : undefined
86
+ },
87
+ set: (/** @type {string} */ path, /** @type {any} */ value) => {
88
+ if (values[path] === value) return
89
+ values[path] = value
90
+ dirty = true
91
+ },
92
+ fire: (/** @type {string} */ path) => {
93
+ events[path] = true
94
+ dirty = true
95
+ },
96
+ subscribe: (/** @type {string} */ path, /** @type {(value: any) => void} */ fn) => {
97
+ const subscription = { path, fn, last: values[path] }
98
+ subscriptions.push(subscription)
99
+ return () => {
100
+ const at = subscriptions.indexOf(subscription)
101
+ if (at >= 0) subscriptions.splice(at, 1)
102
+ }
103
+ },
104
+ }
105
+
106
+ /**
107
+ * The host a descriptor, guard or action runs, made when it is first used.
108
+ *
109
+ * @param {any} ref carries `script`, and `inputs` when the object sets any
110
+ * @returns {typeof entries[number] | null}
111
+ */
112
+ function entryOf(ref) {
113
+ if (ref._runtime === runtime) return ref._host < 0 ? null : entries[ref._host]
114
+ ref._runtime = runtime
115
+ const data = bytes[ref.script]
116
+ if (!data) {
117
+ report("script '" + ref.script + "' is not in the bundle")
118
+ ref._host = -1
119
+ return null
120
+ }
121
+ const host = make({
122
+ bytes: data,
123
+ inputs: ref.inputs,
124
+ viewModel: adapter,
125
+ log: () => {},
126
+ onError: (/** @type {Error} */ error) => report("script '" + ref.script + "': " + error.message),
127
+ })
128
+ host.init()
129
+ entries.push({ host, pure: true, hasIn: false, lastIn: undefined, lastOut: undefined })
130
+ ref._host = entries.length - 1
131
+ return entries[ref._host]
132
+ }
133
+
134
+ /**
135
+ * @param {Record<string, any>} v
136
+ * @param {Record<string, boolean>} [e]
137
+ */
138
+ function bind(v, e) {
139
+ values = v
140
+ if (e) events = e
141
+ }
142
+
143
+ return {
144
+ convert(descriptor, value, isReverse, kind, current) {
145
+ if (current) bind(current)
146
+ const entry = entryOf(descriptor)
147
+ if (!entry) return value
148
+ // a script that read nothing gives the same result for the same input
149
+ if (!isReverse && entry.pure && entry.hasIn && entry.lastIn === value) return entry.lastOut
150
+
151
+ const before = reads
152
+ const out = isReverse ? entry.host.reverseConvert(value, kind) : entry.host.convert(value, kind)
153
+ if (reads !== before) entry.pure = false
154
+ if (!out) return value
155
+ if (!isReverse) {
156
+ entry.hasIn = true
157
+ entry.lastIn = value
158
+ entry.lastOut = out.value
159
+ }
160
+ return out.value
161
+ },
162
+
163
+ evaluate(guard, current) {
164
+ bind(current)
165
+ const entry = entryOf(guard)
166
+ return !!entry && entry.host.evaluate()
167
+ },
168
+
169
+ perform(action, type, current, pending) {
170
+ bind(current, pending)
171
+ const entry = entryOf(action)
172
+ if (!entry) return false
173
+ dirty = false
174
+ pointer.type = POINTER_TYPES[type] || 'click'
175
+ entry.host.perform(pointer)
176
+ return dirty
177
+ },
178
+
179
+ changed(current) {
180
+ if (!subscriptions.length) return
181
+ bind(current)
182
+ const heard = subscriptions.slice()
183
+ for (let n = 0; n < heard.length; n++) {
184
+ const value = values[heard[n].path]
185
+ if (value === heard[n].last) continue
186
+ heard[n].last = value
187
+ heard[n].fn(value)
188
+ }
189
+ },
190
+
191
+ objects(list, env) {
192
+ return createObjects(list, bytes, env, report, make)
193
+ },
194
+
195
+ get count() {
196
+ return entries.length
197
+ },
198
+ }
199
+ }